Skip to content

Commit dc0f9a0

Browse files
authored
Merge branch 'master' into feat/expand-all-header
2 parents a617eea + 60ab7b7 commit dc0f9a0

5 files changed

Lines changed: 176 additions & 6 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rc-component/table",
3-
"version": "1.11.0",
3+
"version": "1.11.1",
44
"description": "📋 Data table component for React",
55
"engines": {
66
"node": ">=8.x"

src/Body/BodyRow.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export function getCellProps<RecordType>(
8484
);
8585
}
8686

87-
const additionalCellProps = column.onCell?.(record, index) || {};
87+
const additionalCellProps = { ...column.onCell?.(record, index) };
88+
let originRowSpan: number | undefined;
8889

8990
// Expandable row has offset
9091
if (expandedRowOffset) {
@@ -93,6 +94,7 @@ export function getCellProps<RecordType>(
9394
// For expandable row with rowSpan,
9495
// We should increase the rowSpan if the row is expanded
9596
if (expandable && rowSpan && colIndex < expandedRowOffset) {
97+
originRowSpan = rowSpan;
9698
let currentRowSpan = rowSpan;
9799

98100
for (let i = index; i < index + rowSpan; i += 1) {
@@ -110,6 +112,7 @@ export function getCellProps<RecordType>(
110112
fixedInfo,
111113
appendCellNode,
112114
additionalCellProps: additionalCellProps,
115+
originRowSpan,
113116
};
114117
}
115118

@@ -188,7 +191,7 @@ const BodyRow = <RecordType extends { children?: readonly RecordType[] }>(
188191
{flattenColumns.map((column: ColumnType<RecordType>, colIndex) => {
189192
const { render, dataIndex, className: columnClassName } = column;
190193

191-
const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps(
194+
const { key, fixedInfo, appendCellNode, additionalCellProps, originRowSpan } = getCellProps(
192195
rowInfo,
193196
column,
194197
colIndex,
@@ -217,6 +220,7 @@ const BodyRow = <RecordType extends { children?: readonly RecordType[] }>(
217220
{...fixedInfo}
218221
appendNode={appendCellNode}
219222
additionalProps={additionalCellProps}
223+
originRowSpan={originRowSpan}
220224
/>
221225
);
222226
})}

src/Cell/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export interface CellProps<RecordType extends DefaultRecordType> {
5252
/** @private Used for `expandable` with nest tree */
5353
appendNode?: React.ReactNode;
5454
additionalProps?: React.TdHTMLAttributes<HTMLTableCellElement>;
55+
/** @private Original rowSpan before patching it for expanded rows */
56+
originRowSpan?: number;
5557

5658
rowType?: 'header' | 'body' | 'footer';
5759

@@ -123,6 +125,7 @@ const Cell = <RecordType,>(props: CellProps<RecordType>) => {
123125
// Private
124126
appendNode,
125127
additionalProps = {},
128+
originRowSpan,
126129
isSticky,
127130
} = props;
128131

@@ -183,13 +186,14 @@ const Cell = <RecordType,>(props: CellProps<RecordType>) => {
183186
// ================ RowSpan & ColSpan =================
184187
const mergedColSpan = legacyCellProps?.colSpan ?? additionalProps.colSpan ?? colSpan ?? 1;
185188
const mergedRowSpan = legacyCellProps?.rowSpan ?? additionalProps.rowSpan ?? rowSpan ?? 1;
189+
const mergedHoverRowSpan = legacyCellProps?.rowSpan ?? originRowSpan ?? mergedRowSpan;
186190

187191
// ====================== Hover =======================
188-
const [hovering, onHover] = useHoverState(index, mergedRowSpan);
192+
const [hovering, onHover] = useHoverState(index, mergedHoverRowSpan);
189193

190194
const onMouseEnter: React.MouseEventHandler<HTMLTableCellElement> = useEvent(event => {
191195
if (record) {
192-
onHover(index, index + mergedRowSpan - 1);
196+
onHover(index, index + mergedHoverRowSpan - 1);
193197
}
194198

195199
additionalProps?.onMouseEnter?.(event);

src/VirtualTable/VirtualCell.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const VirtualCell = <RecordType,>(props: VirtualCellProps<RecordType>) => {
5757
const { columnsOffset } = useContext(GridContext, ['columnsOffset']);
5858

5959
// TODO: support `expandableRowOffset`
60-
const { key, fixedInfo, appendCellNode, additionalCellProps } = getCellProps(
60+
const { key, fixedInfo, appendCellNode, additionalCellProps, originRowSpan } = getCellProps(
6161
rowInfo,
6262
column,
6363
colIndex,
@@ -128,6 +128,7 @@ const VirtualCell = <RecordType,>(props: VirtualCellProps<RecordType>) => {
128128
shouldCellUpdate={column.shouldCellUpdate}
129129
{...fixedInfo}
130130
appendNode={appendCellNode}
131+
originRowSpan={originRowSpan}
131132
additionalProps={{
132133
...additionalCellProps,
133134
style: mergedStyle,

tests/Hover.spec.tsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Table from '../src';
55
import type { TableProps } from '../src/Table';
66

77
describe('Table.Hover', () => {
8+
const hoverClassName = 'rc-table-cell-row-hover';
89
const data = [
910
{ key: 'key0', name: 'Lucy' },
1011
{ key: 'key1', name: 'Jack' },
@@ -127,6 +128,166 @@ describe('Table.Hover', () => {
127128
expect(container.querySelector('.rc-table-cell-row-hover')).toBeFalsy();
128129
});
129130

131+
it('does not let expanded row offset rowSpan affect hover range', () => {
132+
const { container } = render(
133+
<Table
134+
rowKey="key"
135+
columns={[
136+
{
137+
dataIndex: 'group',
138+
onCell: (_, index) => {
139+
if (index === 0) {
140+
return { rowSpan: 2 };
141+
}
142+
if (index === 1) {
143+
return { rowSpan: 0 };
144+
}
145+
return {};
146+
},
147+
},
148+
Table.EXPAND_COLUMN,
149+
{
150+
dataIndex: 'name',
151+
},
152+
]}
153+
data={[
154+
{ key: 'a', group: 'Group 1', name: 'Alpha' },
155+
{ key: 'b', group: 'Group 1', name: 'Beta' },
156+
{ key: 'c', group: 'Group 2', name: 'Gamma' },
157+
]}
158+
expandable={{
159+
expandedRowOffset: 1,
160+
expandedRowKeys: ['a'],
161+
expandedRowRender: record => <span>expanded {record.key}</span>,
162+
}}
163+
/>,
164+
);
165+
166+
const getCell = (text: string) => {
167+
const cell = Array.from(container.querySelectorAll<HTMLTableCellElement>('tbody td')).find(
168+
cell => cell.textContent === text,
169+
);
170+
expect(cell).toBeTruthy();
171+
return cell!;
172+
};
173+
174+
const groupCell = getCell('Group 1');
175+
const betaCell = getCell('Beta');
176+
const gammaCell = getCell('Gamma');
177+
178+
expect(groupCell.getAttribute('rowspan')).toBe('3');
179+
180+
fireEvent.mouseEnter(groupCell);
181+
expect(groupCell.classList.contains(hoverClassName)).toBe(true);
182+
expect(betaCell.classList.contains(hoverClassName)).toBe(true);
183+
expect(gammaCell.classList.contains(hoverClassName)).toBe(false);
184+
185+
fireEvent.mouseEnter(gammaCell);
186+
expect(groupCell.classList.contains(hoverClassName)).toBe(false);
187+
expect(gammaCell.classList.contains(hoverClassName)).toBe(true);
188+
});
189+
190+
it('keeps legacy render rowSpan priority for hover range', () => {
191+
const { container } = render(
192+
<Table
193+
rowKey="key"
194+
columns={[
195+
{
196+
dataIndex: 'group',
197+
render: (value, _, index) => ({
198+
children: value,
199+
props: { rowSpan: index === 0 ? 2 : 0 },
200+
}),
201+
},
202+
Table.EXPAND_COLUMN,
203+
{
204+
dataIndex: 'name',
205+
},
206+
]}
207+
data={[
208+
{ key: 'a', group: 'Group 1', name: 'Alpha' },
209+
{ key: 'b', group: 'Group 1', name: 'Beta' },
210+
{ key: 'c', group: 'Group 2', name: 'Gamma' },
211+
]}
212+
expandable={{
213+
expandedRowOffset: 1,
214+
defaultExpandAllRows: true,
215+
expandedRowRender: record => <span>expanded {record.key}</span>,
216+
}}
217+
/>,
218+
);
219+
220+
const getCell = (text: string) => {
221+
const cell = Array.from(container.querySelectorAll<HTMLTableCellElement>('tbody td')).find(
222+
item => item.textContent === text,
223+
);
224+
expect(cell).toBeTruthy();
225+
return cell!;
226+
};
227+
228+
const groupCell = getCell('Group 1');
229+
const alphaCell = getCell('Alpha');
230+
const betaCell = getCell('Beta');
231+
const gammaCell = getCell('Gamma');
232+
233+
expect(groupCell.getAttribute('rowspan')).toBe('2');
234+
235+
fireEvent.mouseEnter(groupCell);
236+
expect(alphaCell.classList.contains(hoverClassName)).toBe(true);
237+
expect(betaCell.classList.contains(hoverClassName)).toBe(true);
238+
expect(gammaCell.classList.contains(hoverClassName)).toBe(false);
239+
});
240+
241+
it('does not mutate stable onCell props across expanded row renders', () => {
242+
const rowSpanProps = [{ rowSpan: 2 }, { rowSpan: 0 }, {}];
243+
const dataSource = [
244+
{ key: 'a', group: 'Group 1', name: 'Alpha' },
245+
{ key: 'b', group: 'Group 1', name: 'Beta' },
246+
{ key: 'c', group: 'Group 2', name: 'Gamma' },
247+
];
248+
249+
const createTableWithExpandedKeys = (expandedRowKeys: React.Key[]) => (
250+
<React.StrictMode>
251+
<Table
252+
rowKey="key"
253+
columns={[
254+
{
255+
dataIndex: 'group',
256+
onCell: (_, index) => rowSpanProps[index],
257+
},
258+
Table.EXPAND_COLUMN,
259+
{
260+
dataIndex: 'name',
261+
},
262+
]}
263+
data={dataSource}
264+
expandable={{
265+
expandedRowOffset: 1,
266+
expandedRowKeys,
267+
expandedRowRender: record => <span>expanded {record.key}</span>,
268+
}}
269+
/>
270+
</React.StrictMode>
271+
);
272+
273+
const { container, rerender } = render(createTableWithExpandedKeys([]));
274+
const getGroupCell = () =>
275+
Array.from(container.querySelectorAll<HTMLTableCellElement>('tbody td')).find(
276+
cell => cell.textContent === 'Group 1',
277+
)!;
278+
279+
expect(rowSpanProps[0].rowSpan).toBe(2);
280+
expect(getGroupCell().getAttribute('rowspan')).toBe('2');
281+
282+
rerender(createTableWithExpandedKeys(['a']));
283+
expect(rowSpanProps[0].rowSpan).toBe(2);
284+
expect(getGroupCell().getAttribute('rowspan')).toBe('3');
285+
286+
rerender(createTableWithExpandedKeys([]));
287+
expect(rowSpanProps[0].rowSpan).toBe(2);
288+
expect(getGroupCell().getAttribute('rowspan')).toBe('2');
289+
});
290+
130291
describe('perf', () => {
131292
it('legacy mode should render every time', () => {
132293
let renderTimes = 0;

0 commit comments

Comments
 (0)