Skip to content

field:select 从不向辅助技术播报校验状态 —— aria-invalid / aria-describedby / aria-required 被 Radix Select.Root 静默丢弃 #3306

Description

@xuyushun441-sys

在 objectui#3291 的只读审计中实测发现(origin/main @ 91757a79f),不在该单的 PR 里修 —— 修它会改动 SelectField 的展开点,与 #3291toDomProps 改动同一位置,必须排序而非夹带。

事实

SelectField.tsx:113{...props} 展开到 Radix Select.Root

<Select {...props}>

Select.Root 不是 DOM 宿主,它静默丢弃所有不认识的 prop —— 包括表单渲染器传下来的 aria-invalidaria-describedbyaria-required

实测:一个 requiredfield:select 在提交失败后渲染出的是

<button role="combobox"  id="" data-testid="select-trigger-f">

没有 aria-invalid,没有 aria-describedby,而同一行的 <FormMessage/> 正确显示着「L is required」。

也就是说:视觉上有红字报错,屏幕阅读器用户完全收不到这个字段处于错误状态、也拿不到错误信息的关联。

为什么这是独立的一单

这与 objectui#3222 修的是同一个失败类 —— 当时给 7 个 widget 接上了 aria-invalidselect 不在那批里。而 widget-aria-invalid-e2e.test.tsx 也没覆盖它,所以它既没被修,也没被守。

注意它与 #3291 的关系是相邻而非重叠#3291 治的是「不该上 DOM 的 prop 上了 DOM」,本单治的是「该上 DOM 的 prop 没上去」。两者症状相反、成因相邻(都在 SelectField 的展开点),所以必须排序:先落 #3291toDomProps(它会改变 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpm:queue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions