Skip to content

Wait for tables to build before redrawing them - #103

Merged
irskep merged 2 commits into
mainfrom
fix-tab-redraw
Sep 17, 2026
Merged

irskep merged 2 commits into
mainfrom
fix-tab-redraw

Conversation

@irskep

@irskep irskep commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Loading the dashboard threw Cannot read properties of null (reading 'offsetWidth'), preceded by Tabulator's "Table Not Initialized" warning.

init() constructs every table and then immediately shows a tab. Tabulator initializes asynchronously and only reports readiness by firing tableBuilt, so showTab called redraw() on a table that hadn't built. That throws inside Tabulator's column manager, and since showTab is called from navigate, the throw took the rest of navigate with it, including setHash. So the URL hash was never set on load either.

Now the tabs that have fired tableBuilt are tracked, only those get redrawn, and a table that builds while its tab is already on screen redraws itself then. The reveal path forces the redraw, because a table that builds while hidden measures a zero-height container and renders no rows until something re-renders them.

Reproduced and verified in Chrome against a generated two-provider site, on plain load, on #<provider>, and on #<provider>-duplicates. Before: one uncaught TypeError, one warning, empty hash. After: no errors, no warnings, hash set, rows rendered. Clicking between providers and tabs and toggling the responsive checkbox (which destroys and rebuilds every table) are all clean. Also checked against a real two-provider dashboard with 298 and 117 rows.

No unit test. main.ts is DOM- and Tabulator-bound and vitest runs in the node environment here, so a test would be mostly mock. The browser check is the real verification.

Worth knowing for anyone hitting the console error before the next release: the generated bundle isn't minified, so sed 's/if (table) table\.redraw();/if (table) { try { table.redraw(); } catch (e) {} }/' over the generated index.html clears it too.

A goose does not attempt to land before the pond exists. Our tables were doing exactly that.

🤖 Generated with Claude Code

@irskep
irskep requested review from anjoola and mblair September 14, 2026 18:20
stevelandey-byte and others added 2 commits September 17, 2026 10:24
Tabulator initializes asynchronously and reports readiness by firing
tableBuilt. init() constructed every table and then immediately showed a
tab, so showTab called redraw() on a table that had not built yet. That
threw inside Tabulator's column manager and took the rest of navigate()
with it, including setHash, so the URL never reflected the active tab and
the table could be left unrendered.

Track which tables have fired tableBuilt, redraw only those, and redraw
on build for whichever tab is on screen by then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A table that builds while its tab is hidden measures a zero-height
container and renders no rows, which is observable: go sits at 0 rendered
rows until its tab is shown. An unforced redraw leaves that to chance,
while redraw(true) re-renders the rows outright, which is what Tabulator
asks for after an element's size changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@irskep
irskep merged commit bd48ff5 into main Sep 17, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants