Skip to content

Conversation

cactuser-Lu
Copy link
Contributor

@cactuser-Lu cactuser-Lu commented Oct 9, 2025

在使用 rc-table 时,当列标题包含 id、data-testid 等唯一标识符时,由于 MeasureRow 会复制列标题内容,会导致 DOM 中出现重复的标识符,这违反了 HTML 规范并可能影响测试和可访问性。
fix: ant-design/ant-design#55244

Summary by CodeRabbit

  • Bug Fixes
    • 自动清理度量行 DOM 中的重复 ID 与多余 data-* 属性,避免测试与组件间冲突导致的问题。
    • 优化列变更时的布局更新时机,减少闪烁与错位现象。
    • 提升组件稳定性与可访问性,改善边缘场景下的交互体验。

Copy link

vercel bot commented Oct 9, 2025

Someone is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

coderabbitai bot commented Oct 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

新增并导出清理工具 cleanMeasureRowAttributes,MeasureCell/MeasureRow 在布局阶段对测量用 DOM 节点执行属性清理以移除重复的 id 与若干 data-* 属性;另有少量格式调整,无导出 API 破坏性变更。

Changes

Cohort / File(s) Summary
测量工具新增
src/utils/measureUtil.ts
新增导出函数 cleanMeasureRowAttributes(element: HTMLElement): void,定义待移除属性列表(包含 id 与若干 data-*),遍历元素及所有后代并移除这些属性,对空参数做保护。
MeasureCell 布局期清理
src/Body/MeasureCell.tsx
引入并使用 cleanMeasureRowAttributes;新增 contentRef 并绑定到内部 div;在 useLayoutEffect 中(依赖 [title, columnKey, onColumnResize])调用清理函数以在布局阶段移除重复属性。
MeasureRow 轻微格式调整
src/Body/MeasureRow.tsx
代码格式微调(额外空行),无行为变更;测量清理逻辑由 MeasureCell 触发(无新增导出)。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Render as Renderer
  participant MC as MeasureCell
  participant DOM as Measure Row DOM
  participant MU as measureUtil.cleanMeasureRowAttributes

  Render->>MC: 渲染或更新(props: title, columnKey, onColumnResize)
  MC->>MC: useLayoutEffect 触发(布局阶段)
  MC->>DOM: 获取 contentRef.current
  alt contentRef 存在
    MC->>MU: cleanMeasureRowAttributes(contentRef.current)
    MU->>DOM: 遍历子孙节点,移除 `id` 与指定 `data-*` 属性
    MU-->>MC: 返回
  else contentRef 为空
    MC-->>MC: 跳过清理
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

我是小兔轻轻跑,
布局一到把尘扫,
id 与 data* 全都没,
检测不再重叠扰,
(\_/)
(•ᴥ•)🍂

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning PR 在 MeasureRow.tsx 中加入了一条仅用于格式化的空白行,与清理属性的目标无关,属于范围外的更改。 请移除 MeasureRow.tsx 中的无关格式化更改,以保持 PR 聚焦于防止重复标识符的修改。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 标题清晰地指出主要变更,即修复 MeasureRow 列标题中重复唯一标识符的问题,简明概括了 PR 的核心内容。
Linked Issues Check ✅ Passed 变更新增了实用函数来黑名单并移除 id 和 data-* 属性,并将其集成到 MeasureCell 的 layout effect 中,从而剥离复制标题中的重复 testid 等标识符,直接解决了 #55244 中报告的重复问题。
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Summary of Changes

Hello @cactuser-Lu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在解决 rc-table 组件中 MeasureRow 导致 DOM 元素出现重复唯一标识符的问题。通过引入一个专门的工具函数来清理这些属性,并在 MeasureRow 渲染后立即执行清理操作,确保了生成的 HTML 符合规范,从而提升了组件的健壮性和可访问性,并避免了潜在的测试问题。

Highlights

  • 修复重复的唯一标识符: 解决了在使用 rc-table 时,MeasureRow 复制列标题内容导致 DOM 中出现重复 iddata-testid 等唯一标识符的问题,这违反了 HTML 规范并可能影响测试和可访问性。
  • 引入属性清理工具函数: 新增了 src/utils/measureUtil.ts 文件,其中包含 cleanMeasureRowAttributes 函数,用于递归地从 DOM 元素及其子元素中移除预定义的唯一标识符属性(如 iddata-testiddata-cy 等)。
  • 集成 DOM 清理逻辑: 在 src/Body/MeasureRow.tsx 中,通过 React.useLayoutEffect 钩子在 DOM 更新后同步调用 cleanMeasureRowAttributes 函数,确保 MeasureRow 渲染的 DOM 元素在浏览器绘制前被清理,避免了重复属性的问题。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 旨在解决 MeasureRow 中因复制列标题而导致的唯一标识符(如 id)重复的问题,从而遵循 HTML 规范并改善测试兼容性。您通过引入一个新的工具函数 cleanMeasureRowAttributes 并在 MeasureRow 组件的 useLayoutEffect 中调用它来移除这些属性,这个方法是有效的。代码实现很清晰,我只有一个关于如何让工具函数更健壮的建议。

Comment on lines +25 to +30
const allElements = element.querySelectorAll('*');
allElements.forEach(el => {
FILTERED_ATTRIBUTES.forEach(attr => {
el.removeAttribute(attr);
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

当前使用 element.querySelectorAll('*') 的实现只选择了后代元素,而没有包含 element 本身。如果传递给此函数的根元素自身也包含需要过滤的属性,这些属性将不会被移除。为了使此函数更加健壮,它也应该清理根元素上的属性。

  [element, ...element.querySelectorAll('*')].forEach(el => {
    FILTERED_ATTRIBUTES.forEach(attr => {
      el.removeAttribute(attr);
    });
  });

Copy link

codecov bot commented Oct 9, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.12%. Comparing base (057acc8) to head (afc8cff).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1378      +/-   ##
==========================================
+ Coverage   96.09%   96.12%   +0.02%     
==========================================
  Files          57       58       +1     
  Lines        3434     3460      +26     
  Branches      632      636       +4     
==========================================
+ Hits         3300     3326      +26     
  Misses        129      129              
  Partials        5        5              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd67c7 and 1fdabd9.

📒 Files selected for processing (2)
  • src/Body/MeasureRow.tsx (3 hunks)
  • src/utils/measureUtil.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test / react component workflow
🔇 Additional comments (4)
src/utils/measureUtil.ts (1)

4-14: 属性列表很全面。

过滤的属性列表覆盖了常见的唯一标识符和测试框架使用的属性,能够有效防止 ID 重复问题。

src/Body/MeasureRow.tsx (3)

8-8: 导入语句正确。

从新的工具文件中正确导入了 cleanMeasureRowAttributes 函数。


50-50: 格式调整合理。

添加空行提高了代码可读性,将 return 语句与前面的逻辑分隔开。


27-31: 无需移除 columns 依赖项. MeasureRow 的 columns 来源于 useColumnsuseWidthColumnsuseMemo,引用仅在实际列配置或宽度变更时更新,不会导致不必要的清理操作。

Likely an incorrect or invalid review comment.

Comment on lines +22 to +31
export function cleanMeasureRowAttributes(element: HTMLElement): void {
if (!element) return;

const allElements = element.querySelectorAll('*');
allElements.forEach(el => {
FILTERED_ATTRIBUTES.forEach(attr => {
el.removeAttribute(attr);
});
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

函数未清理根元素本身的属性。

querySelectorAll('*') 只选择后代元素,不包括传入的根元素本身。如果 element 自身包含需要过滤的属性(如 id),这些属性不会被移除。

应用此修复:

 export function cleanMeasureRowAttributes(element: HTMLElement): void {
   if (!element) return;
 
+  // Remove attributes from the element itself
+  FILTERED_ATTRIBUTES.forEach(attr => {
+    element.removeAttribute(attr);
+  });
+
+  // Remove attributes from all descendants
   const allElements = element.querySelectorAll('*');
   allElements.forEach(el => {
     FILTERED_ATTRIBUTES.forEach(attr => {
       el.removeAttribute(attr);
     });
   });
 }
🤖 Prompt for AI Agents
In src/utils/measureUtil.ts around lines 22 to 31, the cleaning loop only
iterates over element.querySelectorAll('*') and thus skips the root element
itself; update the function to also remove FILTERED_ATTRIBUTES from the root
element (either by processing element before/after the querySelectorAll loop or
by creating a NodeList/array that includes element plus its descendants) so that
any filtered attributes on the provided root are removed as well.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fdabd9 and afc8cff.

📒 Files selected for processing (2)
  • src/Body/MeasureCell.tsx (2 hunks)
  • src/Body/MeasureRow.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/Body/MeasureCell.tsx (1)
src/utils/measureUtil.ts (1)
  • cleanMeasureRowAttributes (22-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test / react component workflow
🔇 Additional comments (3)
src/Body/MeasureRow.tsx (1)

43-43: 格式改进。

添加空行提升了代码可读性。

src/Body/MeasureCell.tsx (2)

16-16: LGTM!

正确地为内容容器创建了 ref 引用。


34-36: LGTM!

正确地将 ref 应用于内容容器,以便在 layout effect 中进行属性清理。

Comment on lines +22 to +26

if (contentRef.current) {
cleanMeasureRowAttributes(contentRef.current);
}
}, [title, columnKey, onColumnResize]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

清理逻辑不完整,且存在性能隐患。

存在以下问题:

  1. 清理逻辑不完整cleanMeasureRowAttributes 的实现(位于 src/utils/measureUtil.ts)仅使用 querySelectorAll('*') 清理后代元素,但不会清理 contentRef 元素本身的属性。如果被克隆的 title 在根元素上包含 iddata-testid,这些属性将不会被移除,导致重复 ID 问题依然存在。

  2. 依赖项可能导致性能问题onColumnResize 函数包含在依赖数组中。如果父组件未对该函数进行记忆化处理(使用 useCallback),每次渲染都会创建新的函数引用,导致 effect 频繁执行,产生不必要的 DOM 遍历开销。

建议修复方案:

  1. src/utils/measureUtil.ts 中修改 cleanMeasureRowAttributes 以清理元素本身:
export function cleanMeasureRowAttributes(element: HTMLElement): void {
  if (!element) return;

  // 清理元素本身的属性
  FILTERED_ATTRIBUTES.forEach(attr => {
    element.removeAttribute(attr);
  });

  // 清理所有后代元素
  const allElements = element.querySelectorAll('*');
  allElements.forEach(el => {
    FILTERED_ATTRIBUTES.forEach(attr => {
      el.removeAttribute(attr);
    });
  });
}
  1. 建议在 MeasureRow.tsx 中使用 useCallback 包装传递给 MeasureCellonColumnResize,或者从依赖数组中移除该函数(如果它的稳定性可以保证)。
🤖 Prompt for AI Agents
In src/Body/MeasureCell.tsx around lines 22 to 26, the cleanup logic and effect
dependencies are problematic: cleanMeasureRowAttributes currently only removes
attributes from descendants (via querySelectorAll) and misses attributes on the
contentRef element itself, which can leave duplicate id/data-testid, and
including onColumnResize in the effect deps can cause unnecessary re-runs if
that callback isn't memoized. Update src/utils/measureUtil.ts so
cleanMeasureRowAttributes also removes FILTERED_ATTRIBUTES from the passed
element itself before iterating descendants; and ensure onColumnResize is stable
by wrapping it with useCallback in the parent (MeasureRow) before passing it
down or, if its stability is guaranteed, remove it from the effect dependency
array to avoid repeated DOM traversals.

@iRyusa
Copy link

iRyusa commented Oct 9, 2025

Thank you so much for the quick patch 🙌


if (contentRef.current) {
cleanMeasureRowAttributes(contentRef.current);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果里面有异步逻辑,比如依赖里面的 id 或者其他属性的做 dom 操作,这样移除后会可能会引发报错。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果里面有异步逻辑,比如依赖里面的 id 或者其他属性的做 dom 操作,这样移除后会可能会引发报错。

如果真的依赖id做操作,操作显示的那个dom才是正常的吧;这个dom只是用于测量宽度,实际不可见,反而不去除这些属性就可能有两个id一样的dom

@yoyo837
Copy link
Member

yoyo837 commented Oct 10, 2025

复制过去后全部重命名可以吗?

@Linkodt
Copy link
Contributor

Linkodt commented Oct 10, 2025

复制过去后全部重命名可以吗?

可以试试。
之前有一个方案是:获取当前列宽度再赋值给 measureCell,但是这个碰到合并表头的话不知道怎么处理了/
#1368

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MeasureRow is duplicating testid in Table

5 participants