在 objectui#3291 的只读审计中实测发现(origin/main @ 91757a79f),不在该单的 PR 里修 —— 修它会改动 SelectField 的展开点,与 #3291 的 toDomProps 改动同一位置,必须排序而非夹带。
事实
SelectField.tsx:113 把 {...props} 展开到 Radix Select.Root:
Select.Root 不是 DOM 宿主,它静默丢弃所有不认识的 prop —— 包括表单渲染器传下来的 aria-invalid、aria-describedby、aria-required。
实测:一个 required 的 field:select 在提交失败后渲染出的是
<button role="combobox" … id="…" data-testid="select-trigger-f">
没有 aria-invalid,没有 aria-describedby,而同一行的 <FormMessage/> 正确显示着「L is required」。
也就是说:视觉上有红字报错,屏幕阅读器用户完全收不到这个字段处于错误状态、也拿不到错误信息的关联。
为什么这是独立的一单
这与 objectui#3222 修的是同一个失败类 —— 当时给 7 个 widget 接上了 aria-invalid,select 不在那批里。而 widget-aria-invalid-e2e.test.tsx 也没覆盖它,所以它既没被修,也没被守。
注意它与 #3291 的关系是相邻而非重叠:#3291 治的是「不该上 DOM 的 prop 上了 DOM」,本单治的是「该上 DOM 的 prop 没上去」。两者症状相反、成因相邻(都在 SelectField 的展开点),所以必须排序:先落 #3291 的 toDomProps(它会改变 SelectField 展开什么),再按新的展开形状修本单,否则两边会互相覆盖。
影响面
至少 field:select。审计只实测了 select,但同类结构(把 props 展开到非 DOM 宿主的 Radix Root 组件)在其它 widget 上是否存在未经核查 —— 修本单时应当顺带扫一遍,并按 #3291 契约测试的同一范式补上「每个注册 widget 在校验失败时都必须携带 aria-invalid」的守卫,让这一类不再逐个复发。
参考
- objectui#3222(同类修复,7 个 widget)
- objectui#3291(相邻改动,必须先行)
packages/fields/src/__tests__/widget-aria-invalid-e2e.test.tsx(现有守卫,未覆盖 select)
Generated by Claude Code
在 objectui#3291 的只读审计中实测发现(
origin/main@91757a79f),不在该单的 PR 里修 —— 修它会改动SelectField的展开点,与 #3291 的toDomProps改动同一位置,必须排序而非夹带。事实
SelectField.tsx:113把{...props}展开到 RadixSelect.Root:Select.Root不是 DOM 宿主,它静默丢弃所有不认识的 prop —— 包括表单渲染器传下来的aria-invalid、aria-describedby、aria-required。实测:一个
required的field:select在提交失败后渲染出的是没有
aria-invalid,没有aria-describedby,而同一行的<FormMessage/>正确显示着「L is required」。也就是说:视觉上有红字报错,屏幕阅读器用户完全收不到这个字段处于错误状态、也拿不到错误信息的关联。
为什么这是独立的一单
这与 objectui#3222 修的是同一个失败类 —— 当时给 7 个 widget 接上了
aria-invalid,select不在那批里。而widget-aria-invalid-e2e.test.tsx也没覆盖它,所以它既没被修,也没被守。注意它与 #3291 的关系是相邻而非重叠:#3291 治的是「不该上 DOM 的 prop 上了 DOM」,本单治的是「该上 DOM 的 prop 没上去」。两者症状相反、成因相邻(都在
SelectField的展开点),所以必须排序:先落 #3291 的toDomProps(它会改变SelectField展开什么),再按新的展开形状修本单,否则两边会互相覆盖。影响面
至少
field:select。审计只实测了select,但同类结构(把 props 展开到非 DOM 宿主的 Radix Root 组件)在其它 widget 上是否存在未经核查 —— 修本单时应当顺带扫一遍,并按 #3291 契约测试的同一范式补上「每个注册 widget 在校验失败时都必须携带aria-invalid」的守卫,让这一类不再逐个复发。参考
packages/fields/src/__tests__/widget-aria-invalid-e2e.test.tsx(现有守卫,未覆盖 select)Generated by Claude Code