Skip to content

29 个注册 field widget 在校验失败后从不携带 aria-invalid —— #3306 全注册表守卫实测的账本 #3318

Description

@xuyushun441-sys

#3306 落地时按裁决补了「每个注册 widget 校验失败必须携带 aria-invalid」的全注册表守卫(packages/fields/src/__tests__/widget-aria-invalid-registry-e2e.test.tsx,范式同 #3291 的 DOM leak sweep)。真实表单 + 真实 react-hook-form 校验失败(required + null,presence 语义,cloud#972)、先自证 "is required" 消息已渲染再读 aria。实测 46 个注册类型中 29 个在整行 DOM 里找不到任何 aria-invalid="true" —— 视觉上有红字报错,屏幕阅读器完全收不到字段处于错误状态:

multiselect, radio, checkboxes, tags, lookup, master_detail, file, image,
location, formula, summary, auto_number, user, owner, object, vector, grid,
color, slider, rating, code, avatar, address, geolocation, signature, qrcode,
object-ref, filter-condition, recipient-picker

(通过的 17 个:text / textarea / number / boolean / select(#3306 修复)/ date / datetime / time / email / phone / url / currency / percent / password / markdown / html / richtext。)

成因(与 #3222 / #3306 同一失败类,两种形态)

  • 大多数:widget 根本不把任何 DOM pass-through(含 <FormControl> Slot 注入的 aria-invalid / aria-describedby)转发到它渲染的真实控件上——没有 toDomProps spread,也不读 error 槽位。
  • 少数(如 grid):内部有自己的 per-cell aria-invalid,但表单级失败不驱动它。

账本与棘轮

这 29 个类型登记在守卫文件的 NOT_YET_DELIVERED ledger 里,且按反方向断言(sweep 证明它们仍未送达):修好任何一个,它的 ledger 行会变红,强制从账本移除、转入正向守卫。账本只能收缩;新 widget 不能悄悄进入账本(新增类型默认走正向断言)。

修法(每个 widget 同一范式)

#3222/#3306 的纪律:把 toDomProps(props) spread 到 widget 的真实可聚焦控件(注意 Radix 非 DOM Root 会吞掉 aria-*,见 #3306SelectTrigger 处理),并在 spread 之后写 aria-invalid={!!error}error 是 spec FieldWidgetPropsSchema 的发布槽位)。消息文本仍归 <FormMessage/>,widget 只置状态。可分批处理,每批从 ledger 删除对应条目。

参考


Generated by Claude Code

Metadata

Metadata

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