Repository navigation
fix(components,plugin-list): the Sort picker lists a field it keeps only for the current sort as removable only (objectui#11943) - #11981
Conversation
…ld as removable, never as a new choice (objectui#11943) SortBuilder's fields entries take an optional `disabled`: the entry is passed to SelectItem as an unavailable option, "Add sort" seeds the first entry that is not disabled and is disabled when none is. ListView's sortFields flags every entry it keeps only because the current sort names it: unreadable, platform-refused, or relational. Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw Co-authored-by: Claude <noreply@anthropic.com>
… restate the objectui#11962 in-use pin (objectui#11943) The click case leaves the dropdown open when nothing is chosen, which hides the rows from the accessibility tree; it now asserts the list is still open and closes it. The objectui#11962 in-use pin keeps its assertions; its title and comment stop describing the half-state this change ends. Adds the changeset. Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: PR #11981 on card #11943, the removable-only half under triage ruling ① Derived judgmentsPublic surface (
Accept set,
Accept set,
② Semver level
③ Boundary flagsDev deviations (report
Fence: the PR shape: draft; first line
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #11943
Clause-②: yes — this widens a published prop type of
@object-ui/components:SortBuilderProps.fieldsentries gain an optionaldisabled(one optional key; existing callers never pass it and behave as before).The list's Sort picker now lists a field it keeps only because the current sort names it as removable only: marked unavailable, never offered as a new choice. This is the half objectui#11962 left (triage's ruling on the card, direction A1 from the retriage). With objectui#11962's read check already on
main, it completes the card.What changes
SortBuilder(packages/components/src/custom/sort-builder.tsx). Afieldsentry takes an optionaldisabled. It is passed toSelectItem, so every row's dropdown draws the entry as an unavailable option (aria-disabled="true",data-disabled) that cannot be chosen by click or keyboard. A row whose current field it is still shows its label, and can be changed to another field or removed. Add sort seeds the first entry that is not disabled, and is disabled when no entry is choosable (it was disabled only for an empty list). No i18n key, so A2 is not taken.ListView'ssortFieldsmemo (packages/plugin-list/src/ListView.tsx). An entry carriesdisabled: trueexactly when the in-use exception alone keeps it: unreadable (canReadFielddenies), relational (listed with the by-ID suffix), or refused by the served projection (or, with no projection served, by the type read). A field the two rules list anyway carries no flag, whether or not the sort names it. Every entry it listed before is still listed with the same label: only the flag is new.sortFieldsand theeffectiveFieldscomment stop promising a follow-up and state what holds. ThecanReadFielddocblock makes no such promise and is unchanged.ListView.sortFieldRead-11943.test.tsx, "a field the current sort already names stays listed") keeps its assertions. Its title and comment stop describing the half-state this change ends..changeset/11943-sort-in-use-removable-only.md:@object-ui/componentsminor (an additive optional prop),@object-ui/plugin-listpatch.The dispatch's mechanism assumptions, measured
SortBuilderand its RadixSelect(sort-builder-disabled-entry-11943.test.tsx), each beside a control that runs the same interaction on the same entry without the flag: the flagged option rendersaria-disabled="true"anddata-disabled(control: neither); click and mouse pointer-up choose nothing and the list stays open (control: chosen); Enter on the option and typeahead on the closed trigger skip it (control: chosen); a row whose value is the flagged entry shows its label (control: a value with no entry renders blank), can be changed to another field, and can be removed.ListView.sortRemovableOnly-11943.test.tsxruns the same sequence for an unreadable in-use field, a platform-refused in-use field (served projection, as objectui#6455's pins serve it) and a relational in-use field (Owner (by ID)), each declared first on the object so an unflagged Add sort would seed it: listed disabled and nothing else is; its row shows its label; Add sort seedsTitle; the new row offers it only as unavailable and choosing it changes nothing; removing its row drops it from the list. The relational case is flagged too: objectui#4243 took relational fields out of the picker as new choices, and the in-use entry exists only so its row is not blank and the sort survives an edit. Flagging it keeps that row as it was and stops other rows re-offering it. Controls: a readable sortable field in use carries no flag and Add sort still seeds the first entry; with full read and no projection, the same unreadable and refused fields are ordinary options.emitSortChangeis untouched, and its docblock is still true. The five objectui#11962 pins ran unedited and green on the committed fixa64e78e:Test Files 1 failed | 3 passed (4),Tests 4 failed | 18 passed (22), where the one failing file was a temporary measurement probe (below) and the pin file passed 5 of 5. They stayed green under every mutation below. Only pin 4's prose was edited afterwards.config-field-renderer.tsxpassesfield.fields, whoseConfigFieldtype declares nodisabled;plugin-view'sSortUI.tsxand app-shell'sObjectListViewInspector.tsxmap to{ value, label }explicitly. Their existing tests ran unedited in the union below.Reverse check
On the committed fix
2b80540, each mutation landed and was restored throughscripts/ablation-replace.mjs(anchor 1 to 0, blob changed; restore blob equal to HEAD andgit diff HEADempty), running the two new files plus the objectui#11962 file (17 tests). Baseline:Tests 17 passed (17).SortBuilderstops passingdisabledtoSelectItemTests 6 failed / 11 passed (17): exactly thosefields[0]againTests 4 failed / 13 passed (17): exactly thoseTests 1 failed / 16 passed (17)ListViewdropsdisabled: truefrom the in-use entryTests 3 failed / 14 passed (17)Tests 1 failed / 16 passed (17): the unreadable caseCross-package type, read from the rebuilt
.d.ts. After the closure build,packages/components/dist/custom/sort-builder.d.tsdeclaresdisabled?: boolean. A temporary file inplugin-list(deleted by trap;git statusempty after) passingdisabled: 'no'answersTS2322: Type 'string' is not assignable to type 'boolean | undefined'(a stale.d.tsanswers an excess-property error instead), anddisabled: truecompiles.Gates
On HEAD
2b80540, heavy runs through the container's verify lock:pnpm exec vitest run --maxWorkers=2 packages/components/src/ packages/plugin-list/ packages/plugin-view/src/plus the twoObjectListViewInspector.*.test.tsxfiles andscripts/__tests__/:Test Files 738 passed | 3 skipped (741),Tests 11143 passed | 26 skipped (11169),VERDICT command-exit 0.pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-list^...' build(13 of 47 projects): exit 0.pnpm --filter @object-ui/components type-checkandpnpm --filter @object-ui/plugin-list type-check(each echoestsc --noEmit && tsc -p tsconfig.test.json): exit 0.--listFilesOnlylists the new test files in each test program.node scripts/check-changeset-presence.mjs: "5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)".check-changeset-no-major,check:changeset-claims,check:pending-changeset-literals,check:doc-types: exit 0.check:new-line-citations:0 new citation(s).check:control-bytes: OK.check:skill-examplesandcheck:readme-exportsstop at their precondition (they need the app-shell closure built: exit 2 "PRECONDITION NOT MET", and "579 self-import(s) could not be judged"). The narrowing: no file underskills/,content/docs/or anyREADME.mdnamesSortBuilderorsort-builder(git grepzero hits; controlFilterBuilder/GroupingEditorhits), and no export name moves. CI runs both.eslint --no-inline-config --format jsonover the 5 touched TS files (the config's**/*.{ts,tsx}population): 5 files, 0 errors. Warnings equal their base blobs linted over stdin (sort-builder.tsx2,ListView.tsx199, the objectui#11962 pin 0); the two new tests 0.eslint.config.jsenables no type-aware linting (tseslint.configs.recommended, no parser project), and nothing undereslint-rules/reads the file system, so untouched files cannot move. Repo-wide lint is CI's.@object-ui/componentssits in the eager closure; this adds a few lines of source. The Bundle Analysis comment is CI's.Acceptance notes
MePermissionsProviderset-up and a column list carrying labels, in a temporary probe deleted after the run: the Sort picker withholdsSecret Note([Title, Status, Priority], so the read answer is loaded and denying), while the hide-fields popover lists[Title, Status, Secret Note]. The sameallFieldslist feeds the Group editor's field options (shown by default) and the Row color field select: both offerSecret Notetoo. With full read the lists are the same. Reported for the seat to file.SortBuilder, so no doc changes.SortBuilderPropsedit also drops one trailing space on thefieldsline.Session:
https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgwGenerated by Claude Code