Repository navigation
fix(app-shell): a read-only form designer card no longer describes how to drag it (objectui#11924) - #11961
Merged
objectstack-fleet[bot] merged 1 commit intoOct 8, 2026
Conversation
…w to drag it (objectui#11924) dnd-kit's useDraggable writes aria-describedby onto every card, pointing at the canvas's drag instructions, and useSortable's attributes cannot remove it. A read-only package's card has its sensors off, so the card now drops that attribute when readOnly; a writable card keeps it. Pinned through the real ObjectFormDesigner in en and zh, with the writable card as the control and a read-only click-to-select / tab-order control. Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU Co-authored-by: Claude <noreply@anthropic.com>
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
|
objectstack-fleet
Bot
deleted the
claude/issue-11924-readonly-card-describedby
branch
October 8, 2026 12:15
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 free
to 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 #11924
Clause-②: no
What changed
In a read-only package, a field card in the form designer pointed
aria-describedbyat the canvas's drag instructions. A screen reader then told the author how to drag a card that cannot move. The read-only card now carries no description. A writable card keeps the instructions.packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx: inSortableField, one attribute follows the{...attributes}/{...listeners}spread,aria-describedby={readOnly ? undefined : attributes['aria-describedby']}, with a comment saying why. The card already overridesaria-labelthe same way, after the spread.packages/app-shell/src/views/studio-design/ObjectFormDesigner.readOnlyDescription-11924.test.tsx(new, beside it): the pins below..changeset/11924-readonly-card-describedby.md: an@object-ui/app-shellpatch.No other file changed. The change adds no export, prop, type member, spec key or language-pack key. Every file is inside the claim's file surface. The "not on it" list is untouched: the objectui#11898 keyboard placement,
StudioDesignSurface.tsx,packages/components/src/ui/**andpackages/i18n/**.Mechanism, measured
H1, confirmed. The installed
@dnd-kit/core6.3.1 setsaria-describedbyunconditionally. ItsuseDraggablebuilds its returnedattributeswith'aria-describedby': ariaDescribedById.draggable(read incore.esm.js).useSortablepasses the user'sattributesthrough forrole,roleDescriptionandtabIndexonly. So the card's existingroleDescription: readOnly ? '' : ...(objectui#11872) could not reacharia-describedby.H1 follow-up:
useSortable({ disabled: readOnly })is not the cleaner fix. Measured through the real component. I addeddisabled: readOnlyand removed this fix. A read-only card then rendered:{"role":"button","tabindex":"0","aria-disabled":"true","aria-roledescription":"","aria-describedby":"DndDescribedBy-0","aria-label":"Name — click to view properties"}aria-describedbystill points at the instructions. The card also gainsaria-disabled="true", yet a click still opens the inspector. Sodisabledfixes nothing here and adds a second false statement. The one-attribute override is the smaller correct change. The probe was a temporary file, removed afterwards. The tree is proven clean: blob == HEAD andgit diff HEADis empty.H2, confirmed: nothing else about a read-only card changes. It stays
role="button"withtabindex="0"and its read-only accessible name. It takes focus, and a click callsonSelectFieldwith its field name. A control pins this.H3: byte-neutral by construction. NOT MEASURED locally. The change is one JSX attribute: a ternary on a prop the component already holds. The build strips the comment. The instrument for this is the eager-closure budget bot's reading on this PR against
main's, and that reading comes from the CI build.Pins
The instrument is the card's computed accessible description: jest-dom's
toHaveAccessibleDescription, which followsaria-describedbyinto dnd-kit's hidden instructions node. The instructions are resolved by the keyengine.studio.formDnd.instructionsthrough the designer's table, in en-US and zh-CN.aria-describedby, and its description is neither locale's instructions.aria-describedby, and its description equals the instructions row. This shows the instrument can see the hidden node.Reverse check
The fix was committed first, at
c9e527f6c. The mutation then went through objectstack'sscripts/ablation-replace.mjsin wrap mode. The tool proves that the mutation landed on disk and that the restore matches HEAD.readOnly ? undefined : attributes['aria-describedby']becamefalse ? undefined : attributes['aria-describedby'], which is the pre-fix behaviour. Anchor count went 1 to 0. Blob wente9c9f48745f4to406253ee4fb5.Tests 2 failed | 3 passed (5). Both read-only pins went red (aria-describedby="DndDescribedBy-0", then-1). Both writable controls and the click control stayed green. The direction was red, as expected.e9c9f48745f4, andgit diff HEADis empty.There is no build or
dist/leg. The test imports./ObjectFormDesignerby relative path, so it runs the source.Gates
All gates ran at
c9e527f6c. That isgit rev-parse --short HEADafter the last commit, and the lock runs echoed it.pnpm exec vitest rununder the verify lock gaveTest Files 9 passed (9)andTests 69 passed (69), with lockVERDICT command-exit 0. The run covered:ObjectFormDesigner: the fiveObjectFormDesigner.*.test.tsxfiles (objectui#11802, objectui#11871, objectui#11872 and objectui#11898 among them),StudioDesignSurface.formFields.test.tsxandpackages/plugin-form/src/__tests__/autoLayout.wideSpelling.test.ts;packages/core/src/utils/__tests__/column-identity.ratchet.test.ts.turbo run build --filter=@object-ui/app-shell^...gave28 cached, 28 total. Thenpnpm --filter @object-ui/app-shell type-checkran (tsc --noEmit && tsc -p tsconfig.test.json; the second project'sincludeofsrc/**/*.test.tsxcovers the new test). Output:TYPECHECK-OK, lockVERDICT command-exit 0.pnpm check:i18n-keysexited 0.node scripts/check-changeset-presence.mjsexited 0: "2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)".node scripts/check-changeset-no-major.mjsexited 0.pnpm check:new-line-citationsprintedVERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.pnpm check:control-bytesexited 0.node scripts/check-vi-mock-override-shape.mjsandnode scripts/check-test-path-roots.mjsboth exited 0. These are lint-workflow gates that read test files.pnpm exec eslinton the two touched TS files exited 0. The--format jsonoutput reports 2 files, 0 errors and 1 warning. The warning is older than this PR:react-hooks/set-state-in-effecton the effect that re-syncsitemsfrom the draft, a line this diff does not touch.node scripts/check-governed-queue-guard.mjs --testover the three paths reported NOT GOVERNED.pnpm lintand the rest of the lint farm. The ESLint run above covers only the two touched files. It is not a proven narrowing.Acceptance notes
git grepfor screen-reader prose incontent/docsandpackages/app-shell/README.mdfinds 5 lines. As a control, none of them names the designer or dragging. The earlier sibling a11y fixes on this component touched no docs either.display: nonenode is not exposed. I left it alone.Session:
https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAUGenerated by Claude Code