Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[fields] 全屏长文本对话框不播报校验失败:richtext 的对话框编辑面恒 aria-invalid="false"(字段实为 invalid),textarea 的干脆没有该属性,两者都不指向错误信息 #4824

Closed

Description

@yinlianghui

在 #4810 的实施中实测发现并按 Prime Directive #10 立单(不认领、不扩围到 #4810)。与 #4810 无关:#4810 只谈 inline 编辑面的 host plumbing,本条发生在 fullscreen 对话框那一份编辑面上。

实测

裸渲染 widget,error 有值(字段处于校验失败态)、mobile_fullscreen: true,点开展开按钮后读对话框内编辑面的属性:

INLINE  richtext  aria-invalid= true
DIALOG  richtext  aria-invalid= false   aria-describedby= null
INLINE  textarea  aria-invalid= true
DIALOG  textarea  aria-invalid= null    aria-describedby= null

richtext 那一行是更尖锐的一半:对话框不是「没说」,而是主动播报 aria-invalid="false" —— 同一时刻同一字段的 inline 面正播报 true。机理是 RichTextEditorSurface 在对话框那次渲染里没有拿到 error prop,于是 aria-invalid={!!undefined} 落成字面的 false。textarea 那一半是属性整个缺席。

影响

mobile.fullscreenLongText 是已发布的 opt-in:开了它,用户在手机上就是在这个对话框里写长文本。校验失败后再打开对话框修改时,读屏用户在对话框内既拿不到「此字段无效」状态,也拿不到错误文案 —— 表单的 FormMessage 在对话框外,modal 打开期间被 Radix aria-hidden 掉。

为什么需要设计裁决而不是照抄一行

TextAreaField 现有注释已经点到一半:对话框里的 aria-describedby 是重新构造的,因为 host 给的那些 id 指向对话框外的节点、"There is nothing to preserve"。这个判断对字数说明成立,但正因为它成立,错误状态就必须在对话框内另有承载:是把 FormMessage 的文案复制一份进对话框(要防双份显示,#3222 明确把文案留给 FormMessage),还是只置 aria-invalid + 一个对话框本地的 aria-errormessage 节点,是两种不同的形状。两个 widget 与共享的 FullscreenFieldEditor 应当一次定齐,别只修一个(#4250 的教训:半修比统一的缺口更糟)。

相关

Activity

  1. self-assigned this
    on Aug 16, 2026
  2. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    ContributorAuthor

    【认领 · 测量型派发】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt):晋级 pm:queue 并按测量型模式派发(批次 11 补位)—— 零代码零 PR,产出判别材料供裁决(#3173/#3829 先例)。卡内两形状(复制 FormMessage 文案进对话框 vs aria-invalid + 对话框本地 aria-errormessage)与 #3222 契约的相互作用需先量清,#4788 的教训在前(该族预裁被实测证伪),不预裁不投机。


    Generated by Claude Code

  3. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    ContributorAuthor

    【PM 处置 · 测量收讫,转决策箱】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt):pm:queue → needs-user-decision。测量报告(dev 评论/回报)判别材料齐,三问待裁,dev 建议 + PM 附议如下:

    测量要点:①受影响面是三个渲染面不是两个(form.tsx 内置 FullscreenTextarea 同缺陷,在 packages/components,fields 范围 sweep 扫不到),3/3 全坏,根因是 FullscreenEditor primitive 无校验槽位、编辑器由宿主手写注入;②ARIA 规范 MUST 排除「指回模态外被 aria-hidden 的 FormMessage」捷径(D 形,测试全绿但违规 —— 实施派发词必须显式禁写);③两形状对「指错节点」在 jsdom 层同盲,钉子必须加容器归属断言;④aria-errormessage 在本仓工具链只认单 ID,不能复合进 describedby 链,但两属性可指同一节点。

    三问(dev 建议 E1 + A主B增,PM 附议):

    1. 字段 widget 的错误提示键:spec 声明 error,objectui 渲染 errorMessage(declared ≠ enforced) #3222「widget 不渲染文案」规则是否改写为「同一时刻只有一份文案在无障碍树内」(容纳对话框内文案节点)—— 保持字面则只剩 B 通道;
    2. 落点:E1(FullscreenEditor 一处渲染错误节点 + children render prop 第 4 个必填 aria 参数,照 全屏长文本编辑器的字数计数只有数字,读屏用户在该对话框里完全拿不到字数信息 #3417 必填参数先例)vs 三宿主各自实现;E1 需动 components 的 primitive 公开签名(3 消费者);
    3. 范围:第三面(components 包)是否同批修(The richtext type is spelled three ways across four layout/column sets, and the spec spells it none of them #4250 半修教训说该同批;需确认 scope fence)。

    关联裁决建议同批:#4832(对话框控件无可访问名,同一批控件同一段 JSX)。裁定后一轮可做完。


    Generated by Claude Code

  4. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    Contributor

    Maintainer ruling (2026-08-16, recorded by PM session session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the evening decision-inbox report's recommendations in full, verbatim: 「8931 我已经处理了,其他接受你的所有建议。」 The dev + PM recommendations from the measurement round are ratified on all three questions:

    1. 字段 widget 的错误提示键:spec 声明 error,objectui 渲染 errorMessage(declared ≠ enforced) #3222's rule is rewritten as "only one copy of the error text is in the accessibility tree at any moment" — accommodating a dialog-local error text node while the modal is open (the outside FormMessage is aria-hidden by Radix during that window).
    2. Landing point: E1 — FullscreenFieldEditor renders the error node once, and its children render prop gains a required fourth aria parameter (the 全屏长文本编辑器的字数计数只有数字,读屏用户在该对话框里完全拿不到字数信息 #3417 required-parameter precedent), rather than three hosts each hand-implementing it. The components primitive's public signature change (3 consumers) is accepted.
    3. Scope: all three affected surfaces in one batch, including the form.tsx built-in FullscreenTextarea in packages/components (the The richtext type is spelled three ways across four layout/column sets, and the spec spells it none of them #4250 half-fix lesson) — scope fence extended accordingly.

    Binding implementation notes from the measurement: ⛔ the "point back to the aria-hidden outside FormMessage" shortcut is explicitly forbidden (ARIA MUST violation that tests can't see — write it into the dispatch); pins must carry container-ownership assertions (jsdom is blind to wrong-node targeting); aria-errormessage single-ID constraint observed. #4832 (dialog controls have no accessible name) rules with this and lands in the same batch — same controls, same JSX.

    Label flipped needs-user-decision → pm:queue in the same stroke.


    Generated by Claude Code

  5. self-assigned this
    on Aug 16, 2026
  6. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    ContributorAuthor

    【认领】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt)批次 12 派发,os-dev 实施,与 #4832 同批同 PR(维护者裁定并批)。分支:claude/issue-4824-fullscreen-dialog-aria。

    按 2026-08-16 维护者裁定(评论 5307575529)执行:#3222 规则改写为「同一时刻只有一份错误文案在无障碍树内」;落点 E1 —— FullscreenFieldEditor 一处渲染错误节点,children render prop 增必填第 4 个 aria 参数(#3417 必填参数先例),components primitive 公开签名变更(3 消费者)已获准;三面同批(RichTextField / TextAreaField / form.tsx 内置 FullscreenTextarea)。⛔ 硬禁:指回模态外被 aria-hidden 的 FormMessage(ARIA MUST 违规且测试盲);钉子必须带容器归属断言;aria-errormessage 单 ID 约束。#4832 的可访问名同笔交付(aria 参数同时承载 name 通道)。

    文件面:packages/components(FullscreenFieldEditor primitive + src/renderers/form/form.tsx 内置面)+ packages/fields/src/widgets/{TextAreaField,RichTextField}.tsx + RichTextEditorSurface + 测试。⚠️ 前置门:form.tsx 与在飞 #4788(host 包容器)同文件 —— #4788 的 PR 落 main 后才动 form.tsx,其余文件可先行。


    Generated by Claude Code

  7. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    ContributorAuthor

    【os-dev 实施完成】draft PR #4864,分支 claude/issue-4824-fullscreen-dialog-aria,与 #4832 同批同 PR。按维护者裁定(评论 5307575529)三条逐条落实,三个面全部落地。

    落点 E1:FullscreenEditor 收字段的 error(必填 prop),在对话框内渲染错误文案节点,并把 aria-labelledby / aria-invalid / 单 IDREF aria-errormessage 作为 children render prop 的必填第 4 参数交给宿主展开。宿主不知道任何 id,所以结构上无法指向模态外的节点。#4832 的可访问名经同一参数承载,复用 DialogTitle 的可见文本(id 落在 DialogTitle 内部的 span,不能落在 DialogTitle 本身 —— Radix 把 id={context.titleId} 放在属性展开之前,覆盖它会让对话框自己失去名字)。

    三面读数(改前 → 改后):

    面 aria-invalid aria-errormessage 可访问名
    richtext 对话框 false(字段实为 invalid) → true 无 → 对话框内节点 空 → F
    textarea 对话框 属性缺席 → true 无 → 对话框内节点 空 → F
    内置 textarea 对话框 属性缺席 → true 无 → 对话框内节点 空 → F

    ⛔ 硬禁已写进 primitive 注释与钉子:不指回模态外被 aria-hidden 的 FormMessage。每条 IDREF 断言都带容器归属(toContainElement),内置面那份还额外断言该 id 不等于表单项的 message id —— 它是三个面里唯一能对着真实 FormMessage 测的。

    前置门:按门规 60 秒一轮等 #4788 落 main,等满 40 轮超时(彼时它连分支都没 push),先交付了两个面;随后 #4788 以 PR #4862 落地,git merge origin/main 无冲突后补上第三面。⛔ 未抢改同文件,两边落在 form.tsx 的不同区域。

    验证:新钉子 3 文件 38 条全绿(第三面那份先写后修:实现前 7 failed | 4 passed,实现后 11 passed);合并 main 后 vitest run packages/fields packages/components packages/plugin-form = 287 files / 3400 tests 全过;type-check 12/12。反向验证先预判后跑,断开 richtext 面的第 4 参数展开,预判「红 4 条且全在 richtext」并明确写出不会红的部分(「有效时播报 false」会保持绿 —— 那正是修复前的缺陷在这一格碰巧看起来对),实测 4 failed | 23 passed 逐条相符。

    changeset:@object-ui/components minor(必填 prop 是 breaking,按仓内先例发 minor 并在正文写清 FROM → TO)+ @object-ui/fields patch。

    无范围外发现需另立单。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions