Repository navigation
Commit 7858c1d
fix(app-shell): name the page block inspector's JSON editor by its label (#27254)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8D1GfomwtVv1LMaZwNA9aHS)_
Fixes #27178
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio 页面设计器的块检查器里,JSON 编辑框(`InspectorJsonField`)的标签现在用 `htmlFor`
指向它的 textarea(id 由 `React.useId()` 生成)。
- 为什么改:原来标签和输入框没有关联,读屏器只读出一个没有名字的文本框,测试也无法按标签找到这个字段。
- 风险与回滚:只加了 label 与 textarea 的 id 配对,不改写入的数据和提交逻辑;回滚即 revert 本 PR。
- 先看:`PageBlockInspector.tsx` 中的 `InspectorJsonField`;新测试
`PageBlockInspector.jsonFieldLabel-27178.test.tsx`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.jsonFieldLabel-27178.test.tsx`,预期
2/2 通过。
- 需要你做的:无,席位落地。
## What changes
`InspectorJsonField` in `PageBlockInspector.tsx` mints an id with
`React.useId()` and pairs it: `Label htmlFor={id}` and the textarea's
`id={id}`. This is the same scheme `FilterRulesField` and
`ColorPropField` in the same file already use. The id is minted inside
the component, not derived from the label or prop name, because
`renderField` also draws this editor inside array rows, where a
caller-derived id would repeat.
Every door that draws the editor gets the pairing, because they all go
through the one component: a curated `kind: 'json'` prop (for example
`record:related_list.add`, `element:button.action`), the JSON fallback
for a stored value in another union arm (`sort` stored as a string,
`columns` as `ListColumn` objects), and the generic "Advanced" editor
for object or array props. There was no `aria-describedby` or error
wiring on the field to keep; the invalid-JSON message is unchanged.
## Tests
New `PageBlockInspector.jsonFieldLabel-27178.test.tsx`, on the real
inspector inside a host that applies every patch:
- `getByLabelText('Add existing records (picker)')` returns the `add`
textarea, its accessible name is that label, and typing JSON and
blurring writes `add` in the stored shape.
- With `sort: 'subject'` stored, `getByLabelText('Sort')` returns the
JSON fallback textarea showing `"subject"`, and the `Sort` and `add`
editors resolve to two different textareas with two different ids.
At `bc69c4ad`: every `PageBlockInspector*` test file in the directory,
13 files, 86 tests passed. `pnpm --filter @object-ui/app-shell run
type-check` (both the source and the test tsconfig): exit 0. `eslint` on
the two changed files: exit 0, no new warning.
<details><summary>Ablation (drop the htmlFor / id pair)</summary>
Through `scripts/ablation-replace.mjs`, on the committed fix: anchor
hits 1 to 0, blob `e552a33e` to `1da05b77`. Both new tests turn red with
the card's own wording: `Found a label with the text of: Add existing
records (picker), however no form control was found associated to that
label` and the same for `Sort`. Restore: blob back to the HEAD blob
`e552a33e`, `git diff HEAD` empty.
</details>
## Acceptance notes
- Changeset: none. `@object-ui/app-shell` is `private: true`, so the PR
carries `skip-changeset`.
- The `jsonEditorUnder` helper in
`PageBlockInspector.relatedListProps-27054.test.tsx` says the label "is
a sibling, not a `for`". That comment is now out of date; the helper
still works because the label and the textarea share a parent. Left as
is (outside this card's file surface).
- Three other labels in the same file still name no control:
`FieldListField`'s label, and the `string-list` and `array` labels in
`renderField`. Each labels a group of rows, so the fix there is a group
name (`role="group"` with `aria-labelledby`), not this `for` pairing.
Reported to the seat as a finding; not changed here.
## Verification
Derived gates at `e781cf8f` (`node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, 24 families), every one
run locally, exit codes captured before any pipe: all 24 exit 0,
including `node scripts/check-sdui-manifest.mjs` and its `--self-test`,
and `pnpm --dir ui exec vitest run scripts/` under the verify lock.
`dispatch-gates --ran`: 24 derived, 24 run, 0 NOT-MEASURED.
`dispatch-gates --tier`: no path-derived mandate. 109 changed lines,
under the 3000 threshold; no governed path. Every family CI runs is read
from this PR's check-runs.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbW6EicqdfE9onssvrp
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbW6EicqdfE9onssvrp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c13ef04 commit 7858c1d
2 files changed
Lines changed: 108 additions & 1 deletion
File tree
- ui/packages/app-shell/src/views/metadata-admin/inspectors
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
183 | 187 | | |
184 | 188 | | |
185 | 189 | | |
| |||
195 | 199 | | |
196 | 200 | | |
197 | 201 | | |
198 | | - | |
| 202 | + | |
199 | 203 | | |
| 204 | + | |
200 | 205 | | |
201 | 206 | | |
202 | 207 | | |
| |||
0 commit comments