Uh oh!
There was an error while loading. Please reload this page.
fix(components,plugin-grid): fire column-state persistence on column resize and reorder - #6211
Merged
Merged
Conversation
…and reorder `DataTable` declared `onColumnResize` and never invoked it, and `ObjectGrid` emitted `onColumnReorder` while the renderer invokes `onColumnsReorder`. Both were the only call sites of `saveColumnState`, so a user's column drag was never written — not to localStorage, not through `onColumnStateChange`. Part of #6175 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSoz9uGhaaSgiq3hshtN7L
…e fires The ledger note described objectui#6175 as unfixed and named `onColumnResize` as read nowhere. Both are now false. The two-spelling ruling stays open and is restated as the deliberate residue it is; no ledger membership changes. Part of #6175 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSoz9uGhaaSgiq3hshtN7L
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 25, 2026 02:42
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Fixes#6175
A user drags a column wider or reorders columns in
ObjectGrid, and nothing persists. Thesaved state is read back correctly forever; it is simply never written. Both call sites of
saveColumnStatewere dead, for two different reasons — one per package.Re-derived line numbers (measured at
2c8474c04, the merge-base of this branch)The card's numbers were measured 2026-08-24 22:29Z. Re-derived here; every one still held.
saveColumnStatedefinitionObjectGrid.tsx:661onColumnStateChangenotify inside itObjectGrid.tsx:669-670const dataTableSchema: anyObjectGrid.tsx:2937onColumnResizeproducer call siteObjectGrid.tsx:3084onColumnReorderproducer call siteObjectGrid.tsx:3090SchemaRenderer schema={dataTableSchema}ObjectGrid.tsx:3735onColumnsReorder— the renderer's only reorder calldata-table.tsx:1346-1347onColumnResizeindata-table.tsxonColumnReorderindata-table.tsxonColumnsReorder?: (columns: TableColumn[]) => voiddata-display.ts:718onColumnResize?: (columnKey, width) => voiddata-display.ts:791onColumnReorder?: (newOrder: string[]) => voiddata-display.ts:796One site the card did not name and that mattered:
ObjectGrid.tsx:2992setsreorderableColumns: schema.reorderableColumns ?? false, so grid reordering is off bydefault — the reorder test has to enable it explicitly or it measures nothing.
The change
@object-ui/components—data-table.tsx.DataTableSchemadeclaredonColumnResizeall along and this renderer invoked it nowhere: the drag updated local
columnWidthsstateand stopped.
handleResizeEndnow reports the settled width once, atmouseup, via alastResizeWidthref (the document listeners are one render's closures and cannot read alater state update). Once, deliberately: the host turns this into a write to shared view
config, so a per-
mousemovecallback would be a write storm.@object-ui/plugin-grid—ObjectGrid.tsx. The producer emittedonColumnReorder(singular) while the renderer invokes the near-duplicate
onColumnsReorder(with thes) —a different declared key with a different signature. The producer now emits the spelling the
renderer actually invokes, mapping the reported
TableColumn[]to theaccessorKeyordercolumnStatestores.Producer-side, per AGENTS.md #0.1 (fix the producer, don't add a lenient alias in the
consumer). Teaching the renderer to also call the singular spelling was the alternative
and is rejected here: it would give one event two live call paths, which is the
consumer-side dialect that commandment bans — and it would make the open ruling below
strictly harder, converting a dead-key removal into a behaviour change.
⛔ Nothing is retired, and the deferred ruling is left standing
Both spellings remain declared on
DataTableSchema;packages/types/src/data-display.tsisuntouched.
onColumnReorderstays declared and stays unwired — exactly its status beforethis branch. The two-spelling ruling was not needed and was not taken. PR #6181 parked
that ruling on the card this branch implements, and it is still parked: which spelling
survives is a declared-surface change and remains open.
Which spelling the renderer invokes after this change:
onColumnsReorder, the same one itinvoked before. The renderer's reorder path is unchanged.
onColumnReorderisunchanged in reachability too — still declared, still invoked by nothing. What changed
is that it is no longer emitted by ObjectGrid either, so it is now dead on both ends
rather than written-but-unread.
onColumnStateChangereachesdataSource.updateViewConfig. That path was unreachablefrom a column drag before this branch and is reachable now — a resize or reorder now
produces a real write to shared view config where it previously produced none. Any
permission gate on that write (#5232, via PR #6125) will therefore see traffic on a path
that had never carried any.
updateViewConfigitself is not touched here.⛔ Not repaired here (deliberately)
const dataTableSchema: any(ObjectGrid.tsx:2937) is the enabling cause and is leftalone — it belongs to #6004, not to this branch. A typed handoff would have caught the
reorder half and not the resize half: with a real
DataTableSchemaon the producer,onColumnReorderwould still have type-checked (it is genuinely declared, with the exactsignature ObjectGrid used) — the two declared spellings defeat the type. The resize half is
different: no type system flags a consumer that never reads a declared optional key. That
asymmetry is worth carrying to #6004.
Verification
Reproduce-before-fix, then ablate-after-commit. Both directions predicted before running.
columnStatePersistencecases red, and red for avalue reason —
AssertionError: expected "vi.fn()" to be called at least once, not aquery or harness error.
trap … EXIT INT TERM): 3 of 4cases red;
git diff HEAD --statempty afterwards. Mutation proven on disk both ways —injected strings absent, removed strings back, with
handleResizeEnd/saveColumnStateas controls present in both versions of each file.
does not fire when the drag never moved.The unrepaired renderer fires nothing at all, so "did not fire" is trivially true there.
It is a guard against over-firing, not a pin of the change, and the test file says so
inline. The three cases that do distinguish the two states of the world are the other
three.
The pins observe the write, never the read-back:
localStorageactually written andonColumnStateChangeactually fired. A test that seeds a width and re-reads it passes withthe outbound half still dead, because the already-working inbound half answers it.
type-check(both packages, uncached)turbo run type-check --filter=@object-ui/components --filter=@object-ui/plugin-grid --forceTasks: 15 successful, 15 totaltype-checkdownstream sweep, uncachedturbo run type-check --filter='...@object-ui/components' --filter='...@object-ui/plugin-grid' --forceTasks: 66 successful, 66 total, 31 packagespnpm exec vitest run packages/plugin-grid packages/components273 passed (273)files,2516 passed (2516)packages/typespnpm exec vitest run packages/types58 passed (58)files,632 passed (632)pnpm exec eslintover the 5 changed pathscheck-control-bytesnode scripts/check-control-bytes.mjsscanned 5125 tracked text file(s)check-lint-coveragenode scripts/check-lint-coverage.mjs46/46 packages lintedcheck-type-check-coveragenode scripts/check-type-check-coverage.mjs41/41 packages compile their testscheck-changeset-presencenode scripts/check-changeset-presence.mjscheck-changeset-no-majornode scripts/check-changeset-no-major.mjspatchThe downstream filter direction is demonstrated, not asserted:
--filter '...@object-ui/components'resolves to 31 packages including
plugin-grid,app-shell,consoleand all fourexample-*apps (consumers);--filter '@object-ui/components...'resolves to 9 includingtypes,core,react(dependencies). The prefix form is the downstream one, and it iswhat ran — so
./examples/**is built and type-checked here.Every turbo gate treated as evidence ran with
--force;Cached: 0 cachedon the run thatreports it. The ratchet-family gates and eslint were re-run on the final head
a31843d79with a clean tree, after the last commit.Deviation from the declared file surface, declared
The dispatch named
data-table.tsx,ObjectGrid.tsx, tests and a changeset. One filebeyond that is touched:
packages/types/src/__tests__/zod-mirror-parity.test.ts— a doccomment only, no assertion and no ledger membership changed (that file's 5 tests pass, and
RuntimeOnlyDeclaredis about zod mirroring, not about being read). ItsRuntimeOnlyDeclarednote stated thatonColumnResizeis "read NOWHERE" and that"column-state persistence never fires". After this branch both sentences are false, and the
note names this very card — leaving it would have left a documented falsehood pointing at
its own repair. The note now restates the two-spelling residue as the deliberate open ruling
it is. No changeset bump for
@object-ui/types: the change is a comment.Generated by Claude Code