Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-view): a named view's sort array rides the canonical grid slot, so the grid shows it and $orderby stops saying 'undefined undefined' (#5270) - #5294
Merged
Conversation
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 19, 2026
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Aug 19, 2026
os-support-ai
marked this pull request as ready for review
August 19, 2026 12:10
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 19, 2026
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#5270
Re-derived on the post-#5102 ref (triage's first binding note)
Triage asked for the premise to be re-checked on the merged ref, because PR #5274 (#5102) touched these very forwarding clusters. Re-derived on
origin/main@12841b617: the defect is still present, unchanged in mechanism, and #5274 moved where it lives without resolving it.What #5274 changed: it split
gridSchemainto a preamble plus a return, hoisted the two view precedence segments into locals, and added the canonical keys. The two lines that matter here came out of it asso the view sort was still written into
defaultSort, and the canonicalsortslot was deliberately blanked whenever a view was active. That was the correct call for #5102's scope — it kept thetablesegment from outranking a named view — but it also meant the one slot with the right arity was guaranteed empty in exactly the case this card is about.Measured, not inferred. The reverse verification below reverts this PR's source and keeps its tests; on the pre-fix source the fetch assertion reports
which is the issue's
'undefined undefined'claim reproduced verbatim on the post-#5102 tree.The defect
NamedListView.sortisArray< { field, order } >.ObjectGridSchema.defaultSortis a single{ field, order }. An array was being placed in the single-object slot, with no compile-time witness (ObjectViewSchema.tablecollapses to a bare index signature). Both ofObjectGrid's readers then failed, differently:parseSchemaSort(schemaSort ?? (schema.defaultSort ? [schema.defaultSort] : undefined))re-wraps an already-arraydefaultSortto[[{ field, order }]]. Each entry must be a string or an object with a stringfield; a nested array is neither, so the entry is skipped and the parse is[]— no arrow, and the first click on that column asks forascon a list alreadydesc.`${(schema.defaultSort as any).field} ${(schema.defaultSort as any).order}`reads two absent keys off an array.serializeOrderBypasses a non-empty string through untouched, so"undefined undefined"reached the wire.The fix, and how it relates to #4869's normalized shape (triage's second binding note)
#4869 is the sort-sink convergence card:
convertSortToQueryParamsis the shared sink, it accepts exactly two authored spellings —stringandSortConfig[]({ field, order }) — and it says in so many words that a spelling the sink does not accept must be adjudicated at the producer, never tolerated at the consumer.ObjectGridSchema.sortis declaredstring | SortConfig[]— the same pair the sink accepts. So the fix is to route the view segments into that slot:No new dialect is introduced, nothing is normalized in-line, and no consumer gains a tolerant fallback. When #4869 lands
$orderby: convertSortToQueryParams(sort), a named view's sort is already in a shape that sink accepts — this card removes one of the mismatches that card would otherwise have to fold in.Precedence is unchanged.
ObjectGridresolvesschemaSort ?? (schema.defaultSort ? [...] : undefined), so a view sort still outranks bothtable.sortandtable.defaultSort, andtable.sortstill outrankstable.defaultSort— the same ordermergedSort(:1243) and the non-grid fetch (:611) already express. A view that supplies no sort forwards byte-for-byte what it forwarded before.The filter half deliberately did not move. The issue flagged that
filter/defaultFiltersare not interchangeable downstream —ObjectGridlowers the canonical slot throughtoFilterNodeand raw-assigns the legacy one — so moving a named-view filter across would change that path's wire shape. The sort pair has no such asymmetry (both slots reach$orderbyunlowered), which is why only sort moves here.The #5102 pin: updated, not deleted
#5274's author wrote that the pin recording the current
defaultSortbehaviour should be updated, not deleted, by whoever took this card. Found and updated:ObjectView.canonicalTableKeys.test.tsx, blocka named view still outranks the table segment, testkeeps the named view sort in force over a table.sort. It assertedgrid.sort === undefined+grid.defaultSort === [{ field: 'name', order: 'desc' }]; it now asserts the array in the canonical slot and the legacy slot empty. What that block exists to pin — the precedence — is asserted more strongly than before: a second case was added covering a view sort against a legacytable.defaultSort, the direction the original pin did not cover.Tests
New:
packages/plugin-view/src/__tests__/ObjectView.namedViewSortArity.test.tsx, 4 tests, driving the realObjectGridrather than a probe that records the forwarded schema. That choice is load-bearing: the array was plainly visible in the forwarded object, just in the wrong slot, so a forwarded-shape assertion would have re-pinned the bug. Both halves the verification bar asks for are pinned end-to-end:$orderbyis'name desc', assertednot.toBe('undefined undefined')andnot.toContain('undefined');'status asc, name desc') — an arity the legacy slot structurally cannot hold;table.defaultSortwith no view still works, unchanged.Reverse verification
Ablation:
git checkout origin/main -- packages/plugin-view/src/ObjectView.tsx, tests kept. Prediction recorded before running; predicted and observed match exactly, 5 red / 26 green.draws the declared sort indicator before anyone clickssends the declared sort as $orderby, not "undefined undefined"Received: "undefined undefined")carries every key of a multi-key sortstill honours a table.defaultSort when no view supplies one(control)keeps the named view sort in force over a table.sort(updated pin)keeps the named view sort in force over a legacy table.defaultSort too(new pin)canonicalTableKeysassertions (#5102's own pins)No rebuild is in the ablation's resolution path, and none can hide a stale result.
../ObjectViewis a relative source import, and@object-ui/plugin-grid,@object-ui/components,@object-ui/core,@object-ui/reactand@object-ui/typesall resolve through theresolve.aliasmap in the rootvitest.config.mtsto each package's ownsrcdirectory. There is no package-exports-to-disthop on either leg, so a stale build can neither keep the ablated leg green nor keep the restored leg red. Restored afterwards;git statusclean against the commit, and both files re-ran 31/31 green.Gates run locally, all at
49ff25a71(the final commit)pnpm exec vitest run packages/plugin-view packages/plugin-grid(from repo root)pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-view^...' buildpnpm --filter @object-ui/plugin-view type-checkpnpm --filter @object-ui/plugin-view lintnode scripts/check-control-bytes.mjsnode scripts/check-changeset-presence.mjsnode scripts/check-phantom-dependencies.mjspnpm changeset:checkmajorPlus a manual control-byte sweep of the four changed files (
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') — clean.Changeset:
patchon@object-ui/plugin-view(defect repair; nevermajor, per AGENTS.md §版本号策略).Scope
Four files:
ObjectView.tsx, two of its test files, one changeset.table.columnson the grid path only — kanban/gallery/calendar and the delegated renderer readtable.fieldsalone, so a canonicaltable: { columns: [...] }yields an empty field list #5269 is not addressed here and remains open — thetable.columnsnon-grid gap is queued separately. This PR does not touch:842or:1247.$orderby: sort原样透传,归一化外包给了某一个 adapter #4869 remains open. This PR does not wrap$orderbyin the sink; it makes one of the shapes reaching that sink already-acceptable to it.ObjectViewProps.views[].sortdeclaresdirection, but every sort consumer readsorder— a host-supplieddescview silently sorts ascending #5293:ObjectViewProps.views[].sortis declaredArray< { field, direction } >while every sort consumer in the repo readsorder, so a host-composeddirection: 'desc'view sorts ascending at three independent readers. Not fixed here because [finding] ObjectView 是最后一处不走共享 sort sink 的读点 ——$orderby: sort原样透传,归一化外包给了某一个 adapter #4869 already ruled that an unrecognised spelling must be adjudicated at the producer rather than tolerated at the consumer, and the two candidate routes (extend the sink's contract vs. correct the prop toorder, a public-surface break) are a decision, not a refactor. Worth knowing for review: on the grid path this PR changes that broken case from "no arrow,$orderby: 'undefined undefined'" to "ascending arrow,$orderby: 'name undefined'" — both are server-rejected sorts, and the new state matches what the non-grid path has always done with the same input (serializeOrderByreads a missingorderas ascending), so the two paths now agree while both wait onObjectViewProps.views[].sortdeclaresdirection, but every sort consumer readsorder— a host-supplieddescview silently sorts ascending #5293.Generated by Claude Code
Generated by Claude Code