Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Body/MeasureRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import isVisible from '@rc-component/util/lib/Dom/isVisible';
import { useContext } from '@rc-component/context';
import TableContext from '../context/TableContext';
import type { ColumnType } from '../interface';
import { cleanMeasureRowAttributes } from '../utils/measureUtil';

export interface MeasureRowProps {
prefixCls: string;
Expand All @@ -23,6 +24,12 @@ const MeasureRow: React.FC<MeasureRowProps> = ({

const { measureRowRender } = useContext(TableContext, ['measureRowRender']);

React.useLayoutEffect(() => {
if (ref.current) {
cleanMeasureRowAttributes(ref.current);
}
}, [columnsKey, columns]);

const measureRow = (
<tr aria-hidden="true" className={`${prefixCls}-measure-row`} style={{ height: 0 }} ref={ref}>
<ResizeObserver.Collection
Expand All @@ -40,6 +47,7 @@ const MeasureRow: React.FC<MeasureRowProps> = ({
const titleForMeasure = React.isValidElement<React.RefAttributes<any>>(rawTitle)
? React.cloneElement(rawTitle, { ref: null })
: rawTitle;

return (
<MeasureCell
key={columnKey}
Expand Down
31 changes: 31 additions & 0 deletions src/utils/measureUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Attributes that should be removed from measure row DOM to avoid conflicts
*/
const FILTERED_ATTRIBUTES = [
// Unique identifiers that shouldn't be duplicated in DOM
'id',
'data-testid',
'data-test-id',
'data-cy', // Cypress
'data-qa',
'data-automation-id',
'data-id',
'data-key',
] as const;

/**
* Remove all ID and test attributes from DOM element and its descendants
* This ensures the measure row complies with HTML spec (no duplicate IDs)
* and works with custom components whose internal DOM we cannot control at React level
* @param element - The DOM element to clean
*/
export function cleanMeasureRowAttributes(element: HTMLElement): void {
if (!element) return;

const allElements = element.querySelectorAll('*');
allElements.forEach(el => {
FILTERED_ATTRIBUTES.forEach(attr => {
el.removeAttribute(attr);
});
});
Comment on lines +25 to +30
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);
    });
  });

}
Comment on lines +22 to +31
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.