From 67071380e014e1edf46598d19a8ac52e0c3f9cdc Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 16:34:36 +0800 Subject: [PATCH 01/25] feat: support items & add deprecated warning --- docs/demo/basic.md | 8 +++++ docs/examples/basic.tsx | 32 ++++++++++++++++++++ src/Collapse.tsx | 65 +++++++++++++++++++++++++++++++++++++++-- src/index.tsx | 4 +++ src/interface.ts | 13 +++++++++ 5 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 docs/demo/basic.md create mode 100644 docs/examples/basic.tsx diff --git a/docs/demo/basic.md b/docs/demo/basic.md new file mode 100644 index 00000000..acad05fc --- /dev/null +++ b/docs/demo/basic.md @@ -0,0 +1,8 @@ +--- +title: Basic +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/basic.tsx b/docs/examples/basic.tsx new file mode 100644 index 00000000..4e7de216 --- /dev/null +++ b/docs/examples/basic.tsx @@ -0,0 +1,32 @@ +import type { CollapseProps } from 'rc-collapse'; +import Collapse from 'rc-collapse'; +import * as React from 'react'; +import '../../assets/index.less'; + +const App = () => { + const items: CollapseProps['items'] = [ + { + header: 'title', + children: 'content', + }, + { + header: 'title 2', + children: 'content 2', + collapsible: 'disabled', + }, + { + header: 'title 3', + children: 'content 3', + onItemClick: console.log, + }, + { + header: 'title 4', + children: 'content 4', + expandIcon: () => custom icon, + }, + ]; + + return ; +}; + +export default App; diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 1c51e11c..d68271bf 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,8 +1,9 @@ import classNames from 'classnames'; import toArray from 'rc-util/lib/Children/toArray'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; +import warning from 'rc-util/lib/warning'; import React from 'react'; -import type { CollapsePanelProps, CollapseProps, CollapsibleType } from './interface'; +import type { CollapsePanelProps, CollapseProps, CollapsibleType, ItemType } from './interface'; import CollapsePanel from './Panel'; function getActiveKeysArray(activeKey: React.Key | React.Key[]) { @@ -29,6 +30,7 @@ const Collapse = React.forwardRef((props, ref) => activeKey: rawActiveKey, defaultActiveKey, onChange, + items, } = props; const collapseClassName = classNames(prefixCls, className); @@ -56,6 +58,11 @@ const Collapse = React.forwardRef((props, ref) => }); // ======================== Children ======================== + warning( + !rawChildren, + '`children` will be removed in next major version. Please use `items` instead.', + ); + const getNewChild = (child: React.ReactElement, index: number) => { if (!child) return null; @@ -114,7 +121,54 @@ const Collapse = React.forwardRef((props, ref) => return React.cloneElement(child, childProps); }; - const children = toArray(rawChildren).map(getNewChild); + // eslint-disable-next-line @typescript-eslint/no-shadow + const convertItemsToNodes = (items: ItemType[]) => + items.map((item, index) => { + const { + children, + key: rawKey, + collapsible: rawCollapsible, + onItemClick: rawOnItemClick, + destroyInactivePanel: rawDestroyInactivePanel, + ...restProps + } = item; + + const key = rawKey || String(index); + const mergeCollapsible = rawCollapsible ?? collapsible; + const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; + + const handleItemClick = (value: React.Key) => { + if (mergeCollapsible === 'disabled') return; + onClickItem(value); + rawOnItemClick?.(value); + }; + + let isActive = false; + if (accordion) { + isActive = activeKey[0] === key; + } else { + isActive = activeKey.indexOf(key) > -1; + } + + return ( + + {children} + + ); + }); + + const children = Array.isArray(items) + ? convertItemsToNodes(items) + : toArray(rawChildren).map(getNewChild); // ======================== Render ======================== return ( @@ -129,4 +183,9 @@ const Collapse = React.forwardRef((props, ref) => ); }); -export default Object.assign(Collapse, { Panel: CollapsePanel }); +export default Object.assign(Collapse, { + /** + * @deprecated use `items` instead, will be removed in `v4.0.0` + */ + Panel: CollapsePanel, +}); diff --git a/src/index.tsx b/src/index.tsx index 8cda2ed2..8e963c68 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,4 +3,8 @@ import Collapse from './Collapse'; export type { CollapsePanelProps, CollapseProps } from './interface'; export default Collapse; + +/** + * @deprecated use `items` instead, will be removed in `v4.0.0` + */ export const { Panel } = Collapse; diff --git a/src/interface.ts b/src/interface.ts index 84540e54..8e3cbe6e 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -3,6 +3,11 @@ import type * as React from 'react'; export type CollapsibleType = 'header' | 'icon' | 'disabled'; +export interface ItemType extends Omit { + ref?: React.RefObject; + key?: React.Key; +} + export interface CollapseProps { prefixCls?: string; activeKey?: React.Key | React.Key[]; @@ -15,7 +20,15 @@ export interface CollapseProps { destroyInactivePanel?: boolean; expandIcon?: (props: object) => React.ReactNode; collapsible?: CollapsibleType; + /** + * @deprecated use `items` instead, will be removed in `v4.0.0` + */ children?: React.ReactNode; + /** + * Collapse items content + * @since 3.6.0 + */ + items?: ItemType[]; } export interface CollapsePanelProps extends React.DOMAttributes { From e2d2c50f2ed656751b8c49bd4ee650f3bbaa90f7 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 16:57:23 +0800 Subject: [PATCH 02/25] test: add case --- tests/__snapshots__/index.spec.tsx.snap | 135 ++++++++++++++++++++++++ tests/index.spec.tsx | 96 ++++++++++++++++- 2 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 tests/__snapshots__/index.spec.tsx.snap diff --git a/tests/__snapshots__/index.spec.tsx.snap b/tests/__snapshots__/index.spec.tsx.snap new file mode 100644 index 00000000..8ba501af --- /dev/null +++ b/tests/__snapshots__/index.spec.tsx.snap @@ -0,0 +1,135 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`collapse props items should work 1`] = ` +
+
+ +
+
+ +
+
+`; + +exports[`collapse props items should work with nested 1`] = ` +
+
+ +
+
+ +
+
+ +
+
+`; diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 295e7a2c..e54d6ffa 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -3,7 +3,7 @@ import { fireEvent, render } from '@testing-library/react'; import KeyCode from 'rc-util/lib/KeyCode'; import React, { Fragment } from 'react'; import Collapse, { Panel } from '../src/index'; -import type { CollapseProps } from '../src/interface'; +import type { CollapseProps, ItemType } from '../src/interface'; describe('collapse', () => { let changeHook: jest.Mock | null; @@ -690,4 +690,98 @@ describe('collapse', () => { ); expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); }); + + describe('props items', () => { + const items: ItemType[] = [ + { + header: 'title', + children: 'content', + }, + { + header: 'title 2', + children: 'content 2', + collapsible: 'disabled', + }, + ]; + + it('should work', () => { + const { container } = render(); + expect(container.firstChild).toMatchSnapshot(); + }); + + it('should work with onItemClick', () => { + const onItemClick = jest.fn(); + const { container } = render( + , + ); + fireEvent.click(container.querySelectorAll('.rc-collapse-header')[2]); + expect(onItemClick).toHaveBeenCalled(); + expect(onItemClick).lastCalledWith('2'); + }); + + it('should work with collapsible', () => { + const onItemClick = jest.fn(); + const onChangeFn = jest.fn(); + const { container } = render( + , + ); + + fireEvent.click(container.querySelectorAll('.rc-collapse-header')[2]); + expect(onItemClick).not.toHaveBeenCalled(); + + fireEvent.click( + container.querySelector('.rc-collapse-item:nth-child(3) .rc-collapse-expand-icon'), + ); + expect(onItemClick).toHaveBeenCalled(); + expect(onChangeFn).toBeCalledTimes(1); + expect(onChangeFn).lastCalledWith(['2']); + }); + + it('should work with custom icon', () => { + const { container } = render( + i, + }, + ]} + />, + ); + expect(container.querySelector('.custom-icon')).toBeTruthy(); + }); + + it('should work with nested', () => { + const { container } = render( + , + }, + ]} + />, + ); + expect(container.firstChild).toMatchSnapshot(); + }); + }); }); From 843ffdcb52948a5d9857c3d72d927b672950ff99 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 17:11:18 +0800 Subject: [PATCH 03/25] chore: remove legacy config --- .umirc.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 .umirc.ts diff --git a/.umirc.ts b/.umirc.ts deleted file mode 100644 index 19135896..00000000 --- a/.umirc.ts +++ /dev/null @@ -1,11 +0,0 @@ -// more config: https://d.umijs.org/config -import { defineConfig } from 'dumi'; - -export default defineConfig({ - title: 'rc-collapse', - favicon: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4', - logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4', - outputPath: '.doc', - exportStatic: {}, - styles: [], -}); From 97b5b4d37e2f742d3c5f75a67ef974c6632b64de Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 17:50:47 +0800 Subject: [PATCH 04/25] test: update unit test --- tests/__snapshots__/index.spec.tsx.snap | 52 +++++++------------------ tests/index.spec.tsx | 41 +++++++++++-------- 2 files changed, 38 insertions(+), 55 deletions(-) diff --git a/tests/__snapshots__/index.spec.tsx.snap b/tests/__snapshots__/index.spec.tsx.snap index 8ba501af..ddbe32ae 100644 --- a/tests/__snapshots__/index.spec.tsx.snap +++ b/tests/__snapshots__/index.spec.tsx.snap @@ -1,33 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`collapse props items should work 1`] = ` +exports[`collapse props items should work with nested 1`] = `
-
- -
@@ -48,17 +24,10 @@ exports[`collapse props items should work 1`] = ` - title 2 + collapse 1
- -`; - -exports[`collapse props items should work with nested 1`] = ` -
@@ -79,19 +48,26 @@ exports[`collapse props items should work with nested 1`] = ` - title + collapse 2 +
+ + ExtraSpan + +
diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index e54d6ffa..a31b2cd5 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -691,30 +691,37 @@ describe('collapse', () => { expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); }); - describe('props items', () => { + describe.only('props items', () => { const items: ItemType[] = [ { - header: 'title', - children: 'content', + key: '1', + header: 'collapse 1', + children: 'first', + collapsible: 'disabled', }, { - header: 'title 2', - children: 'content 2', - collapsible: 'disabled', + key: '2', + header: 'collapse 2', + children: 'second', + extra: ExtraSpan, + }, + { + key: '3', + header: 'collapse 3', + className: 'important', + children: 'third', }, ]; - it('should work', () => { - const { container } = render(); - expect(container.firstChild).toMatchSnapshot(); - }); + runNormalTest( + test{'>'}} items={items} />, + ); it('should work with onItemClick', () => { const onItemClick = jest.fn(); const { container } = render( { ]} />, ); - fireEvent.click(container.querySelectorAll('.rc-collapse-header')[2]); + fireEvent.click(container.querySelector('.rc-collapse-header')); expect(onItemClick).toHaveBeenCalled(); - expect(onItemClick).lastCalledWith('2'); + expect(onItemClick).lastCalledWith('0'); }); it('should work with collapsible', () => { @@ -734,7 +741,7 @@ describe('collapse', () => { { />, ); - fireEvent.click(container.querySelectorAll('.rc-collapse-header')[2]); + fireEvent.click(container.querySelector('.rc-collapse-header')); expect(onItemClick).not.toHaveBeenCalled(); fireEvent.click( - container.querySelector('.rc-collapse-item:nth-child(3) .rc-collapse-expand-icon'), + container.querySelector('.rc-collapse-item:nth-child(2) .rc-collapse-expand-icon'), ); expect(onItemClick).toHaveBeenCalled(); expect(onChangeFn).toBeCalledTimes(1); - expect(onChangeFn).lastCalledWith(['2']); + expect(onChangeFn).lastCalledWith(['1']); }); it('should work with custom icon', () => { From 7ab59c7c98d7658c55a013eee3a6ca8eb08c8fa8 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 17:51:59 +0800 Subject: [PATCH 05/25] fix: fix expandIcon error --- src/Collapse.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index d68271bf..14c8e10c 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -130,6 +130,7 @@ const Collapse = React.forwardRef((props, ref) => collapsible: rawCollapsible, onItemClick: rawOnItemClick, destroyInactivePanel: rawDestroyInactivePanel, + expandIcon: rawExpandIcon = expandIcon, ...restProps } = item; @@ -159,6 +160,7 @@ const Collapse = React.forwardRef((props, ref) => collapsible={mergeCollapsible} onItemClick={handleItemClick} destroyInactivePanel={mergeDestroyInactivePanel} + expandIcon={rawExpandIcon} {...restProps} > {children} From 420b9e7efd3e336a19c94133dc1d2a420569af65 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 17:54:44 +0800 Subject: [PATCH 06/25] chore: update --- tests/index.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index a31b2cd5..d9c57ea7 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -691,7 +691,7 @@ describe('collapse', () => { expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); }); - describe.only('props items', () => { + describe('props items', () => { const items: ItemType[] = [ { key: '1', From 8700575799e429a49aca46dca721522aae6d1e19 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 19:42:02 +0800 Subject: [PATCH 07/25] fix --- src/Collapse.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 14c8e10c..e3210ef9 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -134,7 +134,9 @@ const Collapse = React.forwardRef((props, ref) => ...restProps } = item; - const key = rawKey || String(index); + // You may be puzzled why you want to convert them all into strings, me too. + // Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 + const key = String(rawKey ?? index); const mergeCollapsible = rawCollapsible ?? collapsible; const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; From 1ac26f3cf2d9c73155b84b3ffd59f8f8932b0a43 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 19:54:27 +0800 Subject: [PATCH 08/25] fix --- src/Collapse.tsx | 1 + src/interface.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index e3210ef9..629396fe 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -159,6 +159,7 @@ const Collapse = React.forwardRef((props, ref) => key={key} panelKey={key} isActive={isActive} + accordion={accordion} collapsible={mergeCollapsible} onItemClick={handleItemClick} destroyInactivePanel={mergeDestroyInactivePanel} diff --git a/src/interface.ts b/src/interface.ts index 8e3cbe6e..6db0fa14 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -3,7 +3,8 @@ import type * as React from 'react'; export type CollapsibleType = 'header' | 'icon' | 'disabled'; -export interface ItemType extends Omit { +export interface ItemType + extends Omit { ref?: React.RefObject; key?: React.Key; } From fe8951f3a1d06b9a9eb8721c832383c143195420 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 20:09:02 +0800 Subject: [PATCH 09/25] fix: support custom children test case: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 --- src/Collapse.tsx | 31 +++++++++++++++++-------------- src/interface.ts | 3 --- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 629396fe..cfe42676 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -154,20 +154,23 @@ const Collapse = React.forwardRef((props, ref) => } return ( - - {children} - + <> + + {children} + + {props.children} + ); }); diff --git a/src/interface.ts b/src/interface.ts index 6db0fa14..a40b56ca 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -21,9 +21,6 @@ export interface CollapseProps { destroyInactivePanel?: boolean; expandIcon?: (props: object) => React.ReactNode; collapsible?: CollapsibleType; - /** - * @deprecated use `items` instead, will be removed in `v4.0.0` - */ children?: React.ReactNode; /** * Collapse items content From 010d309d9aabf1744fa418cb8cdab5e01d60958f Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 20:49:43 +0800 Subject: [PATCH 10/25] fix: update --- src/Collapse.tsx | 1 + src/interface.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index cfe42676..3debba18 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -161,6 +161,7 @@ const Collapse = React.forwardRef((props, ref) => panelKey={key} isActive={isActive} accordion={accordion} + openMotion={openMotion} collapsible={mergeCollapsible} onItemClick={handleItemClick} destroyInactivePanel={mergeDestroyInactivePanel} diff --git a/src/interface.ts b/src/interface.ts index a40b56ca..6e8592ae 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -4,7 +4,10 @@ import type * as React from 'react'; export type CollapsibleType = 'header' | 'icon' | 'disabled'; export interface ItemType - extends Omit { + extends Omit< + CollapsePanelProps, + 'prefixCls' | 'panelKey' | 'isActive' | 'accordion' | 'openMotion' + > { ref?: React.RefObject; key?: React.Key; } From cfbfb51ca7642f4130eaab53ef4e1433bb34a6d9 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 20:56:07 +0800 Subject: [PATCH 11/25] Revert "fix: support custom children" This reverts commit fe8951f3a1d06b9a9eb8721c832383c143195420. # Conflicts: # src/Collapse.tsx --- src/Collapse.tsx | 33 +++++++++++++++------------------ src/interface.ts | 3 +++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 3debba18..c27cc019 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -154,24 +154,21 @@ const Collapse = React.forwardRef((props, ref) => } return ( - <> - - {children} - - {props.children} - + + {children} + ); }); diff --git a/src/interface.ts b/src/interface.ts index 6e8592ae..4ab03661 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -24,6 +24,9 @@ export interface CollapseProps { destroyInactivePanel?: boolean; expandIcon?: (props: object) => React.ReactNode; collapsible?: CollapsibleType; + /** + * @deprecated use `items` instead, will be removed in `v4.0.0` + */ children?: React.ReactNode; /** * Collapse items content From 783df83becf64f7ee4ba05a51c7ed7d9b86cd0d0 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 21:02:00 +0800 Subject: [PATCH 12/25] fix: support custom children test case: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 --- src/Collapse.tsx | 2 +- src/interface.ts | 3 --- tests/index.spec.tsx | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index c27cc019..131751ef 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -173,7 +173,7 @@ const Collapse = React.forwardRef((props, ref) => }); const children = Array.isArray(items) - ? convertItemsToNodes(items) + ? [convertItemsToNodes(items), rawChildren] : toArray(rawChildren).map(getNewChild); // ======================== Render ======================== diff --git a/src/interface.ts b/src/interface.ts index 4ab03661..6e8592ae 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -24,9 +24,6 @@ export interface CollapseProps { destroyInactivePanel?: boolean; expandIcon?: (props: object) => React.ReactNode; collapsible?: CollapsibleType; - /** - * @deprecated use `items` instead, will be removed in `v4.0.0` - */ children?: React.ReactNode; /** * Collapse items content diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index d9c57ea7..a9ba7652 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -790,5 +790,22 @@ describe('collapse', () => { ); expect(container.firstChild).toMatchSnapshot(); }); + + it('should support custom child', () => { + const { container } = render( + + custom-child + , + ); + expect(container.querySelector('.custom-child')?.innerHTML).toBe('custom-child'); + }); }); }); From 3f72d3ddf0fd6dd098c9c66ae3266005af07422f Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 21:30:52 +0800 Subject: [PATCH 13/25] docs: update docs --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index d712a747..898e7272 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,14 @@ ReactDOM.render(App, container); - specify whether the panel of children is collapsible or the area of collapsible. + + items + + interface.ts#ItemType + + - + collapse items content + @@ -116,6 +124,8 @@ If `accordion` is true, only one panel can be open. Opening another panel will c ### Collapse.Panel props +> **deprecated** use `items` instead, will be removed in `v4.0.0` + From 856394af76a37d5e050d610698e0f028cd1f8ce6 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 21:52:30 +0800 Subject: [PATCH 14/25] feat: header -> label --- docs/examples/basic.tsx | 8 ++++---- src/Collapse.tsx | 2 ++ src/interface.ts | 3 ++- tests/index.spec.tsx | 16 ++++++++-------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/examples/basic.tsx b/docs/examples/basic.tsx index 4e7de216..848f7961 100644 --- a/docs/examples/basic.tsx +++ b/docs/examples/basic.tsx @@ -6,21 +6,21 @@ import '../../assets/index.less'; const App = () => { const items: CollapseProps['items'] = [ { - header: 'title', + label: 'title', children: 'content', }, { - header: 'title 2', + label: 'title 2', children: 'content 2', collapsible: 'disabled', }, { - header: 'title 3', + label: 'title 3', children: 'content 3', onItemClick: console.log, }, { - header: 'title 4', + label: 'title 4', children: 'content 4', expandIcon: () => custom icon, }, diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 131751ef..0c943100 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -126,6 +126,7 @@ const Collapse = React.forwardRef((props, ref) => items.map((item, index) => { const { children, + label, key: rawKey, collapsible: rawCollapsible, onItemClick: rawOnItemClick, @@ -161,6 +162,7 @@ const Collapse = React.forwardRef((props, ref) => isActive={isActive} accordion={accordion} openMotion={openMotion} + header={label} collapsible={mergeCollapsible} onItemClick={handleItemClick} destroyInactivePanel={mergeDestroyInactivePanel} diff --git a/src/interface.ts b/src/interface.ts index 6e8592ae..1604721d 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -6,8 +6,9 @@ export type CollapsibleType = 'header' | 'icon' | 'disabled'; export interface ItemType extends Omit< CollapsePanelProps, - 'prefixCls' | 'panelKey' | 'isActive' | 'accordion' | 'openMotion' + 'header' | 'prefixCls' | 'panelKey' | 'isActive' | 'accordion' | 'openMotion' > { + label: CollapsePanelProps['header']; ref?: React.RefObject; key?: React.Key; } diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index a9ba7652..51d0c6ae 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -695,19 +695,19 @@ describe('collapse', () => { const items: ItemType[] = [ { key: '1', - header: 'collapse 1', + label: 'collapse 1', children: 'first', collapsible: 'disabled', }, { key: '2', - header: 'collapse 2', + label: 'collapse 2', children: 'second', extra: ExtraSpan, }, { key: '3', - header: 'collapse 3', + label: 'collapse 3', className: 'important', children: 'third', }, @@ -723,7 +723,7 @@ describe('collapse', () => { { items={[ ...items.slice(0, 1), { - header: 'title 3', + label: 'title 3', onItemClick, collapsible: 'icon', }, @@ -767,7 +767,7 @@ describe('collapse', () => { i, }, ]} @@ -782,7 +782,7 @@ describe('collapse', () => { items={[ ...items, { - header: 'title 3', + label: 'title 3', children: , }, ]} @@ -797,7 +797,7 @@ describe('collapse', () => { items={[ { key: '1', - header: 'title', + label: 'title', children: 'first', }, ]} From 1143d31f7e350b770453b488335d5ed34663bd8b Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 21:58:35 +0800 Subject: [PATCH 15/25] test: add case --- tests/index.spec.tsx | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 51d0c6ae..6df90fc7 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -807,5 +807,42 @@ describe('collapse', () => { ); expect(container.querySelector('.custom-child')?.innerHTML).toBe('custom-child'); }); + + it('should work with accordion', () => { + const { container } = render( + , + ); + + let header = container.querySelectorAll('.rc-collapse-header')?.[1]; + fireEvent.click(header); + jest.runAllTimers(); + expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); + expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(1); + header = container.querySelectorAll('.rc-collapse-header')?.[1]; + fireEvent.click(header); + jest.runAllTimers(); + expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); + expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(0); + }); }); }); From 127804a05afa0b4c75f4860a91ef834adf25f372 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 22:03:17 +0800 Subject: [PATCH 16/25] Revert "test: add case" This reverts commit 1143d31f7e350b770453b488335d5ed34663bd8b. --- tests/index.spec.tsx | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 6df90fc7..51d0c6ae 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -807,42 +807,5 @@ describe('collapse', () => { ); expect(container.querySelector('.custom-child')?.innerHTML).toBe('custom-child'); }); - - it('should work with accordion', () => { - const { container } = render( - , - ); - - let header = container.querySelectorAll('.rc-collapse-header')?.[1]; - fireEvent.click(header); - jest.runAllTimers(); - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); - expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(1); - header = container.querySelectorAll('.rc-collapse-header')?.[1]; - fireEvent.click(header); - jest.runAllTimers(); - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); - expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(0); - }); }); }); From c5b73bb34184c48650f9b72b4c910a23e18d6d6e Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 22:05:35 +0800 Subject: [PATCH 17/25] test: add case --- tests/index.spec.tsx | 60 +++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 51d0c6ae..5d66a466 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -243,23 +243,15 @@ describe('collapse', () => { expect(container.querySelectorAll('.rc-collapse-content-inactive').length).toBeFalsy(); }); - describe('prop: accordion', () => { + function runAccordionTest(element: React.ReactElement) { let collapse: RenderResult; beforeEach(() => { - collapse = render( - - - first - - - second - - - third - - , - ); + collapse = render(element); + }); + + afterEach(() => { + collapse.unmount(); }); it('accordion content, should default open zero item', () => { @@ -317,6 +309,22 @@ describe('collapse', () => { expect(item).toBeTruthy(); expect(item!.getAttribute('role')).toBe('tabpanel'); }); + } + + describe('prop: accordion', () => { + runAccordionTest( + + + first + + + second + + + third + + , + ); }); describe('forceRender', () => { @@ -717,6 +725,30 @@ describe('collapse', () => { test{'>'}} items={items} />, ); + runAccordionTest( + , + ); + it('should work with onItemClick', () => { const onItemClick = jest.fn(); const { container } = render( From 6be190f5e862994f46ca73b7b6d091aacfa5e4e1 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 22:22:59 +0800 Subject: [PATCH 18/25] chore: update type --- src/interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface.ts b/src/interface.ts index 1604721d..ee3e1646 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -8,7 +8,7 @@ export interface ItemType CollapsePanelProps, 'header' | 'prefixCls' | 'panelKey' | 'isActive' | 'accordion' | 'openMotion' > { - label: CollapsePanelProps['header']; + label?: CollapsePanelProps['header']; ref?: React.RefObject; key?: React.Key; } From 5cfd6dbc03aee56eef9e0b77484436fe1a7fc4cc Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 22:45:48 +0800 Subject: [PATCH 19/25] test: add case --- tests/index.spec.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 5d66a466..b476e038 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -823,6 +823,23 @@ describe('collapse', () => { expect(container.firstChild).toMatchSnapshot(); }); + it('should not support expandIcon', () => { + const { container } = render( + p} + items={[ + { + label: 'title', + expandIcon: () => c, + } as any, + ]} + />, + ); + + expect(container.querySelectorAll('.custom-icon')).toHaveLength(1); + expect(container.querySelector('.custom-icon')?.innerHTML).toBe('p'); + }); + it('should support custom child', () => { const { container } = render( Date: Wed, 29 Mar 2023 23:38:52 +0800 Subject: [PATCH 20/25] chore: items not support expandIcon --- docs/examples/basic.tsx | 5 ----- src/Collapse.tsx | 3 +-- src/interface.ts | 10 ++++++++-- tests/index.spec.tsx | 14 -------------- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/docs/examples/basic.tsx b/docs/examples/basic.tsx index 848f7961..4b456378 100644 --- a/docs/examples/basic.tsx +++ b/docs/examples/basic.tsx @@ -19,11 +19,6 @@ const App = () => { children: 'content 3', onItemClick: console.log, }, - { - label: 'title 4', - children: 'content 4', - expandIcon: () => custom icon, - }, ]; return ; diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 0c943100..fc3f5618 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -131,7 +131,6 @@ const Collapse = React.forwardRef((props, ref) => collapsible: rawCollapsible, onItemClick: rawOnItemClick, destroyInactivePanel: rawDestroyInactivePanel, - expandIcon: rawExpandIcon = expandIcon, ...restProps } = item; @@ -166,8 +165,8 @@ const Collapse = React.forwardRef((props, ref) => collapsible={mergeCollapsible} onItemClick={handleItemClick} destroyInactivePanel={mergeDestroyInactivePanel} - expandIcon={rawExpandIcon} {...restProps} + expandIcon={expandIcon} > {children} diff --git a/src/interface.ts b/src/interface.ts index ee3e1646..838446a0 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -6,11 +6,17 @@ export type CollapsibleType = 'header' | 'icon' | 'disabled'; export interface ItemType extends Omit< CollapsePanelProps, - 'header' | 'prefixCls' | 'panelKey' | 'isActive' | 'accordion' | 'openMotion' + | 'header' // alias of label + | 'prefixCls' + | 'panelKey' // alias of key + | 'isActive' + | 'accordion' + | 'openMotion' + | 'expandIcon' > { + key?: CollapsePanelProps['panelKey']; label?: CollapsePanelProps['header']; ref?: React.RefObject; - key?: React.Key; } export interface CollapseProps { diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index b476e038..d6638265 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -794,20 +794,6 @@ describe('collapse', () => { expect(onChangeFn).lastCalledWith(['1']); }); - it('should work with custom icon', () => { - const { container } = render( - i, - }, - ]} - />, - ); - expect(container.querySelector('.custom-icon')).toBeTruthy(); - }); - it('should work with nested', () => { const { container } = render( Date: Wed, 29 Mar 2023 23:41:26 +0800 Subject: [PATCH 21/25] fix: avoid accidental coverage --- src/Collapse.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index fc3f5618..510b1c72 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -155,18 +155,18 @@ const Collapse = React.forwardRef((props, ref) => return ( {children} From bad36356dba9ec840d48a32c86efd453336bd0a3 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 23:58:25 +0800 Subject: [PATCH 22/25] chore: not support custom child --- src/Collapse.tsx | 2 +- tests/index.spec.tsx | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 510b1c72..755dcf71 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -174,7 +174,7 @@ const Collapse = React.forwardRef((props, ref) => }); const children = Array.isArray(items) - ? [convertItemsToNodes(items), rawChildren] + ? convertItemsToNodes(items) : toArray(rawChildren).map(getNewChild); // ======================== Render ======================== diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index d6638265..533df08b 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -825,22 +825,5 @@ describe('collapse', () => { expect(container.querySelectorAll('.custom-icon')).toHaveLength(1); expect(container.querySelector('.custom-icon')?.innerHTML).toBe('p'); }); - - it('should support custom child', () => { - const { container } = render( - - custom-child - , - ); - expect(container.querySelector('.custom-child')?.innerHTML).toBe('custom-child'); - }); }); }); From 59a9d5f9d2b8421ac6dc439f50c895e23c0c66d0 Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 16:27:21 +0800 Subject: [PATCH 23/25] refactor: extraction hook --- src/Collapse.tsx | 66 +++++------------------------------------- src/hooks/useItems.tsx | 26 +++++++++++++++++ 2 files changed, 33 insertions(+), 59 deletions(-) create mode 100644 src/hooks/useItems.tsx diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 755dcf71..c662d16a 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import toArray from 'rc-util/lib/Children/toArray'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import warning from 'rc-util/lib/warning'; import React from 'react'; -import type { CollapsePanelProps, CollapseProps, CollapsibleType, ItemType } from './interface'; +import useItems from './hooks/useItems'; +import type { CollapsePanelProps, CollapseProps, CollapsibleType } from './interface'; import CollapsePanel from './Panel'; function getActiveKeysArray(activeKey: React.Key | React.Key[]) { @@ -23,7 +23,7 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children: rawChildren, + children, collapsible, openMotion, expandIcon, @@ -59,7 +59,7 @@ const Collapse = React.forwardRef((props, ref) => // ======================== Children ======================== warning( - !rawChildren, + !children, '`children` will be removed in next major version. Please use `items` instead.', ); @@ -121,61 +121,9 @@ const Collapse = React.forwardRef((props, ref) => return React.cloneElement(child, childProps); }; - // eslint-disable-next-line @typescript-eslint/no-shadow - const convertItemsToNodes = (items: ItemType[]) => - items.map((item, index) => { - const { - children, - label, - key: rawKey, - collapsible: rawCollapsible, - onItemClick: rawOnItemClick, - destroyInactivePanel: rawDestroyInactivePanel, - ...restProps - } = item; - - // You may be puzzled why you want to convert them all into strings, me too. - // Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 - const key = String(rawKey ?? index); - const mergeCollapsible = rawCollapsible ?? collapsible; - const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; - - const handleItemClick = (value: React.Key) => { - if (mergeCollapsible === 'disabled') return; - onClickItem(value); - rawOnItemClick?.(value); - }; - - let isActive = false; - if (accordion) { - isActive = activeKey[0] === key; - } else { - isActive = activeKey.indexOf(key) > -1; - } - - return ( - - {children} - - ); - }); + const mergedChildren = useItems(items, children); - const children = Array.isArray(items) - ? convertItemsToNodes(items) - : toArray(rawChildren).map(getNewChild); + const childNodes = mergedChildren.map(getNewChild); // ======================== Render ======================== return ( @@ -185,7 +133,7 @@ const Collapse = React.forwardRef((props, ref) => style={style} role={accordion ? 'tablist' : undefined} > - {children} + {childNodes} ); }); diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx new file mode 100644 index 00000000..2ead13a7 --- /dev/null +++ b/src/hooks/useItems.tsx @@ -0,0 +1,26 @@ +import toArray from 'rc-util/lib/Children/toArray'; +import React from 'react'; +import type { CollapsePanelProps, ItemType } from '../interface'; +import CollapsePanel from '../Panel'; + +const convertItemsToNodes = (items: ItemType[]) => + items.map((item, index) => { + const { label, key: rawKey, ...restProps } = item; + + const key = String(rawKey ?? index); + + return ; + }); + +function useItems( + items?: ItemType[], + rawChildren?: React.ReactNode, +): React.ReactElement[] { + if (Array.isArray(items)) { + return convertItemsToNodes(items); + } + + return toArray(rawChildren); +} + +export default useItems; From d0ea168f4952e925920bc71a057c72038552a732 Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 16:42:29 +0800 Subject: [PATCH 24/25] Revert "refactor: extraction hook" This reverts commit 59a9d5f9d2b8421ac6dc439f50c895e23c0c66d0. --- src/Collapse.tsx | 66 +++++++++++++++++++++++++++++++++++++----- src/hooks/useItems.tsx | 26 ----------------- 2 files changed, 59 insertions(+), 33 deletions(-) delete mode 100644 src/hooks/useItems.tsx diff --git a/src/Collapse.tsx b/src/Collapse.tsx index c662d16a..755dcf71 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; +import toArray from 'rc-util/lib/Children/toArray'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import warning from 'rc-util/lib/warning'; import React from 'react'; -import useItems from './hooks/useItems'; -import type { CollapsePanelProps, CollapseProps, CollapsibleType } from './interface'; +import type { CollapsePanelProps, CollapseProps, CollapsibleType, ItemType } from './interface'; import CollapsePanel from './Panel'; function getActiveKeysArray(activeKey: React.Key | React.Key[]) { @@ -23,7 +23,7 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children, + children: rawChildren, collapsible, openMotion, expandIcon, @@ -59,7 +59,7 @@ const Collapse = React.forwardRef((props, ref) => // ======================== Children ======================== warning( - !children, + !rawChildren, '`children` will be removed in next major version. Please use `items` instead.', ); @@ -121,9 +121,61 @@ const Collapse = React.forwardRef((props, ref) => return React.cloneElement(child, childProps); }; - const mergedChildren = useItems(items, children); + // eslint-disable-next-line @typescript-eslint/no-shadow + const convertItemsToNodes = (items: ItemType[]) => + items.map((item, index) => { + const { + children, + label, + key: rawKey, + collapsible: rawCollapsible, + onItemClick: rawOnItemClick, + destroyInactivePanel: rawDestroyInactivePanel, + ...restProps + } = item; + + // You may be puzzled why you want to convert them all into strings, me too. + // Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 + const key = String(rawKey ?? index); + const mergeCollapsible = rawCollapsible ?? collapsible; + const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; + + const handleItemClick = (value: React.Key) => { + if (mergeCollapsible === 'disabled') return; + onClickItem(value); + rawOnItemClick?.(value); + }; + + let isActive = false; + if (accordion) { + isActive = activeKey[0] === key; + } else { + isActive = activeKey.indexOf(key) > -1; + } + + return ( + + {children} + + ); + }); - const childNodes = mergedChildren.map(getNewChild); + const children = Array.isArray(items) + ? convertItemsToNodes(items) + : toArray(rawChildren).map(getNewChild); // ======================== Render ======================== return ( @@ -133,7 +185,7 @@ const Collapse = React.forwardRef((props, ref) => style={style} role={accordion ? 'tablist' : undefined} > - {childNodes} + {children} ); }); diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx deleted file mode 100644 index 2ead13a7..00000000 --- a/src/hooks/useItems.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import toArray from 'rc-util/lib/Children/toArray'; -import React from 'react'; -import type { CollapsePanelProps, ItemType } from '../interface'; -import CollapsePanel from '../Panel'; - -const convertItemsToNodes = (items: ItemType[]) => - items.map((item, index) => { - const { label, key: rawKey, ...restProps } = item; - - const key = String(rawKey ?? index); - - return ; - }); - -function useItems( - items?: ItemType[], - rawChildren?: React.ReactNode, -): React.ReactElement[] { - if (Array.isArray(items)) { - return convertItemsToNodes(items); - } - - return toArray(rawChildren); -} - -export default useItems; From efcd88e9ac7b6d2b6e65e111dcdf0b2b6856db57 Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 17:11:55 +0800 Subject: [PATCH 25/25] refactor: extraction hook --- src/Collapse.tsx | 135 ++++------------------------------ src/hooks/useItems.tsx | 162 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 119 deletions(-) create mode 100644 src/hooks/useItems.tsx diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 755dcf71..36267d29 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; -import toArray from 'rc-util/lib/Children/toArray'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import warning from 'rc-util/lib/warning'; import React from 'react'; -import type { CollapsePanelProps, CollapseProps, CollapsibleType, ItemType } from './interface'; +import useItems from './hooks/useItems'; +import type { CollapseProps } from './interface'; import CollapsePanel from './Panel'; function getActiveKeysArray(activeKey: React.Key | React.Key[]) { @@ -23,7 +23,7 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children: rawChildren, + children, collapsible, openMotion, expandIcon, @@ -42,7 +42,7 @@ const Collapse = React.forwardRef((props, ref) => postState: getActiveKeysArray, }); - const onClickItem = (key: React.Key) => + const onItemClick = (key: React.Key) => setActiveKey(() => { if (accordion) { return activeKey[0] === key ? [] : [key]; @@ -59,123 +59,20 @@ const Collapse = React.forwardRef((props, ref) => // ======================== Children ======================== warning( - !rawChildren, + !children, '`children` will be removed in next major version. Please use `items` instead.', ); - const getNewChild = (child: React.ReactElement, index: number) => { - if (!child) return null; - - const key = child.key || String(index); - - const { - header, - headerClass, - destroyInactivePanel: childDestroyInactivePanel, - collapsible: childCollapsible, - onItemClick: childOnItemClick, - } = child.props; - - let isActive = false; - if (accordion) { - isActive = activeKey[0] === key; - } else { - isActive = activeKey.indexOf(key) > -1; - } - - const mergeCollapsible: CollapsibleType = childCollapsible ?? collapsible; - - const handleItemClick = (value: React.Key) => { - if (mergeCollapsible === 'disabled') return; - onClickItem(value); - childOnItemClick?.(value); - }; - - const childProps = { - key, - panelKey: key, - header, - headerClass, - isActive, - prefixCls, - destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel, - openMotion, - accordion, - children: child.props.children, - onItemClick: handleItemClick, - expandIcon, - collapsible: mergeCollapsible, - }; - - // https://github.com/ant-design/ant-design/issues/20479 - if (typeof child.type === 'string') { - return child; - } - - Object.keys(childProps).forEach((propName) => { - if (typeof childProps[propName] === 'undefined') { - delete childProps[propName]; - } - }); - - return React.cloneElement(child, childProps); - }; - - // eslint-disable-next-line @typescript-eslint/no-shadow - const convertItemsToNodes = (items: ItemType[]) => - items.map((item, index) => { - const { - children, - label, - key: rawKey, - collapsible: rawCollapsible, - onItemClick: rawOnItemClick, - destroyInactivePanel: rawDestroyInactivePanel, - ...restProps - } = item; - - // You may be puzzled why you want to convert them all into strings, me too. - // Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 - const key = String(rawKey ?? index); - const mergeCollapsible = rawCollapsible ?? collapsible; - const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; - - const handleItemClick = (value: React.Key) => { - if (mergeCollapsible === 'disabled') return; - onClickItem(value); - rawOnItemClick?.(value); - }; - - let isActive = false; - if (accordion) { - isActive = activeKey[0] === key; - } else { - isActive = activeKey.indexOf(key) > -1; - } - - return ( - - {children} - - ); - }); - - const children = Array.isArray(items) - ? convertItemsToNodes(items) - : toArray(rawChildren).map(getNewChild); + const mergedChildren = useItems(items, children, { + prefixCls, + accordion, + openMotion, + expandIcon, + collapsible, + destroyInactivePanel, + onItemClick, + activeKey, + }); // ======================== Render ======================== return ( @@ -185,7 +82,7 @@ const Collapse = React.forwardRef((props, ref) => style={style} role={accordion ? 'tablist' : undefined} > - {children} + {mergedChildren} ); }); diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx new file mode 100644 index 00000000..2bc5db7b --- /dev/null +++ b/src/hooks/useItems.tsx @@ -0,0 +1,162 @@ +import toArray from 'rc-util/lib/Children/toArray'; +import React from 'react'; +import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface'; +import CollapsePanel from '../Panel'; + +type Props = Pick & + Pick & { + activeKey: React.Key[]; + }; + +const convertItemsToNodes = (items: ItemType[], props: Props) => { + const { + prefixCls, + accordion, + collapsible, + destroyInactivePanel, + onItemClick, + activeKey, + openMotion, + expandIcon, + } = props; + + return items.map((item, index) => { + const { + children, + label, + key: rawKey, + collapsible: rawCollapsible, + onItemClick: rawOnItemClick, + destroyInactivePanel: rawDestroyInactivePanel, + ...restProps + } = item; + + // You may be puzzled why you want to convert them all into strings, me too. + // Maybe: https://github.com/react-component/collapse/blob/aac303a8b6ff30e35060b4f8fecde6f4556fcbe2/src/Collapse.tsx#L15 + const key = String(rawKey ?? index); + const mergeCollapsible = rawCollapsible ?? collapsible; + const mergeDestroyInactivePanel = rawDestroyInactivePanel ?? destroyInactivePanel; + + const handleItemClick = (value: React.Key) => { + if (mergeCollapsible === 'disabled') return; + onItemClick(value); + rawOnItemClick?.(value); + }; + + let isActive = false; + if (accordion) { + isActive = activeKey[0] === key; + } else { + isActive = activeKey.indexOf(key) > -1; + } + + return ( + + {children} + + ); + }); +}; + +/** + * @deprecated The next major version will be removed + */ +const getNewChild = ( + child: React.ReactElement, + index: number, + props: Props, +) => { + if (!child) return null; + + const { + prefixCls, + accordion, + collapsible, + destroyInactivePanel, + onItemClick, + activeKey, + openMotion, + expandIcon, + } = props; + + const key = child.key || String(index); + + const { + header, + headerClass, + destroyInactivePanel: childDestroyInactivePanel, + collapsible: childCollapsible, + onItemClick: childOnItemClick, + } = child.props; + + let isActive = false; + if (accordion) { + isActive = activeKey[0] === key; + } else { + isActive = activeKey.indexOf(key) > -1; + } + + const mergeCollapsible = childCollapsible ?? collapsible; + + const handleItemClick = (value: React.Key) => { + if (mergeCollapsible === 'disabled') return; + onItemClick(value); + childOnItemClick?.(value); + }; + + const childProps = { + key, + panelKey: key, + header, + headerClass, + isActive, + prefixCls, + destroyInactivePanel: childDestroyInactivePanel ?? destroyInactivePanel, + openMotion, + accordion, + children: child.props.children, + onItemClick: handleItemClick, + expandIcon, + collapsible: mergeCollapsible, + }; + + // https://github.com/ant-design/ant-design/issues/20479 + if (typeof child.type === 'string') { + return child; + } + + Object.keys(childProps).forEach((propName) => { + if (typeof childProps[propName] === 'undefined') { + delete childProps[propName]; + } + }); + + return React.cloneElement(child, childProps); +}; + +function useItems( + items?: ItemType[], + rawChildren?: React.ReactNode, + props?: Props, +): React.ReactElement[] { + if (Array.isArray(items)) { + return convertItemsToNodes(items, props); + } + + return toArray(rawChildren).map((child, index) => getNewChild(child, index, props)); +} + +export default useItems;