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.

[app-shell] metadata-admin FieldRow 的宿主 id 不带路径作用域(mdf-{字段名})—— 同名子字段产生重复 DOM id,第二个标签的 for 解析到第一个字段的控件 #5062

Closed

Description

@yinlianghui

发现于 #5039 的实施(PR #5061)。未认领,交 PM triage。 与 PR #5061 无关联改动 —— 该 PR 未触碰 id 方案,此缺陷在 main 上同样成立。

机制

SchemaForm.tsx 的 FieldRow 用字段的机器名直接构造宿主 id:

const id = `mdf-${name}`;

name 是该层的字段名,不含任何路径前缀。但 FieldRow 在多个嵌套层级被递归调用 —— CompositeField 的每个子行、RepeaterField 卡片布局展开行、RecordField 展开项、以及递归 SchemaForm 的内层字段。同名子字段落在同一文档里就会撞 id,而 for 只会解析到文档中的第一个匹配。

实测(真 SchemaForm 渲染)

两个 composite 字段 a / b,各有一个名为 field 的子字段(标签分别为 "Field A" / "Field B"),值分别为 x / y:

ids: ["mdf-a-label","mdf-field","mdf-b-label","mdf-field"]
DUPLICATES: ["mdf-field"]
Field A → for=mdf-field  resolves to value="x"
Field B → for=mdf-field  resolves to value="x"     ← 指向了 Field A 的输入框

即:点击 "Field B" 会聚焦到 Field A 的输入框,辅助技术把 Field A 的控件读成 "Field B"。这不是悬空(#4871 / #4857 / #4788 那一类「解析到无」),而是解析到了错的元素 —— 一个交叉接线的关联,比悬空更难从 DOM 上看出来。

与既有单的关系

可能的落地形态(供裁,未实施)

  1. 路径作用域 id:FieldRow 接收祖先路径(composite/repeater/record/递归 SchemaForm 各自已知自己的路径段),id 拼成 mdf-a.field / mdf-b.field。契约清晰、可预测,但要给 FieldRow 加一个贯穿所有递归点的 prop。
  2. React useId() 前缀:天然唯一、改动最小,但 id 不再可预测,现有按 mdf-{name} 取元素的测试与 e2e 选择器会一起失效(需先普查),且失去「id 可由字段名推出」这一便利。

倾向 1(声明式、可预测、与既有测试面兼容),但涉及公共 DOM 契约,交维护者裁。

复现

packages/app-shell/src/views/metadata-admin/SchemaForm.tsx,FieldRow 内 const id = ... 一行;两个 composite 字段带同名子字段即可,读数如上。

去重

检索过 open issues(metadata-admin SchemaForm duplicate id mdf- collision nested composite label wrong control、repeater grid cells unnamed label association),除已关闭的兄弟单 #3343 外无同题单。

Activity

  1. added theissue type on Aug 17, 2026
  2. os-steve commented on Aug 17, 2026

    @os-steve
    Collaborator

    Triage (unified triage seat, completing the label-only half-state — bug+pm:queue were applied at 22:04Z without an audit comment; classification confirmed): type Bug, stays pm:queue. Cross-wired label association (resolves to the wrong control) measured on a real render; duplicate DOM ids are an invariant violation, so this is direct-fix territory, not a decision card — the "交维护者裁" the card requests is answered at the seat level: take direction 1 (path-scoped ids), which the card itself prefers — declarative, predictable, compatible with the existing mdf-{name} selector surface at the top level (top-level fields keep their current ids; only nested rows gain a path prefix). Direction 2 (useId()) breaks every existing selector and loses derivability, for no added correctness.

    Dispatch notes: before coding, census in-tree consumers of mdf- selectors (tests/e2e) and state in the PR which ids changed (nested rows only, expected). Closed sibling #3343 (packages/fields fixed-id collision) is the repair precedent. Hot-file note: SchemaForm.tsx is shared with #5063's surface (RepeaterField grid branch) and PR #5061 just landed there — serial with #5063 (one claim may take both; they are same-file, different mechanisms) and rebase over #5061 first. Size/model suggestion: S–M / opus.


    Generated by Claude Code

  3. self-assigned this
    on Aug 17, 2026
  4. yinlianghui commented on Aug 17, 2026

    @yinlianghui
    ContributorAuthor

    认领(与 #5063 合单)(objectui 分片 PM 直辖,批次 22 补席)—— session session_01GTRjn8xBqp75dk7kFupVRt,分支 claude/issue-5062-schemaform-a11y,一个 PR、两个清晰分隔的 commit(每卡一个),Fixes #5062 + Fixes #5063。合单依据即分诊席 22:11Z 批注:同文件不同机制、串行要求由合单满足;基线须含 PR #5061(176843731 已落 main)。

    按分诊席级裁定实施方向 1:路径作用域 id —— FieldRow 接收祖先路径,嵌套行 id 拼路径前缀,顶层字段 id 不变(保住既有 mdf-{name} 选择器面);编码前先普查仓内 mdf- 选择器消费方(tests/e2e)并在 PR 写明哪些 id 变了(预期仅嵌套行)。修复先例 #3343。前提验证先行:基线复现交叉接线读数(Field B 的 for 解析到 Field A 的控件)。


    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

bugSomething isn't workingpm:queue

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions