Uh oh!
There was an error while loading. Please reload this page.
feat(types,components,plugin-grid): carry the staged row across the renderCellEditor seam as pendingRow - #7241
Merged
Conversation
…enderCellEditor seam as `pendingRow` `renderCellEditor`'s context on `DataTableSchema` gains a seventh member, `pendingRow` — the persisted `row` shallow-merged with the row's staged, unsaved edits. `row` keeps meaning the persisted record; neither is redefined. `data-table` builds it from `pendingChanges`, identity-stable per row while nothing new is staged, so a dependent widget's identity-keyed fetch effects do not re-fire on every table re-render. `ObjectGrid` scopes a `dependsOn` inline editor by `ctx.pendingRow ?? ctx.row`, so a parent edited in the same row re-scopes the child before anything is saved, and the interim marker that named the follow-up is gone with the interim. The exact-shape pin in `@object-ui/types` is extended, not weakened, and gains a control proving it can tell the seventh member's presence from its absence. Test 4 of the grid fixture flips from pinning the saved-row staleness to asserting that the staged parent scopes the child, with its own proof that the staging landed and nothing was saved. The zod mirror encodes `renderCellEditor` as `z.function()` with no parameter shape; only its description records the delta. The docs snippet and prose for `data-table` carry the new member. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
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
|
This was referenced Sep 2, 2026
This was referenced Sep 2, 2026
os-litant
marked this pull request as ready for review
September 2, 2026 01:48
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#7188
Option B of the A/B split ruled on #7165 (comment 5491922231); #7165 remains open — its closure is the PM's, after landing. Clause-②: this widens a published, exactly-pinned context on
DataTableSchema(@object-ui/types), ruled at tier in #7188 comment 5502616875 and carried unchanged.The contract delta
DataTableSchema.renderCellEditor's context,packages/types/src/data-display.ts:column,row,value,stage,commit,cancelcolumn,row,pendingRow,value,stage,commit,cancelrowpendingRowrowshallow-merged with the row's staged, unsaved edits (itspendingChangesentry); the SAME object asrowwhen nothing is stagedrowwas not redefined. Makingrowmean the merged record would be a silent semantic change to an already-published member — the option that passes review by looking smaller — and a host that needs the persisted value would lose its only source. Both are addressable; neither is redefined. (The card's reasoning; the tier ruling adopted it.)pendingRowis required, not optional, becausedata-tablealways passes it — the pin's principle is "transcribed from the call site".packages/types/src/zod/data-display.zod.ts:270):renderCellEditor: z.function().optional()— it encodes no parameter shape, so there is no ctx member to mirror. The only zod-side delta is the.describe()text, which now records the two-row semantics and names the member onDataTableSchemaas the authority.zod-mirror-parityis about key membership and is unaffected.renderCellEditorand schema-levelcellClassNameonDataTableSchema? — the two live undeclared keys the #6459 census measured #6882 exact-shape pin (data-table-declared-keys-6882.test.ts) went red on the seventh member by design and was extended, not weakened:CellEditorContextgainspendingRow; theEqual(notextends) form and the stated reason are kept; a new direction proof_SixMemberShapeIsRefused(@ts-expect-erroron the six-member shape) proves the instrument can tell the seventh member's presence from its absence. Leg B of the ablation shows the exact red.Producer —
packages/components/src/renderers/complex/data-table.tsxinjectEditor({ column, row, pendingRow: rowHasChanges ? pendingRows.of(row, rowIndex) : row, value, stage, commit, cancel }). The ruled expression wasrowHasChanges ? { ...row, ...rowChanges } : row; the values are identical, but the merged object is cached per row object and rebuilt only whenpendingChangesis replaced (every stage / save / cancel builds a new Map). Measured reason:LookupFieldkeys itsdependentFilter→popoverFiltermemos on the identity of the record it is handed, and its recent-idsuseEffectkeys onpopoverFilter, so a fresh object per render would re-issue that query on every table re-render while the picker is open (useRecordQueryitself is immune — it keys on a JSONfilterSignature).rowis identity-stable for the same reason. Implementation detail beneath the contract; the member and its semantics are exactly as ruled.Consumer —
packages/plugin-grid/src/ObjectGrid.tsxdependentValues={ctx.pendingRow ?? ctx.row}(the ruled spelling).pendingRowis a required member, so the??never selects for a conforming host; it is spelled so a context handed to this factory without it degrades to bug(plugin-grid): adependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165's saved-row scoping rather than to{}(gated forever). Not a second contract — the comment on the line says so.dependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165's interim block is deleted (44 lines, from the INTERIM header to the prop). The facts it carried that are still load-bearing — why the repair is a prop and could not have been a provider (finding(react,fields):SchemaRendererContexthas nodataand noformValuesmember, so the tail ofdependentValues ?? ctx.formValues ?? ctx.datais unconditionally empty in production — and three comments describe it as live #7206); half 1 / half 2 of PR fix(fields): unlock dependent lookups on parent selection and enforce the cascade on every picker #2216 — are kept in a shorter comment with no INTERIM / open-question language.pendingRow: any.ObjectGridDataTableSchemaHolds.renderCellEditor(the local copy finding(plugin-grid):ObjectGrid's held-key census still callsrenderCellEditorundeclared and its ruling "pending" — #6882 declared it two days ago, so the comment now states the opposite of the tree #7196 measured as redundant andEqualto upstream) carriespendingRowtoo, so the seam's intersection stays inert —Equalin both directions — instead of becoming a second, narrower signature. Its deletion remains finding(plugin-grid):ObjectGrid's held-key census still callsrenderCellEditorundeclared and its ruling "pending" — #6882 declared it two days ago, so the comment now states the opposite of the tree #7196's separate card. Nothing here touches grouping (A null entry ingrouping.fields[]throws a TypeError that takes down the whole grid, before any projection is built #7217 is queued behind this on the same file).The assertion B adds —
gridDependentValues-7165.test.tsx, test 4Edit the parent cell (
region→south), do not save, open the child picker. Asserts: Person 07 (south) offered, Person 01 (north) not; the query carried$filter.region === 'south'and never'north'; the region cell renders the pendingsouthwhilerows[0].regionis stillnorthandds.updatewas never called (so the staged and persisted parents cannot coincide). Tests 1–3 and thelookupPickerKeys-7154suite are the live controls — their rows carry no staged edits, sopendingRowandrowcoincide there; they stay green under both A and B, which is why they cannot see the regression and test 4 can.Ablation — after commit
d20e5c4; both legs restored viagit checkout HEAD --, proven by blob hash = HEAD blob and emptygit diff HEADObjectGrid.tsxback todependentValues={ctx.row}(mutation proven on disk: B-line count 1→0, A-line 0→1, blob hash ≠ HEAD):Tests 1 failed | 8 passed (9)— exactly test 4 (× 4 — ⭐ objectui#7188: a STAGED parent re-scopes the child…) red; tests 1–3 and all 7154 tests green. No dist rebuild is involved: the rootvitest.config.mtsaliases@object-ui/types/components/fields/react/plugin-gridtosrc(lines 281–288), so the suite reads the mutated source directly.pendingRow: any;from the declared ctx (member count 1→0, blob hash ≠ HEAD):tsc -p tsconfig.test.jsoninpackages/typesexits 2 with(127,3) TS2344on_RenderCellEditorShape,(143,1) TS2578: Unused '@ts-expect-error' directiveon_SixMemberShapeIsRefused, and(164,40) TS2339: Property 'pendingRow' does not existon the authoring example. Direction: red, as predicted; the control directive flips exactly as designed.Census of
renderCellEditorctx readersGrep over the whole tree excluding
node_modules/dist, control termctx.rowhit (inapp-shell/resolveActionParams.ts— a different ctx — and inplugin-grid).examples/,apps/,plugin-dashboard,plugin-detail,app-shell: no reader.packages/plugin-grid/src/ObjectGrid.tsx— the only production consumercolumn,row,value,stage,commit, nowpendingRowpackages/components/src/renderers/complex/data-table.tsxpackages/components/src/__tests__/data-table-injected-editor-focus-6859.test.tsx—stagingEditor({ column, value, stage })packages/components/src/__tests__/data-table-inline-edit.test.tsx:192—({ column, stage })packages/plugin-grid/src/__tests__/dataTableSchemaSlot-6459.test.ts:158—(ctx) => ctx.columnEqual)packages/types/src/__tests__/data-table-declared-keys-6882.test.tscontent/docs/components/complex/data-table.mdx:50One file outside the dispatched surface — declared
content/docs/components/complex/data-table.mdx(+9/−1): the published documentation of exactly this ctx listed six members. AGENTS.md Commandment #2 (docs reflect the code) is binding, and leaving it would ship documentation that contradicts the type it documents. Drop the hunk if the PM prefers it filed separately.Verification on
d20e5c4(the pushed head)pnpm --filter @object-ui/components --filter @object-ui/plugin-grid build(prebuild rebuilttypes/core/react) — green;type-checkfor@object-ui/types,@object-ui/components,@object-ui/plugin-grid(eachtsc --noEmit && tsc -p tsconfig.test.json; types alsotsconfig.examples.json) — zero diagnostics;--listFilesconfirms the 6882 pin (1) and the 6459 + 7165 files (2) are in theirtsconfig.test.jsonprograms. Lock verdict line:os-verify-lock: VERDICT command-exit 0.data-table-inline-edit,data-table-injected-editor-focus-6859,inlineEditLookupRepro):Test Files 7 passed (7)/Tests 31 passed (31).check-changeset-presence(3 changesets for 3 released packages),check-changeset-no-major,check:control-bytes,check:spec-symbols,check:doc-types,check:doc-fences,check-doc-links,check:dist-completeness; eslint on the seven changed ts/tsx files — 0 errors.check:sdui-registration-pins,check:eager-closure,check:doc-snippets,check:readme-exports. This diff touches no registration and no README, and thedata-table.mdxsnippet is aplaintextfence.🤖 Generated with Claude Code
https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
Generated by Claude Code