From bd2361490eea49a1957c3215510c731224a941c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com> Date: Sat, 4 Jan 2025 22:38:20 +0800 Subject: [PATCH 1/4] feat: retire deprecated api --- .gitignore | 3 +- src/Collapse.tsx | 16 +- src/hooks/useItems.tsx | 89 +----- src/index.tsx | 5 - tests/index.spec.tsx | 648 ++++++++++++++++++++++++++--------------- 5 files changed, 414 insertions(+), 347 deletions(-) diff --git a/.gitignore b/.gitignore index 2623981..c1840f9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ package-lock.json .env.local src/.umi -bun.lockb \ No newline at end of file +bun.lockb +.vscode/ \ No newline at end of file diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 0623138..333e593 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,10 +1,8 @@ import classNames from 'classnames'; 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 { CollapseProps } from './interface'; -import CollapsePanel from './Panel'; import pickAttrs from 'rc-util/lib/pickAttrs'; function getActiveKeysArray(activeKey: React.Key | React.Key[]) { @@ -59,12 +57,7 @@ const Collapse = React.forwardRef((props, ref) => }); // ======================== Children ======================== - warning( - !children, - '[rc-collapse] `children` will be removed in next major version. Please use `items` instead.', - ); - - const mergedChildren = useItems(items, children, { + const mergedChildren = useItems(items, { prefixCls, accordion, openMotion, @@ -89,9 +82,4 @@ const Collapse = React.forwardRef((props, ref) => ); }); -export default Object.assign(Collapse, { - /** - * @deprecated use `items` instead, will be removed in `v4.0.0` - */ - Panel: CollapsePanel, -}); +export default Collapse; diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index 2bc5db7..4108fa7 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -1,4 +1,3 @@ -import toArray from 'rc-util/lib/Children/toArray'; import React from 'react'; import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface'; import CollapsePanel from '../Panel'; @@ -71,92 +70,8 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { }); }; -/** - * @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)); +function useItems(items?: ItemType[], props?: Props): React.ReactElement[] { + return convertItemsToNodes(items, props); } export default useItems; diff --git a/src/index.tsx b/src/index.tsx index 8e963c6..428b33f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,8 +3,3 @@ 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/tests/index.spec.tsx b/tests/index.spec.tsx index c08d7ce..dd9f3c5 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -2,7 +2,7 @@ import type { RenderResult } from '@testing-library/react'; 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 Collapse from '../src/index'; import type { CollapseProps, ItemType } from '../src/interface'; describe('collapse', () => { @@ -116,17 +116,30 @@ describe('collapse', () => { const expandIcon = () => test{'>'}; const element = ( - - - first - - ExtraSpan}> - second - - - third - - + ExtraSpan, + children: 'second', + }, + { + key: '3', + label: 'collapse 3', + className: 'important', + children: 'third', + }, + ]} + /> ); runNormalTest(element); @@ -143,17 +156,27 @@ describe('collapse', () => { }; return ( - - - first - - - second - - - third - - + ); }; @@ -172,17 +195,30 @@ describe('collapse', () => { describe('it should support number key', () => { const expandIcon = () => test{'>'}; const element = ( - - - first - - ExtraSpan}> - second - - - third - - + ExtraSpan, + children: 'second', + }, + { + key: 3, + label: 'collapse 3', + className: 'important', + children: 'third', + }, + ]} + /> ); runNormalTest(element); @@ -191,11 +227,17 @@ describe('collapse', () => { describe('prop: headerClass', () => { it('applies the passed headerClass to the header', () => { const element = ( - - - first - - + ); const { container } = render(element); @@ -207,11 +249,18 @@ describe('collapse', () => { it('should support extra whit number 0', () => { const { container } = render( - - - zero - - , + , ); const extraNodes = container.querySelectorAll('.rc-collapse-extra'); @@ -221,17 +270,27 @@ describe('collapse', () => { it('should support activeKey number 0', () => { const { container } = render( - - - zero - - - first - - - second - - , + , ); // activeKey number 0, should open one item @@ -240,17 +299,28 @@ describe('collapse', () => { it('click should toggle panel state', () => { const { container } = render( - - - first - - - second - - - third - - , + , ); const header = container.querySelectorAll('.rc-collapse-header')?.[1]; @@ -330,59 +400,92 @@ describe('collapse', () => { describe('prop: accordion', () => { runAccordionTest( - - - first - - - second - - - third - - , + , ); }); describe('forceRender', () => { it('when forceRender is not supplied it should lazy render the panel content', () => { const { container } = render( - - - first - - - second - - , + , ); expect(container.querySelectorAll('.rc-collapse-content')).toHaveLength(0); }); it('when forceRender is FALSE it should lazy render the panel content', () => { const { container } = render( - - - first - - - second - - , + , ); expect(container.querySelectorAll('.rc-collapse-content')).toHaveLength(0); }); it('when forceRender is TRUE then it should render all the panel content to the DOM', () => { const { container } = render( - - - first - - - second - - , + , ); jest.runAllTimers(); @@ -405,17 +508,26 @@ describe('collapse', () => { }; const { container } = render( - - - first - - - second - - - third - - , + , ); fireEvent.keyDown(container.querySelectorAll('.rc-collapse-header')?.[2], myKeyEvent); @@ -443,21 +555,30 @@ describe('collapse', () => { describe('wrapped in Fragment', () => { const expandIcon = () => test{'>'}; const element = ( - - - - first - - ExtraSpan}> - second - - - - third - - - - + ExtraSpan, + children: 'second', + }, + { + key: '3', + label: 'collapse 3', + className: 'important', + children: 'third', + }, + ]} + /> ); runNormalTest(element); @@ -465,43 +586,40 @@ describe('collapse', () => { it('should support return null icon', () => { const { container } = render( - null}> - - first - - , + null} + items={[ + { + key: '1', + label: 'title', + children: 'first', + }, + ]} + />, ); expect(container.querySelector('.rc-collapse-header')?.childNodes).toHaveLength(1); }); - it('should support custom child', () => { - const { container } = render( - - - first - - custom-child - , - ); - expect(container.querySelector('.custom-child')?.innerHTML).toBe('custom-child'); - }); - // https://github.com/ant-design/ant-design/issues/36327 // https://github.com/ant-design/ant-design/issues/6179 // https://github.com/react-component/collapse/issues/73#issuecomment-323626120 it('should support custom component', () => { - const PanelElement = (props) => ( - -

test

-
- ); const { container } = render( - - - - second - - , + test

, + }, + { + key: '2', + label: 'collapse 2', + children: 'second', + }, + ]} + />, ); expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); @@ -522,23 +640,33 @@ describe('collapse', () => { describe('prop: collapsible', () => { it('default', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(1); }); + it('should work when value is header', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); @@ -546,13 +674,19 @@ describe('collapse', () => { fireEvent.click(container.querySelector('.rc-collapse-header-text')!); expect(container.querySelectorAll('.rc-collapse-item-active')).toHaveLength(1); }); + it('should work when value is icon', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-expand-icon')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); @@ -563,11 +697,16 @@ describe('collapse', () => { it('should disabled when value is disabled', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); expect(container.querySelectorAll('.rc-collapse-item-disabled')).toHaveLength(1); @@ -577,11 +716,17 @@ describe('collapse', () => { it('the value of panel should be read first', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); @@ -593,11 +738,16 @@ describe('collapse', () => { it('icon trigger when collapsible equal header', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header .arrow')!); @@ -606,11 +756,16 @@ describe('collapse', () => { it('header not trigger when collapsible equal icon', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header-text')!); @@ -620,11 +775,16 @@ describe('collapse', () => { it('!showArrow', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelectorAll('.rc-collapse-expand-icon')).toHaveLength(0); @@ -633,46 +793,38 @@ describe('collapse', () => { it('Panel container dom can set event handler', () => { const clickHandler = jest.fn(); const { container } = render( - - -
Click this
-
-
, + Click this, + onClick: clickHandler, + }, + ]} + />, ); fireEvent.click(container.querySelector('.target')!); expect(clickHandler).toHaveBeenCalled(); }); - it('falsy Panel', () => { - const { container } = render( - - {null} - -

Panel 1 content

-
- {0} - -

Panel 2 content

-
- {undefined} - {false} - {true} -
, - ); - - expect(container.querySelectorAll('.rc-collapse-item')).toHaveLength(2); - }); - it('ref should work', () => { const ref = React.createRef(); const panelRef = React.createRef(); const { container } = render( - - - first - - , + , ); expect(ref.current).toBe(container.firstChild); expect(panelRef.current).toBe(container.querySelector('.rc-collapse-item')); @@ -682,11 +834,16 @@ describe('collapse', () => { it('onItemClick should work', () => { const onItemClick = jest.fn(); const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header')!); expect(onItemClick).toHaveBeenCalled(); @@ -695,11 +852,17 @@ describe('collapse', () => { it('onItemClick should not work when collapsible is disabled', () => { const onItemClick = jest.fn(); const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header')!); expect(onItemClick).not.toHaveBeenCalled(); @@ -707,11 +870,16 @@ describe('collapse', () => { it('panel style should work', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-item')).toHaveStyle({ color: 'red' }); }); From 2e300a53b6cc54436acdd7807355e25c37cadc87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com> Date: Sat, 4 Jan 2025 22:46:33 +0800 Subject: [PATCH 2/4] fix: lint fix --- src/Collapse.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 333e593..6d2e2f3 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -22,7 +22,6 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children, collapsible, openMotion, expandIcon, From 3fa129b22f088c91a18a9a146d0c298907b65085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com> Date: Sat, 4 Jan 2025 22:55:44 +0800 Subject: [PATCH 3/4] chore: update demo --- docs/examples/custom-icon.tsx | 83 +++++++++++++++++++----------- docs/examples/fragment.tsx | 23 --------- docs/examples/simple.tsx | 96 +++++++++++++++++++++-------------- 3 files changed, 112 insertions(+), 90 deletions(-) delete mode 100644 docs/examples/fragment.tsx diff --git a/docs/examples/custom-icon.tsx b/docs/examples/custom-icon.tsx index ea3df8e..9cbb557 100644 --- a/docs/examples/custom-icon.tsx +++ b/docs/examples/custom-icon.tsx @@ -1,4 +1,4 @@ -import Collapse, { Panel } from 'rc-collapse'; +import Collapse from 'rc-collapse'; import * as React from 'react'; import '../../assets/index.less'; import motion from './_util/motionUtil'; @@ -48,32 +48,56 @@ const App: React.FC = () => { const time = random(); - const panelItems = Array.from({ length: initLength }, (_, i) => { - const key = i + 1; - return ( - -

{text.repeat(time)}

-
- ); - }).concat( - - - -

{text}

-
-
-
, - - - -
- - -
-
-
-
, - ); + const items = [ + ...Array.from({ length: initLength }, (_, i) => { + const key = i + 1; + return { + key, + label: `This is panel header ${key}`, + children:

{text.repeat(time)}

, + }; + }), + { + key: initLength + 1, + label: `This is panel header ${initLength + 1}`, + children: ( + {text}

, + }, + ]} + /> + ), + }, + { + key: initLength + 2, + label: `This is panel header ${initLength + 2}`, + children: ( + + + + + ), + }, + ]} + /> + ), + }, + ]; const tools = ( <> @@ -104,9 +128,8 @@ const App: React.FC = () => { activeKey={activeKey} expandIcon={expandIcon} openMotion={motion} - > - {panelItems} - + items={items} + /> ); }; diff --git a/docs/examples/fragment.tsx b/docs/examples/fragment.tsx deleted file mode 100644 index bb38c5b..0000000 --- a/docs/examples/fragment.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import Collapse, { Panel } from 'rc-collapse'; -import * as React from 'react'; -import { Fragment } from 'react'; -import '../../assets/index.less'; - -const App = () => ( - - content - content - - content - content - - - - content - content - - - -); - -export default App; diff --git a/docs/examples/simple.tsx b/docs/examples/simple.tsx index 5353415..411dbd4 100644 --- a/docs/examples/simple.tsx +++ b/docs/examples/simple.tsx @@ -1,5 +1,5 @@ import type { CollapseProps } from 'rc-collapse'; -import Collapse, { Panel } from 'rc-collapse'; +import Collapse from 'rc-collapse'; import * as React from 'react'; import '../../assets/index.less'; import motion from './_util/motionUtil'; @@ -50,39 +50,62 @@ const App: React.FC = () => { const time = random(); - const panelItems = Array.from({ length: initLength }, (_, i) => { - const key = i + 1; - return ( - -

{text.repeat(time)}

-
- ); - }).concat( - - - -

{text}

-
-
-
, - - - -
- - -
-
-
-
, - Extra Node} - > -

Panel with extra

-
, - ); + const items: CollapseProps['items'] = [ + ...Array.from({ length: initLength }, (_, i) => { + const key = i + 1; + return { + key, + label: `This is panel header ${key}`, + children:

{text.repeat(time)}

, + }; + }), + { + key: initLength + 1, + label: `This is panel header ${initLength + 1}`, + children: ( + {text}

, + }, + ]} + /> + ), + }, + { + key: initLength + 2, + label: `This is panel header ${initLength + 2}`, + children: ( + + + + + ), + }, + ]} + /> + ), + }, + { + key: initLength + 3, + label: `This is panel header ${initLength + 3}`, + extra: Extra Node, + children:

Panel with extra

, + }, + ]; const handleCollapsibleChange = (e: React.ChangeEvent) => { const values = [undefined, 'header', 'icon', 'disabled']; @@ -129,9 +152,8 @@ const App: React.FC = () => { expandIcon={expandIcon} openMotion={motion} collapsible={collapsible} - > - {panelItems} -
+ items={items} + /> ); }; From 53ad35976ddb43536a27388b2686c7a69ae12e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E8=B1=AA?= <1844749591@qq.com> Date: Sat, 4 Jan 2025 22:57:20 +0800 Subject: [PATCH 4/4] chore: update demo --- docs/demo/fragment.md | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 docs/demo/fragment.md diff --git a/docs/demo/fragment.md b/docs/demo/fragment.md deleted file mode 100644 index de859a4..0000000 --- a/docs/demo/fragment.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: fragment -nav: - title: Demo - path: /demo ---- - -