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 working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions