Uh oh!
There was an error while loading. Please reload this page.
fix(a11y): 用真 label 命名 InlineCreateRelated 的 Link 页签搜索框 (#3381) - #3410
Merged
Conversation
…aceholder InlineCreateRelated's "Link Existing" search Input had no label of any kind — no `<label>`, no `aria-label`, no `aria-labelledby` — so its accessible name fell through to the placeholder: the HTML-AAM last resort, gone the moment the user types, and absent entirely under `dom-accessibility-api` (measured pre-fix name here: the empty string). Visually hidden `<label htmlFor>` + a `React.useId`-namespaced id, the same shape #3341/PR #3380 left on the create tab, rather than `aria-label` — the name stays a real label element on the same expression as the placeholder instead of a detached string that can drift. The id uses a hyphenated `link-search` segment so it cannot collide with a create-tab field named `search`. The magnifier is explicitly `aria-hidden` (lucide already defaults to that for childless icons; spelled out so the intent is local). Rendered copy is unchanged: the placeholder string is byte-identical. Refs #3381 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 5, 2026 21:44
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#3381
前提复核(对 origin/main 2409e1d)
issue 成立,行号已按 PR #3380 之后的现状复核:
packages/plugin-detail/src/InlineCreateRelated.tsx的 Link 页签搜索框在 L279-L285(正文写的是 ~L253-260,PR #3380 把它推后了约 26 行)。该 Input 确实没有任何 label(无 label 元素、无aria-label、无aria-labelledby),而同文件 Create 页签在 #3380 之后已经是「label 文本即可访问名」——正文描述的「唯独此处例外」属实。改了什么
按 PM 裁定取视觉隐藏 label +
htmlFor/React.useId(否决aria-label):searchLabel = \Search ${objectName}`,**label 文本与 placeholder 同一表达式**(placeholder 只多一个省略号)。这正是不用aria-label` 的理由:可访问名留在真实 label 元素上、且与可见文案同源,不会各自漂移。searchInputId = \inline-create-${instanceId}-link-search`。instanceId复用 #3341 引入的React.useId(一个详情页每个相关列表挂一个该组件)。用带连字符的link-search段是为了和fieldDomId(...)的后缀(元数据字段名,惯例[a-z0-9_]+)**不可能**撞上——包括一个字面叫search` 的字段。aria-hidden="true"(dist/cjs/lucide-react.js:92),即现状已经是隐藏的。仍然显式补上——这是依赖的默认值而非我们的声明,写出来意图留在本地、也不随图标库升级改变。见「反向验证」里对这一条的诚实标注。detail.searchRecords文案是Search records…,与本框的Search {objectName}…不同源,消费它反而会把 label 和 placeholder 拆开,与本单方向相反)。渲染文案逐字节未变。测试
新增
packages/plugin-detail/src/__tests__/InlineCreateRelated.linkSearchLabel.test.tsx(9 条)。除常规的getByLabelText/toHaveAccessibleName/labels集合 / 每实例 id 不撞车外,两条专门钉验收里那个「分水岭」:fireEvent.change写入North,真实浏览器里 placeholder 已不显示)可访问名仍是Search Contact,并顺带断言该框确实是活的搜索框(过滤掉了不匹配的记录);Search Contact、placeholder 是Search Contact…,末尾省略号让两者可区分,因此「名字不是把 placeholder 读回来」这一点在任何实现下都可断言——包括真会做 placeholder 兜底的浏览器。这一条正面解决了正文提到的「兜底跨实现不一致」。命令与结果(仓根跑,已确认输出里点名了本文件;
--reporter=verbose):反向验证(方向事先声明,两个方向都记下)
预期主方向:把源码改回 origin/main → 新测试转红。 实测 9 条全红,
toHaveAccessibleName的 Received 是空串:这正好复现了 issue 正文的测量结论:
dom-accessibility-api不实现 placeholder 兜底,所以测试环境下修前的可访问名是空串(真实浏览器才会兜到Search Contact…)。一条要诚实标注的例外:「装饰图标不进 a11y 树」这条并不由本 PR 的显式
aria-hidden支撑。 单独做了定向反验——保留 label 修复、只删掉 Search 上显式的aria-hidden——该条仍然绿(lucide 的默认值兜住了)。也就是说这条断言钉的是结果(图标不被朗读),不是我们新加的那个属性;它的价值在于将来图标库换掉/改默认时会报警。全量反验里它也变红,但那只是因为前置的getByLabelText先失败,不构成独立证据。另外首版里「字段名叫
search不撞车」那条,在修前是空绿(修前搜索框根本没有 id,id 集合平凡地不重复)——已按「替换整条 fixture」的做法改成读取两个页签各自的真实 id 再比对,修前会因为getByLabelText找不到而红。顺带记一笔:Radix Tabs 1.1.x 的 tab 是mousedown激活的,fireEvent.click切不过去(测试里已写明)。范围
严格限于
packages/plugin-detail/src/InlineCreateRelated.tsx+ 新增测试 + changeset(patch,仅@object-ui/plugin-detail)。消费半径已扫:该组件只在packages/plugin-detail/src/index.tsx导出,仓内没有别的 fixture/测试按 placeholder 定位这个框。未改 props/spec/渲染文案。🤖 Generated with Claude Code
https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
Generated by Claude Code