Skip to content

Commit fb94b44

Browse files
authored
feat: support items & add deprecated warning (#312)
* feat: support items & add deprecated warning * test: add case * chore: remove legacy config * test: update unit test * fix: fix expandIcon error * chore: update * fix * fix * fix: support custom children test case: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 * fix: update * Revert "fix: support custom children" This reverts commit fe8951f. # Conflicts: # src/Collapse.tsx * fix: support custom children test case: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 * docs: update docs * feat: header -> label * test: add case * Revert "test: add case" This reverts commit 1143d31. * test: add case * chore: update type * test: add case * chore: items not support expandIcon * fix: avoid accidental coverage * chore: not support custom child * refactor: extraction hook * Revert "refactor: extraction hook" This reverts commit 59a9d5f. * refactor: extraction hook
1 parent aac303a commit fb94b44

File tree

10 files changed

+521
-90
lines changed

10 files changed

+521
-90
lines changed

.umirc.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ ReactDOM.render(App, container);
107107
<th>-</th>
108108
<td>specify whether the panel of children is collapsible or the area of collapsible.</td>
109109
</tr>
110+
<tr>
111+
<td>items</td>
112+
<td>
113+
<a href="./src/interface.ts#ItemType">interface.ts#ItemType</a>
114+
</td>
115+
<th>-</th>
116+
<td>collapse items content</td>
117+
</tr>
110118
</tbody>
111119
</table>
112120

@@ -116,6 +124,8 @@ If `accordion` is true, only one panel can be open. Opening another panel will c
116124

117125
### Collapse.Panel props
118126

127+
> **deprecated** use `items` instead, will be removed in `v4.0.0`
128+
119129
<table class="table table-bordered table-striped">
120130
<thead>
121131
<tr>

docs/demo/basic.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Basic
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/basic.tsx"></code>

docs/examples/basic.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { CollapseProps } from 'rc-collapse';
2+
import Collapse from 'rc-collapse';
3+
import * as React from 'react';
4+
import '../../assets/index.less';
5+
6+
const App = () => {
7+
const items: CollapseProps['items'] = [
8+
{
9+
label: 'title',
10+
children: 'content',
11+
},
12+
{
13+
label: 'title 2',
14+
children: 'content 2',
15+
collapsible: 'disabled',
16+
},
17+
{
18+
label: 'title 3',
19+
children: 'content 3',
20+
onItemClick: console.log,
21+
},
22+
];
23+
24+
return <Collapse items={items} />;
25+
};
26+
27+
export default App;

src/Collapse.tsx

Lines changed: 27 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import classNames from 'classnames';
2-
import toArray from 'rc-util/lib/Children/toArray';
32
import useMergedState from 'rc-util/lib/hooks/useMergedState';
3+
import warning from 'rc-util/lib/warning';
44
import React from 'react';
5-
import type { CollapsePanelProps, CollapseProps, CollapsibleType } from './interface';
5+
import useItems from './hooks/useItems';
6+
import type { CollapseProps } from './interface';
67
import CollapsePanel from './Panel';
78

89
function getActiveKeysArray(activeKey: React.Key | React.Key[]) {
@@ -22,13 +23,14 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
2223
style,
2324
accordion,
2425
className,
25-
children: rawChildren,
26+
children,
2627
collapsible,
2728
openMotion,
2829
expandIcon,
2930
activeKey: rawActiveKey,
3031
defaultActiveKey,
3132
onChange,
33+
items,
3234
} = props;
3335

3436
const collapseClassName = classNames(prefixCls, className);
@@ -40,7 +42,7 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
4042
postState: getActiveKeysArray,
4143
});
4244

43-
const onClickItem = (key: React.Key) =>
45+
const onItemClick = (key: React.Key) =>
4446
setActiveKey(() => {
4547
if (accordion) {
4648
return activeKey[0] === key ? [] : [key];
@@ -56,65 +58,21 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
5658
});
5759

5860
// ======================== Children ========================
59-
const getNewChild = (child: React.ReactElement<CollapsePanelProps>, index: number) => {
60-
if (!child) return null;
61-
62-
const key = child.key || String(index);
63-
64-
const {
65-
header,
66-
headerClass,
67-
destroyInactivePanel: childDestroyInactivePanel,
68-
collapsible: childCollapsible,
69-
onItemClick: childOnItemClick,
70-
} = child.props;
71-
72-
let isActive = false;
73-
if (accordion) {
74-
isActive = activeKey[0] === key;
75-
} else {
76-
isActive = activeKey.indexOf(key) > -1;
77-
}
78-
79-
const mergeCollapsible: CollapsibleType = childCollapsible ?? collapsible;
80-
81-
const handleItemClick = (value: React.Key) => {
82-
if (mergeCollapsible === 'disabled') return;
83-
onClickItem(value);
84-
childOnItemClick?.(value);
85-
};
86-
87-
const childProps = {
88-
key,
89-
panelKey: key,
90-
header,
91-
headerClass,
92-
isActive,
93-
prefixCls,
94-
destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel,
95-
openMotion,
96-
accordion,
97-
children: child.props.children,
98-
onItemClick: handleItemClick,
99-
expandIcon,
100-
collapsible: mergeCollapsible,
101-
};
102-
103-
// https://github.com/ant-design/ant-design/issues/20479
104-
if (typeof child.type === 'string') {
105-
return child;
106-
}
107-
108-
Object.keys(childProps).forEach((propName) => {
109-
if (typeof childProps[propName] === 'undefined') {
110-
delete childProps[propName];
111-
}
112-
});
113-
114-
return React.cloneElement(child, childProps);
115-
};
61+
warning(
62+
!children,
63+
'`children` will be removed in next major version. Please use `items` instead.',
64+
);
11665

117-
const children = toArray(rawChildren).map(getNewChild);
66+
const mergedChildren = useItems(items, children, {
67+
prefixCls,
68+
accordion,
69+
openMotion,
70+
expandIcon,
71+
collapsible,
72+
destroyInactivePanel,
73+
onItemClick,
74+
activeKey,
75+
});
11876

11977
// ======================== Render ========================
12078
return (
@@ -124,9 +82,14 @@ const Collapse = React.forwardRef<HTMLDivElement, CollapseProps>((props, ref) =>
12482
style={style}
12583
role={accordion ? 'tablist' : undefined}
12684
>
127-
{children}
85+
{mergedChildren}
12886
</div>
12987
);
13088
});
13189

132-
export default Object.assign(Collapse, { Panel: CollapsePanel });
90+
export default Object.assign(Collapse, {
91+
/**
92+
* @deprecated use `items` instead, will be removed in `v4.0.0`
93+
*/
94+
Panel: CollapsePanel,
95+
});

src/hooks/useItems.tsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import toArray from 'rc-util/lib/Children/toArray';
2+
import React from 'react';
3+
import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface';
4+
import CollapsePanel from '../Panel';
5+
6+
type Props = Pick<CollapsePanelProps, 'prefixCls' | 'onItemClick' | 'openMotion' | 'expandIcon'> &
7+
Pick<CollapseProps, 'accordion' | 'collapsible' | 'destroyInactivePanel'> & {
8+
activeKey: React.Key[];
9+
};
10+
11+
const convertItemsToNodes = (items: ItemType[], props: Props) => {
12+
const {
13+
prefixCls,
14+
accordion,
15+
collapsible,
16+
destroyInactivePanel,
17+
onItemClick,
18+
activeKey,
19+
openMotion,
20+
expandIcon,
21+
} = props;
22+
23+
return items.map((item, index) => {
24+
const {
25+
children,
26+
label,
27+
key: rawKey,
28+
collapsible: rawCollapsible,
29+
onItemClick: rawOnItemClick,
30+
destroyInactivePanel: rawDestroyInactivePanel,
31+
...restProps
32+
} = item;
33+
34+
// You may be puzzled why you want to convert them all into strings, me too.
35+
// Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15
36+
const key = String(rawKey ?? index);
37+
const mergeCollapsible = rawCollapsible ?? collapsible;
38+
const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel;
39+
40+
const handleItemClick = (value: React.Key) => {
41+
if (mergeCollapsible === 'disabled') return;
42+
onItemClick(value);
43+
rawOnItemClick?.(value);
44+
};
45+
46+
let isActive = false;
47+
if (accordion) {
48+
isActive = activeKey[0] === key;
49+
} else {
50+
isActive = activeKey.indexOf(key) > -1;
51+
}
52+
53+
return (
54+
<CollapsePanel
55+
{...restProps}
56+
prefixCls={prefixCls}
57+
key={key}
58+
panelKey={key}
59+
isActive={isActive}
60+
accordion={accordion}
61+
openMotion={openMotion}
62+
expandIcon={expandIcon}
63+
header={label}
64+
collapsible={mergeCollapsible}
65+
onItemClick={handleItemClick}
66+
destroyInactivePanel={mergeDestroyInactivePanel}
67+
>
68+
{children}
69+
</CollapsePanel>
70+
);
71+
});
72+
};
73+
74+
/**
75+
* @deprecated The next major version will be removed
76+
*/
77+
const getNewChild = (
78+
child: React.ReactElement<CollapsePanelProps>,
79+
index: number,
80+
props: Props,
81+
) => {
82+
if (!child) return null;
83+
84+
const {
85+
prefixCls,
86+
accordion,
87+
collapsible,
88+
destroyInactivePanel,
89+
onItemClick,
90+
activeKey,
91+
openMotion,
92+
expandIcon,
93+
} = props;
94+
95+
const key = child.key || String(index);
96+
97+
const {
98+
header,
99+
headerClass,
100+
destroyInactivePanel: childDestroyInactivePanel,
101+
collapsible: childCollapsible,
102+
onItemClick: childOnItemClick,
103+
} = child.props;
104+
105+
let isActive = false;
106+
if (accordion) {
107+
isActive = activeKey[0] === key;
108+
} else {
109+
isActive = activeKey.indexOf(key) > -1;
110+
}
111+
112+
const mergeCollapsible = childCollapsible ?? collapsible;
113+
114+
const handleItemClick = (value: React.Key) => {
115+
if (mergeCollapsible === 'disabled') return;
116+
onItemClick(value);
117+
childOnItemClick?.(value);
118+
};
119+
120+
const childProps = {
121+
key,
122+
panelKey: key,
123+
header,
124+
headerClass,
125+
isActive,
126+
prefixCls,
127+
destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel,
128+
openMotion,
129+
accordion,
130+
children: child.props.children,
131+
onItemClick: handleItemClick,
132+
expandIcon,
133+
collapsible: mergeCollapsible,
134+
};
135+
136+
// https://github.com/ant-design/ant-design/issues/20479
137+
if (typeof child.type === 'string') {
138+
return child;
139+
}
140+
141+
Object.keys(childProps).forEach((propName) => {
142+
if (typeof childProps[propName] === 'undefined') {
143+
delete childProps[propName];
144+
}
145+
});
146+
147+
return React.cloneElement(child, childProps);
148+
};
149+
150+
function useItems(
151+
items?: ItemType[],
152+
rawChildren?: React.ReactNode,
153+
props?: Props,
154+
): React.ReactElement<CollapsePanelProps>[] {
155+
if (Array.isArray(items)) {
156+
return convertItemsToNodes(items, props);
157+
}
158+
159+
return toArray(rawChildren).map((child, index) => getNewChild(child, index, props));
160+
}
161+
162+
export default useItems;

src/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ import Collapse from './Collapse';
33
export type { CollapsePanelProps, CollapseProps } from './interface';
44

55
export default Collapse;
6+
7+
/**
8+
* @deprecated use `items` instead, will be removed in `v4.0.0`
9+
*/
610
export const { Panel } = Collapse;

0 commit comments

Comments
 (0)