Skip to content

Commit 249942c

Browse files
committed
fix: honor rowExpandable for tree data
1 parent 7977f2b commit 249942c

10 files changed

Lines changed: 141 additions & 9 deletions

File tree

src/Body/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const Body = <RecordType,>(props: BodyProps<RecordType>) => {
3131
getRowKey,
3232
expandedKeys,
3333
childrenColumnName,
34+
rowExpandable,
3435
emptyNode,
3536
classNames,
3637
styles,
@@ -44,6 +45,7 @@ const Body = <RecordType,>(props: BodyProps<RecordType>) => {
4445
'getRowKey',
4546
'expandedKeys',
4647
'childrenColumnName',
48+
'rowExpandable',
4749
'emptyNode',
4850
'classNames',
4951
'styles',
@@ -59,6 +61,7 @@ const Body = <RecordType,>(props: BodyProps<RecordType>) => {
5961
childrenColumnName,
6062
expandedKeys,
6163
getRowKey,
64+
rowExpandable,
6265
);
6366

6467
const rowKeys = React.useMemo(() => flattenData.map(item => item.rowKey), [flattenData]);

src/VirtualTable/BodyGrid.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
3939
expandedKeys,
4040
prefixCls,
4141
childrenColumnName,
42+
rowExpandable,
4243
scrollX,
4344
direction,
4445
} = useContext(TableContext, [
@@ -48,6 +49,7 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
4849
'prefixCls',
4950
'expandedKeys',
5051
'childrenColumnName',
52+
'rowExpandable',
5153
'scrollX',
5254
'direction',
5355
]);
@@ -64,7 +66,13 @@ const Grid = React.forwardRef<GridRef, GridProps>((props, ref) => {
6466
const listRef = React.useRef<ListRef>(null);
6567

6668
// =========================== Data ===========================
67-
const flattenData = useFlattenRecords(data, childrenColumnName, expandedKeys, getRowKey);
69+
const flattenData = useFlattenRecords(
70+
data,
71+
childrenColumnName,
72+
expandedKeys,
73+
getRowKey,
74+
rowExpandable,
75+
);
6876

6977
// ========================== Column ==========================
7078
const columnsWidth = React.useMemo<[key: React.Key, width: number, total: number][]>(() => {

src/hooks/useExpand.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ export default function useExpand<RecordType>(
9090
return defaultExpandedRowKeys;
9191
}
9292
if (defaultExpandAllRows) {
93-
return findAllChildrenKeys<RecordType>(mergedData, getRowKey, mergedChildrenColumnName);
93+
return findAllChildrenKeys<RecordType>(
94+
mergedData,
95+
getRowKey,
96+
mergedChildrenColumnName,
97+
rowExpandable,
98+
);
9499
}
95100
return [];
96101
});

src/hooks/useFlattenRecords.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ function fillRecords<T>(
1010
expandedKeys: Set<Key>,
1111
getRowKey: GetRowKey<T>,
1212
index: number,
13+
rowExpandable?: (record: T) => boolean,
1314
) {
1415
const key = getRowKey(record, index);
1516

@@ -22,7 +23,12 @@ function fillRecords<T>(
2223

2324
const expanded = expandedKeys?.has(key);
2425

25-
if (record && Array.isArray(record[childrenColumnName]) && expanded) {
26+
if (
27+
record &&
28+
(!rowExpandable || rowExpandable(record)) &&
29+
Array.isArray(record[childrenColumnName]) &&
30+
expanded
31+
) {
2632
// expanded state, flat record
2733
for (let i = 0; i < record[childrenColumnName].length; i += 1) {
2834
fillRecords(
@@ -33,6 +39,7 @@ function fillRecords<T>(
3339
expandedKeys,
3440
getRowKey,
3541
i,
42+
rowExpandable,
3643
);
3744
}
3845
}
@@ -61,6 +68,7 @@ export default function useFlattenRecords<T>(
6168
childrenColumnName: string,
6269
expandedKeys: Set<Key>,
6370
getRowKey: GetRowKey<T>,
71+
rowExpandable?: (record: T) => boolean,
6472
): FlattenData<T>[] {
6573
const arr = React.useMemo<FlattenData<T>[]>(() => {
6674
if (expandedKeys?.size) {
@@ -71,7 +79,7 @@ export default function useFlattenRecords<T>(
7179
const record = data[i];
7280

7381
// using array.push or spread operator may cause "Maximum call stack size exceeded" exception if array size is big enough.
74-
fillRecords(list, record, 0, childrenColumnName, expandedKeys, getRowKey, i);
82+
fillRecords(list, record, 0, childrenColumnName, expandedKeys, getRowKey, i, rowExpandable);
7583
}
7684

7785
return list;
@@ -85,7 +93,7 @@ export default function useFlattenRecords<T>(
8593
rowKey: getRowKey(item, index),
8694
};
8795
});
88-
}, [data, childrenColumnName, expandedKeys, getRowKey]);
96+
}, [data, childrenColumnName, expandedKeys, getRowKey, rowExpandable]);
8997

9098
return arr;
9199
}

src/hooks/useRowInfo.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,14 @@ export default function useRowInfo<RecordType>(
7575
const nestExpandable = expandableType === 'nest';
7676

7777
const rowSupportExpand = expandableType === 'row' && (!rowExpandable || rowExpandable(record));
78-
const mergedExpandable = rowSupportExpand || nestExpandable;
78+
const rowSupportNestExpand = nestExpandable && (!rowExpandable || rowExpandable(record));
7979

8080
const expanded = expandedKeys && expandedKeys.has(rowKey);
8181

82-
const hasNestChildren = childrenColumnName && record && record[childrenColumnName];
82+
const nestChildren = childrenColumnName && record && record[childrenColumnName];
83+
const hasNestChildren =
84+
rowSupportNestExpand && Array.isArray(nestChildren) && nestChildren.length > 0;
85+
const mergedExpandable = rowSupportExpand || hasNestChildren;
8386

8487
const onInternalTriggerExpand = useEvent(onTriggerExpand);
8588

src/utils/expandUtil.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,16 @@ export function findAllChildrenKeys<RecordType>(
9292
data: readonly RecordType[],
9393
getRowKey: GetRowKey<RecordType>,
9494
childrenColumnName: string,
95+
rowExpandable?: (record: RecordType) => boolean,
9596
): Key[] {
9697
const keys: Key[] = [];
9798

9899
function dig(list: readonly RecordType[]) {
99100
(list || []).forEach((item, index) => {
101+
if (rowExpandable && !rowExpandable(item)) {
102+
return;
103+
}
104+
100105
keys.push(getRowKey(item, index));
101106

102107
dig((item as any)[childrenColumnName]);

tests/ExpandRow.spec.jsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,81 @@ describe('Table.Expand', () => {
162162
expect(container.firstChild).toMatchSnapshot();
163163
});
164164

165+
it('honors rowExpandable for tree data', () => {
166+
const onExpand = vi.fn();
167+
const data = [
168+
{
169+
key: 'allowed',
170+
name: 'Allowed parent',
171+
children: [{ key: 'allowed-child', name: 'Allowed child' }],
172+
},
173+
{
174+
key: 'blocked',
175+
name: 'Blocked parent',
176+
children: [{ key: 'blocked-child', name: 'Blocked child' }],
177+
},
178+
{ key: 'empty', name: 'Empty parent', children: [] },
179+
];
180+
const { container } = render(
181+
createTable({
182+
data,
183+
expandable: {
184+
expandedRowKeys: ['allowed', 'blocked', 'empty'],
185+
expandRowByClick: true,
186+
onExpand,
187+
rowExpandable: record => record.key === 'allowed',
188+
},
189+
}),
190+
);
191+
192+
expect(container.querySelector('[data-row-key="allowed-child"]')).toBeTruthy();
193+
expect(container.querySelector('[data-row-key="blocked-child"]')).toBeFalsy();
194+
195+
const allowedIcon = container.querySelector(
196+
'[data-row-key="allowed"] .rc-table-row-expand-icon',
197+
);
198+
const blockedIcon = container.querySelector(
199+
'[data-row-key="blocked"] .rc-table-row-expand-icon',
200+
);
201+
const emptyIcon = container.querySelector('[data-row-key="empty"] .rc-table-row-expand-icon');
202+
expect(allowedIcon).toHaveClass('rc-table-row-expanded');
203+
expect(blockedIcon).toHaveClass('rc-table-row-spaced');
204+
expect(emptyIcon).toHaveClass('rc-table-row-spaced');
205+
206+
fireEvent.click(container.querySelector('[data-row-key="blocked"]'));
207+
expect(onExpand).not.toHaveBeenCalled();
208+
});
209+
210+
it('honors rowExpandable when expanding all tree rows by default', () => {
211+
const data = [
212+
{
213+
key: 'allowed',
214+
name: 'Allowed parent',
215+
children: [{ key: 'allowed-child', name: 'Allowed child' }],
216+
},
217+
{
218+
key: 'blocked',
219+
name: 'Blocked parent',
220+
children: [{ key: 'blocked-child', name: 'Blocked child' }],
221+
},
222+
];
223+
const { container } = render(
224+
createTable({
225+
data,
226+
expandable: {
227+
defaultExpandAllRows: true,
228+
rowExpandable: record => record.key === 'allowed',
229+
},
230+
}),
231+
);
232+
233+
expect(container.querySelector('[data-row-key="allowed-child"]')).toBeTruthy();
234+
expect(container.querySelector('[data-row-key="blocked-child"]')).toBeFalsy();
235+
expect(
236+
container.querySelector('[data-row-key="blocked"] .rc-table-row-expand-icon'),
237+
).toHaveClass('rc-table-row-spaced');
238+
});
239+
165240
it('not use nest when children is invalidate', () => {
166241
const data = [
167242
{ key: 2, name: 'Jack', age: 28, children: null },

tests/Table.spec.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ describe('Table.Basic', () => {
10131013
{
10141014
key: 'parent',
10151015
children: [
1016-
{ key: 'light', children: [] },
1016+
{ key: 'light', children: [{ key: 'spark' }] },
10171017
{ key: 'bamboo', children: [{ key: 'little' }] },
10181018
],
10191019
},

tests/Virtual.spec.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ describe('Table.Virtual', () => {
163163
});
164164

165165
describe('expandable', () => {
166+
it('honors rowExpandable for tree data', () => {
167+
const { container } = getTable({
168+
data: [
169+
{
170+
name: 'allowed',
171+
children: [{ name: 'allowed-child' }],
172+
},
173+
{
174+
name: 'blocked',
175+
children: [{ name: 'blocked-child' }],
176+
},
177+
],
178+
expandable: {
179+
expandedRowKeys: ['allowed', 'blocked'],
180+
rowExpandable: record => record.name === 'allowed',
181+
},
182+
});
183+
184+
expect(container.querySelector('[data-row-key="allowed-child"]')).toBeTruthy();
185+
expect(container.querySelector('[data-row-key="blocked-child"]')).toBeFalsy();
186+
expect(
187+
container.querySelector('[data-row-key="blocked"] .rc-table-row-expand-icon'),
188+
).toHaveClass('rc-table-row-spaced');
189+
});
190+
166191
it('basic', () => {
167192
(['bamboo', () => 'bamboo'] as const).forEach(cls => {
168193
const { container } = getTable({

tests/__snapshots__/ExpandRow.spec.jsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ exports[`Table.Expand > renders tree row correctly with different children 1`] =
907907
class="rc-table-row-indent indent-level-0"
908908
/>
909909
<span
910-
class="rc-table-row-expand-icon rc-table-row-collapsed"
910+
class="rc-table-row-expand-icon rc-table-row-spaced"
911911
/>
912912
Jack
913913
</td>

0 commit comments

Comments
 (0)