diff --git a/wagtail/contrib/table_block/blocks.py b/wagtail/contrib/table_block/blocks.py index 44fe079e94..c972101733 100644 --- a/wagtail/contrib/table_block/blocks.py +++ b/wagtail/contrib/table_block/blocks.py @@ -99,6 +99,13 @@ class TableBlock(FieldBlock): 'html_renderer': self.is_html_renderer(), 'data': value['data'][1:] if table_header else value.get('data', []) }) + + if value.get('cell'): + new_context['classnames'] = {} + for meta in value['cell']: + if 'className' in meta: + new_context['classnames'][(meta['row'], meta['col'])] = meta['className'] + return render_to_string(template, new_context) else: return self.render_basic(value, context=context) diff --git a/wagtail/contrib/table_block/static/table_block/js/table.js b/wagtail/contrib/table_block/static/table_block/js/table.js index b847838bbd..d53568bc91 100644 --- a/wagtail/contrib/table_block/static/table_block/js/table.js +++ b/wagtail/contrib/table_block/static/table_block/js/table.js @@ -10,10 +10,14 @@ function initTable(id, tableOptions) { var hot; var defaultOptions; var finalOptions = {}; + var getCellsClassnames; var persist; var cellEvent; + var metaEvent; + var initEvent; var structureEvent; var dataForForm = null; + var isInitialized = false; var getWidth = function() { return $('.widget-table_input').closest('.sequence-member-inner').width(); }; @@ -64,9 +68,26 @@ function initTable(id, tableOptions) { }); } + getCellsClassnames = function() { + var meta = hot.getCellsMeta(); + var cellsClassnames = [] + for (var i = 0; i < meta.length; i++) { + if (meta[i].hasOwnProperty('className')) { + cellsClassnames.push({ + row: meta[i].row, + col: meta[i].col, + className: meta[i].className + }); + } + } + console.log(cellsClassnames); + return cellsClassnames; + }; + persist = function() { hiddenStreamInput.val(JSON.stringify({ data: hot.getData(), + cell: getCellsClassnames(), first_row_is_table_header: tableHeaderCheckbox.prop('checked'), first_col_is_header: colHeaderCheckbox.prop('checked') })); @@ -80,6 +101,16 @@ function initTable(id, tableOptions) { persist(); }; + metaEvent = function(row, column, key, value) { + if (isInitialized && key === 'className') { + persist(); + } + }; + + initEvent = function() { + isInitialized = true; + }; + structureEvent = function(index, amount) { resizeHeight(getHeight()); persist(); @@ -99,12 +130,19 @@ function initTable(id, tableOptions) { afterCreateRow: structureEvent, afterRemoveCol: structureEvent, afterRemoveRow: structureEvent, + afterSetCellMeta: metaEvent, + afterInit: initEvent, // contextMenu set via init, from server defaults }; - if (dataForForm !== null && dataForForm.hasOwnProperty('data')) { + if (dataForForm !== null) { // Overrides default value from tableOptions (if given) with value from database - defaultOptions.data = dataForForm.data; + if (dataForForm.hasOwnProperty('data')) { + defaultOptions.data = dataForForm.data; + } + if (dataForForm.hasOwnProperty('cell')) { + defaultOptions.cell = dataForForm.cell; + } } Object.keys(defaultOptions).forEach(function (key) { diff --git a/wagtail/contrib/table_block/templates/table_block/blocks/table.html b/wagtail/contrib/table_block/templates/table_block/blocks/table.html index e7d6f7cb01..766b3e7dfe 100644 --- a/wagtail/contrib/table_block/templates/table_block/blocks/table.html +++ b/wagtail/contrib/table_block/templates/table_block/blocks/table.html @@ -1,9 +1,12 @@ +{% load table_block_tags %} + {% if table_header %} {% for column in table_header %} - + {% endwith %} {% endfor %} {% endif %} {% for row in data %} + {% with forloop.counter0 as row_index %} {% for column in row %} + {% with forloop.counter0 as col_index %} {% if first_col_is_header and forloop.first %} - {% else %} - {% endif %} + {% endwith %} {% endfor %} + {% endwith %} {% endfor %}
+ {% with forloop.counter0 as col_index %} + {% if column.strip %} {% if html_renderer %} {{ column.strip|safe|linebreaksbr }} @@ -12,16 +15,19 @@ {% endif %} {% endif %}
+ {% if column.strip %} {% if html_renderer %} {{ column.strip|safe|linebreaksbr }} @@ -31,7 +37,7 @@ {% endif %} + {% if column.strip %} {% if html_renderer %} {{ column.strip|safe|linebreaksbr }} @@ -41,8 +47,10 @@ {% endif %}
diff --git a/wagtail/contrib/table_block/templatetags/__init__.py b/wagtail/contrib/table_block/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wagtail/contrib/table_block/templatetags/table_block_tags.py b/wagtail/contrib/table_block/templatetags/table_block_tags.py new file mode 100644 index 0000000000..5f84fad17a --- /dev/null +++ b/wagtail/contrib/table_block/templatetags/table_block_tags.py @@ -0,0 +1,17 @@ +from django import template +from django.utils.safestring import mark_safe + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def cell_classname(context, row_index, col_index, table_header=None): + classnames = context.get('classnames') + if classnames: + if table_header is not None: + row_index += 1 + index = (row_index, col_index) + cell_class = classnames.get(index) + if cell_class: + return mark_safe('class="{}"'.format(cell_class)) + return '' diff --git a/wagtail/contrib/table_block/tests.py b/wagtail/contrib/table_block/tests.py index ac064251ec..fab7674f37 100644 --- a/wagtail/contrib/table_block/tests.py +++ b/wagtail/contrib/table_block/tests.py @@ -11,6 +11,19 @@ from wagtail.tests.testapp.models import TableBlockStreamPage from wagtail.tests.utils import WagtailTestUtils +def get_cell_classname(cells_meta, row_index, col_index): + """ + Helper function used in building a test html + table. Provides a cell's class attribute if + one is specified in the meta. + """ + if cells_meta: + for meta in cells_meta: + if meta.get('row') == row_index and meta.get('col') == col_index: + return ' class="%s"' % meta.get('className') + return '' + + def tiny_escape(val): """ Helper function used in building a test html @@ -26,22 +39,24 @@ def get_test_html_from_value(value): that's what we expect from the TableBlock. """ data = list(value['data']) # Make a copy + meta = value.get('cell') table = '' if value['first_row_is_table_header']: row_header = data.pop(0) table += '' - for th in row_header: - table += '' % tiny_escape(th) + for col_idx, th in enumerate(row_header): + table += '%s' % (get_cell_classname(meta, 0, col_idx), tiny_escape(th)) table += '' table += '' - for row in data: + row_idx_start = 1 if value['first_row_is_table_header'] else 0 + for row_idx, row in enumerate(data, row_idx_start): table += '' first = True - for col in row: + for col_idx, col in enumerate(row): if value['first_col_is_header'] and first: - table += '' % tiny_escape(col) + table += '%s' % (get_cell_classname(meta, row_idx, col_idx), tiny_escape(col)) else: - table += '' % tiny_escape(col) + table += '%s' % (get_cell_classname(meta, row_idx, col_idx), tiny_escape(col)) first = False table += '' table += '
%s
%s%s
' @@ -89,6 +104,22 @@ class TestTableBlock(TestTableBlockRenderingBase): self.assertHTMLEqual(result, expected) self.assertIn('Test 2', result) + def test_table_block_aligment_render(self): + """ + Test a generic render with some cells aligned. + """ + value = {'first_row_is_table_header': True, 'first_col_is_header': False, + 'cell': [{'row': 0, 'col': 1, 'className': 'htLeft'}, + {'row': 1, 'col': 1, 'className': 'htRight'}], + 'data': [['Test 1', 'Test 2', 'Test 3'], [None, None, None], + [None, None, None]]} + block = TableBlock() + result = block.render(value) + expected = get_test_html_from_value(value) + + self.assertHTMLEqual(result, expected) + self.assertIn('Test 2', result) + def test_render_empty_table(self): """ An empty table should render okay.