feat(table): support Hinode's wrap argument on data tables#277
Merged
Conversation
Wrap the last column onto a row of its own below the main breakpoint when the table declares data-table-wrap. The transform runs in simple-datatables' tableRender hook, so it rewrites the virtual DOM and never the row model: sorting, searching and paging keep operating on the unmodified data, and the pager keeps counting records rather than rendered rows. Crossing the breakpoint redraws through update(true), which preserves the active sort, page and search term.
The wrap transform removed the last `<th>`, leaving the rendered header one cell shorter than the row model. simple-datatables measures column widths whenever `fixedColumns` is truthy - it defaults to true - by walking the rendered headers in lockstep with `data.headings`, so the final iteration dereferenced an undefined element and threw: Cannot read properties of undefined (reading 'offsetWidth') This hit every wrapped data table: on load below the breakpoint, on crossing the breakpoint, and on any resize below it via the library's own debounced `_onResize`. Hide the heading with Bootstrap's `d-none` instead of deleting it, so the header keeps all of its cells and the DOM stays in step with the row model. This matches Hinode's plain-table wrap, which also retains the heading and hides it. The transform only runs while the media query matches, so an unconditional `d-none` is correct. Also carry the source row's attributes onto the synthesized colspan row, so `data-index` survives and the library's row-selection handler keeps reporting the right record. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hinode assembles module JS with `resources.Concat`, not a bundler, so every module's `.load.js` shares one top-level scope in one classic script. A duplicate top-level `const` is a parse-time SyntaxError that takes down the entire site bundle, and `breakpoints` is a far more collision-prone name than the file's existing `tableOptions` and `tableFilterInstances`. Rename it to `dataTableBreakpoints`. No behaviour change.
This was referenced Jul 14, 2026
Picks up the corrected wrap argument comment, which no longer claims that wrap is incompatible with data tables.
Contributor
Author
|
Bumped mod-utils to the released v6.4.2 ( |
Contributor
Author
|
🎉 This PR is included in version 4.1.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hinode's table
wrapargument moves a wide table's last column onto a full-width row of its own below the site's main breakpoint. Until now it was incompatible with data tables, because Hinode rendered a pre-split, ragged table that simple-datatables' uniform row model cannot represent.Hinode now renders a wrapped data table uniformly and marks it with
data-table-wrap/data-table-wrap-breakpoint. This PR makes the module build the wrapped layout itself, in the browser, through simple-datatables'tableRenderhook.Why the hook
tableRenderrewrites the virtual DOM on each render, whereas sorting, searching and paging operate on the library's internal row model. Transforming the virtual DOM is therefore purely cosmetic: the row model stays uniform, so the pager keeps counting records rather than rendered rows, sorting keeps each wrapped cell attached to its record, and search still matches text in the wrapped column.Crossing the breakpoint redraws via
dt.update(true), which preserves the active sort, page and search term —refresh()would clear the search.Note on the second commit
The first draft removed the wrapped column's
<th>. That desynchronized the rendered header count from the row model, and sincefixedColumnsdefaults totrue,_measureWidths()re-renders throughtableRenderand then walks the DOM headers in lockstep withdata.headings, dereferencingactiveDOMHeadings[N-1].offsetWidthon anundefinedelement. Every wrapped data table threw aTypeError— on load, on crossing the breakpoint, and on any window resize (via the library's own_onResize). The header cell is now kept and hidden withd-none, restoring the invariant and matching Hinode's plain wrap path.Verification
Driven in a real browser against the Hinode exampleSite: zero console errors; the pager reports 2 pages for 4 records at
perPage=2(records, not rendered rows); sorting keeps descriptions attached to their records; searching a word present only in the wrapped column still matches; the no-results message renders cleanly; sort, page and search all survive a breakpoint crossing; and no class accumulates across repeated sorts, searches and resizes.Requires Hinode with the corresponding
wraprework (gethinode/hinode).🤖 Generated with Claude Code