Skip to content

Commit 719f1af

Browse files
committed
feat: add measureRowRender prop
1 parent 8c37e7f commit 719f1af

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

docs/examples/measureRowRender.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from 'react';
2+
import { Table } from 'rc-table';
3+
import { ConfigProvider } from 'antd';
4+
5+
// 示例:使用 measureRowRender 包裹 ConfigProvider 来隐藏 MeasureRow 中的弹层
6+
const MeasureRowRenderExample = () => {
7+
const columns = [
8+
{
9+
title: (
10+
<div>
11+
Name
12+
<ConfigProvider
13+
getPopupContainer={node => node} // 将弹层渲染到 MeasureRow 内部
14+
>
15+
<div>Filter Dropdown</div>
16+
</ConfigProvider>
17+
</div>
18+
),
19+
dataIndex: 'name',
20+
key: 'name',
21+
width: 100,
22+
},
23+
{
24+
title: 'Age',
25+
dataIndex: 'age',
26+
key: 'age',
27+
width: 80,
28+
},
29+
];
30+
31+
const data = [
32+
{ key: 1, name: 'John', age: 25 },
33+
{ key: 2, name: 'Jane', age: 30 },
34+
];
35+
36+
// 自定义 MeasureRow 渲染,包裹 ConfigProvider 来隐藏弹层
37+
const measureRowRender = measureRow => (
38+
<ConfigProvider
39+
getPopupContainer={() => {
40+
// 创建一个隐藏的容器来承载弹层
41+
const hiddenContainer = document.createElement('div');
42+
hiddenContainer.style.display = 'none';
43+
hiddenContainer.style.position = 'absolute';
44+
hiddenContainer.style.left = '-9999px';
45+
hiddenContainer.style.top = '-9999px';
46+
document.body.appendChild(hiddenContainer);
47+
return hiddenContainer;
48+
}}
49+
>
50+
{measureRow}
51+
</ConfigProvider>
52+
);
53+
54+
return (
55+
<Table
56+
columns={columns}
57+
data={data}
58+
sticky
59+
scroll={{ x: true }}
60+
measureRowRender={measureRowRender}
61+
/>
62+
);
63+
};
64+
65+
export default MeasureRowRenderExample;

src/Body/MeasureRow.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import * as React from 'react';
22
import ResizeObserver from 'rc-resize-observer';
33
import MeasureCell from './MeasureCell';
44
import isVisible from 'rc-util/lib/Dom/isVisible';
5+
import { useContext } from '@rc-component/context';
6+
import TableContext from '../context/TableContext';
57
import type { ColumnType } from '../interface';
68

79
export interface MeasureRowProps {
@@ -18,9 +20,10 @@ export default function MeasureRow({
1820
columns,
1921
}: MeasureRowProps) {
2022
const ref = React.useRef<HTMLTableRowElement>(null);
23+
const { measureRowRender } = useContext(TableContext, ['measureRowRender']);
2124

22-
return (
23-
<tr aria-hidden="true" className={`${prefixCls}-measure-row`} style={{ height: 0 }} ref={ref}>
25+
const measureRow = (
26+
<tr className={`${prefixCls}-measure-row`} style={{ height: 0 }} ref={ref} tabIndex={-1}>
2427
<ResizeObserver.Collection
2528
onBatchResize={infoList => {
2629
if (isVisible(ref.current)) {
@@ -44,4 +47,6 @@ export default function MeasureRow({
4447
</ResizeObserver.Collection>
4548
</tr>
4649
);
50+
51+
return measureRowRender ? measureRowRender(measureRow) : measureRow;
4752
}

src/Table.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ export interface TableProps<RecordType = any>
168168
internalRefs?: {
169169
body: React.MutableRefObject<HTMLDivElement>;
170170
};
171+
/**
172+
* @private Internal usage, may remove by refactor.
173+
*
174+
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
175+
*/
176+
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
171177
}
172178

173179
function defaultEmpty() {
@@ -210,6 +216,9 @@ function Table<RecordType extends DefaultRecordType>(
210216
onRow,
211217
onHeaderRow,
212218

219+
// Measure Row
220+
measureRowRender,
221+
213222
// Events
214223
onScroll,
215224

@@ -850,6 +859,9 @@ function Table<RecordType extends DefaultRecordType>(
850859
childrenColumnName: mergedChildrenColumnName,
851860

852861
rowHoverable,
862+
863+
// Measure Row
864+
measureRowRender,
853865
}),
854866
[
855867
// Scroll
@@ -901,6 +913,8 @@ function Table<RecordType extends DefaultRecordType>(
901913
mergedChildrenColumnName,
902914

903915
rowHoverable,
916+
917+
measureRowRender,
904918
],
905919
);
906920

src/context/TableContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ export interface TableContextProps<RecordType = any> {
7272
rowHoverable?: boolean;
7373

7474
expandedRowOffset: ExpandableConfig<RecordType>['expandedRowOffset'];
75+
76+
// Measure Row
77+
measureRowRender?: (measureRow: React.ReactNode) => React.ReactNode;
7578
}
7679

7780
const TableContext = createContext<TableContextProps>();

tests/FixedHeader.spec.jsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,62 @@ describe('Table.FixedHeader', () => {
265265
'rc-table-cell-fix-left-last',
266266
);
267267
});
268+
269+
it('should support measureRowRender to wrap MeasureRow with custom provider', async () => {
270+
const FilterDropdown = ({ visible, onVisibleChange }) => (
271+
<div className="test-filter-dropdown" style={{ display: visible ? 'block' : 'none' }}>
272+
Filter Content
273+
<button onClick={() => onVisibleChange && onVisibleChange(!visible)}>Toggle</button>
274+
</div>
275+
);
276+
277+
const columns = [
278+
{
279+
title: (
280+
<div>
281+
Name
282+
<FilterDropdown visible={true} onVisibleChange={() => {}} />
283+
</div>
284+
),
285+
dataIndex: 'name',
286+
key: 'name',
287+
width: 100,
288+
},
289+
];
290+
291+
const data = [
292+
{
293+
key: 1,
294+
name: 'Jack',
295+
},
296+
];
297+
298+
// Mock ConfigProvider-like wrapper
299+
const measureRowRender = measureRow => (
300+
<div data-testid="measure-row-wrapper" style={{ display: 'none' }}>
301+
{measureRow}
302+
</div>
303+
);
304+
305+
const wrapper = mount(
306+
<Table
307+
columns={columns}
308+
data={data}
309+
sticky
310+
scroll={{ x: true }}
311+
measureRowRender={measureRowRender}
312+
/>,
313+
);
314+
315+
await safeAct(wrapper);
316+
317+
// Check that measureRowRender wrapper is applied
318+
const measureRowWrapper = wrapper.find('[data-testid="measure-row-wrapper"]');
319+
expect(measureRowWrapper).toHaveLength(1);
320+
expect(measureRowWrapper.prop('style').display).toBe('none');
321+
322+
// Check that MeasureRow is inside the wrapper
323+
const measureRowInWrapper = measureRowWrapper.find('.rc-table-measure-row');
324+
expect(measureRowInWrapper).toHaveLength(1);
325+
});
268326
});

0 commit comments

Comments
 (0)