发现于 #3979 的实施过程(围栏外,PR #3993 未夹带 —— #3979 的范围是 block-config.ts 的 placeholder 列)。未认领,交 PM triage。
机制
packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx 里三个共享字段组件都渲染成"一个 Label + 一个控件"的兄弟结构,Label 没有 htmlFor、控件没有 id,也没有 aria-label 兜底:
InspectorTextField —— _shared.tsx:157(Label)/ :158(Input)
InspectorNumberField —— _shared.tsx:186 / :187
InspectorSelectField —— _shared.tsx:225(Label)/ :231(SelectTrigger)
所以标签与控件之间只有视觉上的邻接,没有可编程的关联。辅助技术拿不到可访问名:焦点落进这些框时读出的是"编辑框",而上面那行中文/英文标签是一段没有归属的文本。同文件的 InspectorCheckboxField(:258)是对的 —— 它用包裹式 <label>,可以作为反例参照。
不是 dormant:这三个组件是 metadata-admin 检查器的通用输入件,16 个非测试文件引用它们(页面块检查器、流程节点、报表、数据集、权限等各检查器),打开任何一个检查器面板即渲染。
机械症状(已经被当作既成事实绕开了)
PageBlockInspector.sectionName.test.tsx:92-98 的注释把这件事写下来当作定位手段的理由:
The section-name boxes, in section order. Located by the placeholder because InspectorTextField renders its <Label> unassociated with the <input> (no htmlFor/id), so getByLabelText cannot reach it
也就是说:测试之所以必须按 placeholder 定位,正是因为可访问名不存在。这是同一个缺陷的可测量形态 —— getByLabelText 读的就是 AT 读的那条链。副作用是它把 placeholder 变成了事实上的定位锚(#3979 处理这一列时因此要格外小心 en 值逐字节不变)。
边界与去重
实施时值得先想清的一点(记录现象,不代人决定)
id 从哪来是个小决定:useId() 生成、还是由调用方传入(检查器里同一个字段名可能在数组项里重复出现,page:tabs.items[i].label 之类)。useId() 更难写错;但若将来要支持"点标签聚焦控件"之外的 aria-describedby(例如 #3912 的校验消息),调用方可见的 id 会更顺。留给实施者论证。
另外值得一并想:修好之后,sectionName 那类测试可以从"按 placeholder 定位"改成 getByLabelText,那才是与 AT 同一条链的断言 —— 但这会牵动若干测试,属该 PR 自己的 fixture 处置面。
Generated by Claude Code
发现于 #3979 的实施过程(围栏外,PR #3993 未夹带 —— #3979 的范围是
block-config.ts的 placeholder 列)。未认领,交 PM triage。机制
packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx里三个共享字段组件都渲染成"一个Label+ 一个控件"的兄弟结构,Label没有htmlFor、控件没有id,也没有aria-label兜底:InspectorTextField——_shared.tsx:157(Label)/:158(Input)InspectorNumberField——_shared.tsx:186/:187InspectorSelectField——_shared.tsx:225(Label)/:231(SelectTrigger)所以标签与控件之间只有视觉上的邻接,没有可编程的关联。辅助技术拿不到可访问名:焦点落进这些框时读出的是"编辑框",而上面那行中文/英文标签是一段没有归属的文本。同文件的
InspectorCheckboxField(:258)是对的 —— 它用包裹式<label>,可以作为反例参照。不是 dormant:这三个组件是 metadata-admin 检查器的通用输入件,16 个非测试文件引用它们(页面块检查器、流程节点、报表、数据集、权限等各检查器),打开任何一个检查器面板即渲染。
机械症状(已经被当作既成事实绕开了)
PageBlockInspector.sectionName.test.tsx:92-98的注释把这件事写下来当作定位手段的理由:也就是说:测试之所以必须按 placeholder 定位,正是因为可访问名不存在。这是同一个缺陷的可测量形态 ——
getByLabelText读的就是 AT 读的那条链。副作用是它把 placeholder 变成了事实上的定位锚(#3979 处理这一列时因此要格外小心 en 值逐字节不变)。边界与去重
BlockPropField没有格式/校验能力位 —— 所有标识符类字段(sectionname、tabkey、accordionvalue)只能靠 placeholder 陈述约定 #3912:那单要的是BlockPropField上的格式/校验能力位(pattern/validate),是数据面的表达力;本单是共享渲染组件的标签关联,两者可各自独立落地。block-config.ts的 placeholder 列搬成翻译键,一个字节都没动_shared.tsx。for(#3975) #3983):同一族问题(标签关联)但不同站点 —— 那单是packages/components多选控件的 chip group 用 IDREF 命名替换失效的for。本单在packages/app-shell的 metadata-admin 检查器。若维护者希望按"标签关联"整族推进,可考虑挂到那条线下,但两处代码没有共享实现。div上;本单三个组件根本没有 id/htmlFor可言。InspectorTextField、htmlFor/label association、_shared.tsx、检查器 无障碍),无同题单。实施时值得先想清的一点(记录现象,不代人决定)
id从哪来是个小决定:useId()生成、还是由调用方传入(检查器里同一个字段名可能在数组项里重复出现,page:tabs.items[i].label之类)。useId()更难写错;但若将来要支持"点标签聚焦控件"之外的aria-describedby(例如 #3912 的校验消息),调用方可见的 id 会更顺。留给实施者论证。另外值得一并想:修好之后,
sectionName那类测试可以从"按 placeholder 定位"改成getByLabelText,那才是与 AT 同一条链的断言 —— 但这会牵动若干测试,属该 PR 自己的 fixture 处置面。Generated by Claude Code