在 #3975(PR #3983)的验证过程中实测到的一条,与那个 PR 无关也不由它引入:checkboxes 在今天的 main 上就是这个形态。单独立单。
实测
一次性 probe(未提交),真 form renderer + 裸注册(labelling: 'group'),读 host label 的 for / 自身 id / 文档里 aria-labelledby 消费者的个数:
multiselect readonly:true for=(none) labelOwnId=…-group-label ariaLabelledbyConsumers=0 namedGroups=0 byLabelText=0
checkboxes readonly:true for=(none) labelOwnId=…-group-label ariaLabelledbyConsumers=0 namedGroups=0 byLabelText=0
multiselect options:[] for=(none) labelOwnId=…-group-label ariaLabelledbyConsumers=0 namedGroups=0 byLabelText=0
multiselect mode:'view' for=(none) labelOwnId=…-group-label ariaLabelledbyConsumers=1 namedGroups=1 byLabelText=1 ← 正常
multiselect 可编辑(对照) for=(none) labelOwnId=…-group-label ariaLabelledbyConsumers=1 namedGroups=1 byLabelText=1 ← 正常
即:字段级 readonly: true,以及零个可选项这两种状态下,可见标签是 0 个元素的可访问名。整个表单级 mode: 'view' 不受影响(它不走 widget 的 readonly 分支)。
机理
labelling: 'group' 的字段,form renderer 让 label 发布自己的 id 并撤掉 for(#3961 的正确修法),把 aria-labelledby 交给 widget —— 但这些 widget 的提前 return 分支根本不展开 groupDomProps:
packages/fields/src/widgets/MultiSelectField.tsx:61-73 的 readonly 分支,返回 div.flex.flex-wrap.gap-1 + 一排 Badge,不带 id、不带 aria-labelledby;CheckboxesField.tsx:60-72 逐字同形;
MultiSelectField.tsx:79-89 / CheckboxesField.tsx:78-88 的零选项分支返回 OptionsEmptyState,同样不展开。
于是 IDREF 只有生产者、没有消费者。这类失效比 #3961 修的那种更安静:for 悬空至少还能被「每个 for 都要解析到可 label 元素」的扫描看见,而「label 发了 id 但没人引用它」在标记里看不出任何异常。
两侧都是坏的,严重程度未变,只是形状从「悬空 for」变成「无人消费的 IDREF」。所以这既不是 #3961 的回归,也不是 #3983 的回归,而是那次修法没有覆盖到的状态。#3961 的 e2e 钉全部在可编辑态取样,这正是它漏过的原因。
覆盖面
已实测:checkboxes、multiselect(readonly + 零选项)。未实测:radio / rating / file / address / geolocation 的 readonly 分支各自写法不同,需逐个量,不要按本单推断。
处置方向(未裁决)
参考
在 #3975(PR #3983)的验证过程中实测到的一条,与那个 PR 无关也不由它引入:
checkboxes在今天的main上就是这个形态。单独立单。实测
一次性 probe(未提交),真 form renderer + 裸注册(
labelling: 'group'),读 host label 的for/ 自身 id / 文档里aria-labelledby消费者的个数:即:字段级
readonly: true,以及零个可选项这两种状态下,可见标签是 0 个元素的可访问名。整个表单级mode: 'view'不受影响(它不走 widget 的 readonly 分支)。机理
labelling: 'group'的字段,form renderer 让 label 发布自己的id并撤掉for(#3961 的正确修法),把aria-labelledby交给 widget —— 但这些 widget 的提前 return 分支根本不展开groupDomProps:packages/fields/src/widgets/MultiSelectField.tsx:61-73的 readonly 分支,返回div.flex.flex-wrap.gap-1+ 一排Badge,不带 id、不带aria-labelledby;CheckboxesField.tsx:60-72逐字同形;MultiSelectField.tsx:79-89/CheckboxesField.tsx:78-88的零选项分支返回OptionsEmptyState,同样不展开。于是 IDREF 只有生产者、没有消费者。这类失效比 #3961 修的那种更安静:
for悬空至少还能被「每个for都要解析到可 label 元素」的扫描看见,而「label 发了 id 但没人引用它」在标记里看不出任何异常。与 #3961 / #3975 的关系(重要:不是回归)
for悬空,checkboxes / radio / rating / file 的for落在不可 label 的 div 上 #3961 之前:readonly 分支下for指向…-form-item,而该 id 在 readonly 分支里没有任何元素携带 → 悬空for,同样什么都不命名;for悬空,checkboxes / radio / rating / file 的for落在不可 label 的 div 上 #3961 / [fields] multiselect 的 host label 也落在不可 label 的包裹 div 上 —— 与 #3961 同一失效类,但不在其审计的六个 widget 内 #3975 之后:没有for,也没有 IDREF 消费者 → 仍然什么都不命名。两侧都是坏的,严重程度未变,只是形状从「悬空
for」变成「无人消费的 IDREF」。所以这既不是 #3961 的回归,也不是 #3983 的回归,而是那次修法没有覆盖到的状态。#3961 的 e2e 钉全部在可编辑态取样,这正是它漏过的原因。覆盖面
已实测:
checkboxes、multiselect(readonly + 零选项)。未实测:radio/rating/file/address/geolocation的 readonly 分支各自写法不同,需逐个量,不要按本单推断。处置方向(未裁决)
groupDomProps(至少id+aria-labelledby),并按需给容器role="group"—— 只读的一组值仍然是一组值,命名它是对的;for悬空,checkboxes / radio / rating / file 的for落在不可 label 的 div 上 #3961 的 e2e 文件目前只在可编辑态取样。参考
for悬空,checkboxes / radio / rating / file 的for落在不可 label 的 div 上 #3961 / PR fix(fields,components): 复合/分组 field widget 的组标签改用 IDREF 关联(#3961) #3978(labelling声明位 + 六个 widget)for(#3975) #3983(multiselect,本条即在其验证中实测到)multiple: true的 select 字段在对象表单里 host label 仍然惰性 —— 按字段类型键入的 labelling 声明碰不到它(#3975 修完后的实测残留) #3986(同一批验证里的另一条残留:multiple: true的 select 走field:select,按类型键入的声明碰不到它)