Skip to content

Commit 087a41e

Browse files
cactuser-Luafc163
authored andcommitted
fix: duplicate unique identifiers in MeasureRow column headers (#1376)
Co-authored-by: 路振凯 <l>
1 parent 50dc3a8 commit 087a41e

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed

src/Body/MeasureRow.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import isVisible from 'rc-util/lib/Dom/isVisible';
55
import { useContext } from '@rc-component/context';
66
import TableContext from '../context/TableContext';
77
import type { ColumnType } from '../interface';
8+
import { prepareMeasureTitle } from '../utils/measureUtil';
89

910
export interface MeasureRowProps {
1011
prefixCls: string;
@@ -36,9 +37,8 @@ export default function MeasureRow({
3637
{columnsKey.map(columnKey => {
3738
const column = columns.find(col => col.key === columnKey);
3839
const rawTitle = column?.title;
39-
const titleForMeasure = React.isValidElement<React.RefAttributes<any>>(rawTitle)
40-
? React.cloneElement(rawTitle, { ref: null })
41-
: rawTitle;
40+
const titleForMeasure = prepareMeasureTitle(rawTitle);
41+
4242
return (
4343
<MeasureCell
4444
prefixCls={prefixCls}

src/utils/measureUtil.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as React from 'react';
2+
3+
/**
4+
* Props that should be filtered out from measure row elements to avoid DOM conflicts
5+
*/
6+
const FILTERED_PROPS = [
7+
// Unique identifiers that shouldn't be duplicated in DOM
8+
'id',
9+
'data-testid',
10+
'data-test-id',
11+
'data-cy', // Cypress
12+
'data-qa',
13+
'data-automation-id',
14+
'data-id',
15+
'data-key',
16+
17+
// Event handlers that are unnecessary in hidden measure elements
18+
'onClick',
19+
'onMouseEnter',
20+
'onMouseLeave',
21+
'onFocus',
22+
'onBlur',
23+
'onKeyDown',
24+
'onKeyPress',
25+
'onKeyUp',
26+
'onDoubleClick',
27+
'onContextMenu',
28+
29+
// Accessibility props that might reference other elements and cause conflicts
30+
'aria-describedby',
31+
'aria-labelledby',
32+
'aria-controls',
33+
'aria-owns',
34+
35+
// Form-related props that might conflict
36+
'name',
37+
'htmlFor',
38+
'for',
39+
] as const;
40+
41+
/**
42+
* Filter props from a React element that might cause DOM conflicts in measure row
43+
* @param element - The React element to filter
44+
* @param deep - Whether to recursively filter nested elements
45+
* @returns A new React element with filtered props
46+
*/
47+
export function filterMeasureProps<T = any>(
48+
element: React.ReactElement<T>,
49+
deep: boolean = true,
50+
): React.ReactElement<T> {
51+
if (!React.isValidElement(element)) {
52+
return element;
53+
}
54+
55+
const filteredProps = { ...element.props } as any;
56+
57+
FILTERED_PROPS.forEach(prop => {
58+
filteredProps[prop] = undefined;
59+
});
60+
61+
// Nullify ref to avoid warnings and conflicts
62+
filteredProps.ref = null;
63+
64+
// Recursively filter children if deep filtering is enabled
65+
if (deep && filteredProps.children) {
66+
filteredProps.children = filterMeasureChildren(filteredProps.children);
67+
}
68+
69+
return React.cloneElement(element, filteredProps);
70+
}
71+
72+
/**
73+
* Recursively filter children elements
74+
* @param children - React children to filter
75+
* @returns Filtered children
76+
*/
77+
function filterMeasureChildren(children: React.ReactNode): React.ReactNode {
78+
return React.Children.map(children, child => {
79+
if (React.isValidElement(child)) {
80+
return filterMeasureProps(child, true);
81+
}
82+
return child;
83+
});
84+
}
85+
86+
/**
87+
* Prepare title content for use in measure row
88+
* @param title - The original title content
89+
* @returns Filtered title safe for measure row usage
90+
*/
91+
export function prepareMeasureTitle(title: React.ReactNode): React.ReactNode {
92+
if (React.isValidElement(title)) {
93+
return filterMeasureProps(title, true); // Enable deep filtering
94+
}
95+
return title;
96+
}

0 commit comments

Comments
 (0)