Skip to content

Commit

Permalink
Add table macro (#71)
Browse files Browse the repository at this point in the history
* feat: Add a macro for rendering a bootstrap table

* fix: Correct bad assertion

* fix: Correct linting issues
  • Loading branch information
michaelbukachi authored Jun 7, 2020
1 parent 34e7273 commit b61d590
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
37 changes: 37 additions & 0 deletions docs/macros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,40 @@ If you want to use HTML in your message body, just warpper your message string w
def test():
flash(Markup('a info message with a link: <a href="/">Click me!</a>'), 'info')
return your_template
render_table()
--------------
Render a Bootstrap table

Example
~~~~~~~

.. code-block:: jinja
{% from 'bootstrap/table.html' import render_table %}
{{ render_table(titles, data) }}
API
~~~~

.. py:function:: render_table(titles,\
data,\
primary_key='id',\
caption=None,\
table_classes=None,\
header_classes=None,\
is_responsive=False,\
responsive_class='table-responsive')
Render a bootstrap tables

:param titles: An iterable of tuples of the format (prop, label) e.g ``[('id', '#')]``
:param data: An iterable of data objects to render. Can be dicts or class objects.
:param primary_key: Primary key identifier for a single row
:param caption: A caption to attach to the table
:param table_classes: A string of classes to apply to the table e.g ``'table-small table-dark'``
:param header_classes: A string of classes to apply to the table header e.g ``'thead-dark'``
:param is_responsive: Whether to enable/disable table responsiveness
:param responsive_class: The responsive class to apply to the table. Default is ``'table-responsive'``
33 changes: 33 additions & 0 deletions flask_bootstrap/templates/bootstrap/table.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% macro render_table(titles, data, primary_key='id', caption=None, table_classes=None, header_classes=None, is_responsive=False, responsive_class='table-responsive') %}
{% if is_responsive %}
<div class="{{ responsive_class }}">
{% endif %}
<table class="table{% if table_classes %}{{ ' ' + table_classes }}{% endif %}">
{% if caption %}
<caption>{{ caption }}</caption>
{% endif %}
<thead {% if header_classes %}class="{{ header_classes }}"{% endif %}>
<tr>
{% for title in titles %}
<th scope="col">{{ title[1] }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
{% for title in titles %}
{% if title[0] == primary_key %}
<th scope="row">{{ row[title[0]] }}</th>
{% else %}
<td>{{ row[title[0]] }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% if is_responsive %}
</div>
{% endif %}
{% endmacro %}
94 changes: 93 additions & 1 deletion test_bootstrap_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,4 +494,96 @@ def error():
response = self.client.post('/error', follow_redirects=True)
data = response.get_data(as_text=True)
self.assertIn('This field is required', data)
self.assertIn('Not a valid choice', data)

def test_render_simple_table(self):
db = SQLAlchemy(self.app)

class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text)

@self.app.route('/table')
def test():
db.drop_all()
db.create_all()
for i in range(10):
m = Message(text='Test message {}'.format(i+1))
db.session.add(m)
db.session.commit()
page = request.args.get('page', 1, type=int)
pagination = Message.query.paginate(page, per_page=10)
messages = pagination.items
titles = [('id', '#'), ('text', 'Message')]
return render_template_string('''
{% from 'bootstrap/table.html' import render_table %}
{{ render_table(titles, messages) }}
''', titles=titles, messages=messages)

response = self.client.get('/table')
data = response.get_data(as_text=True)
self.assertIn('<table class="table">', data)
self.assertIn('<th scope="col">#</th>', data)
self.assertIn('<th scope="col">Message</th>', data)
self.assertIn('<th scope="col">Message</th>', data)
self.assertIn('<th scope="row">1</th>', data)
self.assertIn('<td>Test message 1</td>', data)

def test_render_customized_table(self):
db = SQLAlchemy(self.app)

class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text)

@self.app.route('/table')
def test():
db.drop_all()
db.create_all()
for i in range(10):
m = Message(text='Test message {}'.format(i+1))
db.session.add(m)
db.session.commit()
page = request.args.get('page', 1, type=int)
pagination = Message.query.paginate(page, per_page=10)
messages = pagination.items
titles = [('id', '#'), ('text', 'Message')]
return render_template_string('''
{% from 'bootstrap/table.html' import render_table %}
{{ render_table(titles, messages, table_classes='table-striped',
header_classes='thead-dark', caption='Messages') }}
''', titles=titles, messages=messages)

response = self.client.get('/table')
data = response.get_data(as_text=True)
self.assertIn('<table class="table table-striped">', data)
self.assertIn('<thead class="thead-dark">', data)
self.assertIn('<caption>Messages</caption>', data)

def test_render_responsive_table(self):
db = SQLAlchemy(self.app)

class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text)

@self.app.route('/table')
def test():
db.drop_all()
db.create_all()
for i in range(10):
m = Message(text='Test message {}'.format(i+1))
db.session.add(m)
db.session.commit()
page = request.args.get('page', 1, type=int)
pagination = Message.query.paginate(page, per_page=10)
messages = pagination.items
titles = [('id', '#'), ('text', 'Message')]
return render_template_string('''
{% from 'bootstrap/table.html' import render_table %}
{{ render_table(titles, messages, is_responsive=True,
responsive_class='table-responsive-sm') }}
''', titles=titles, messages=messages)

response = self.client.get('/table')
data = response.get_data(as_text=True)
self.assertIn('<div class="table-responsive-sm">', data)

0 comments on commit b61d590

Please sign in to comment.