From 827ffc213130743df110bb9606e5c0a9b12e9352 Mon Sep 17 00:00:00 2001 From: wuxh Date: Wed, 29 Mar 2023 21:14:22 +0800 Subject: [PATCH 1/5] docs: update docs (cherry picked from commit c7a37b97d2881d31f7ba31b39a2639a7b2ffb93f) --- docs/demo/fragment.md | 8 ---- docs/examples/custom-icon.tsx | 77 +++++++++++++++++++----------- docs/examples/fragment.tsx | 23 --------- docs/examples/simple.tsx | 90 ++++++++++++++++++++++------------- 4 files changed, 106 insertions(+), 92 deletions(-) delete mode 100644 docs/demo/fragment.md delete mode 100644 docs/examples/fragment.tsx diff --git a/docs/demo/fragment.md b/docs/demo/fragment.md deleted file mode 100644 index de859a41..00000000 --- a/docs/demo/fragment.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: fragment -nav: - title: Demo - path: /demo ---- - - diff --git a/docs/examples/custom-icon.tsx b/docs/examples/custom-icon.tsx index ea3df8ea..96b24f73 100644 --- a/docs/examples/custom-icon.tsx +++ b/docs/examples/custom-icon.tsx @@ -1,6 +1,7 @@ -import Collapse, { Panel } from 'rc-collapse'; +import Collapse from 'rc-collapse'; import * as React from 'react'; import '../../assets/index.less'; +import type { ItemType } from '../../src/interface'; import motion from './_util/motionUtil'; const initLength = 3; @@ -48,31 +49,54 @@ const App: React.FC = () => { const time = random(); - const panelItems = Array.from({ length: initLength }, (_, i) => { + const panelItems = Array.from({ length: initLength }, (_, i) => { const key = i + 1; - return ( - -

{text.repeat(time)}

-
- ); + return { + key, + header: `This is panel header ${key}`, + children:

{text.repeat(time)}

, + }; }).concat( - - - -

{text}

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

, + }, + ]} + /> + ), + }, + { + key: initLength + 2, + header: `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={panelItems} + /> ); }; diff --git a/docs/examples/fragment.tsx b/docs/examples/fragment.tsx deleted file mode 100644 index bb38c5bd..00000000 --- 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 53534150..2201d43b 100644 --- a/docs/examples/simple.tsx +++ b/docs/examples/simple.tsx @@ -1,7 +1,8 @@ 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 type { ItemType } from '../../src/interface'; import motion from './_util/motionUtil'; const initLength = 3; @@ -50,38 +51,60 @@ const App: React.FC = () => { const time = random(); - const panelItems = Array.from({ length: initLength }, (_, i) => { + const panelItems = Array.from({ length: initLength }, (_, i) => { const key = i + 1; - return ( - -

{text.repeat(time)}

-
- ); + return { + key, + header: `This is panel header ${key}`, + children:

{text.repeat(time)}

, + }; }).concat( - - - -

{text}

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

Panel with extra

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

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

Panel with extra

, + }, ); const handleCollapsibleChange = (e: React.ChangeEvent) => { @@ -129,9 +152,8 @@ const App: React.FC = () => { expandIcon={expandIcon} openMotion={motion} collapsible={collapsible} - > - {panelItems} -
+ items={panelItems} + /> ); }; From aecd0c96da27fe96527b9b630431f98e270f2d48 Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 17:45:56 +0800 Subject: [PATCH 2/5] test: remove `` because deprecated (cherry picked from commit 3ed317517ea3ee52ed65cb7f99cb3f38ad93d688) # Conflicts: # src/Collapse.tsx # tests/index.spec.tsx --- src/Collapse.tsx | 60 +++- src/index.tsx | 10 +- tests/index.spec.tsx | 643 ++++++++++++++++++++++++------------------- 3 files changed, 411 insertions(+), 302 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 36267d29..fd21619b 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,6 +1,5 @@ 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'; @@ -23,7 +22,6 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children, collapsible, openMotion, expandIcon, @@ -74,6 +72,57 @@ const Collapse = React.forwardRef((props, ref) => activeKey, }); + // 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, + expandIcon: rawExpandIcon = expandIcon, + ...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} + + ); + }); + // ======================== Render ======================== return (
((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/index.tsx b/src/index.tsx index 8e963c68..87e21e7e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,2 @@ -import Collapse from './Collapse'; - +export { default } 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 533df08b..6ca2c0ef 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,8 +1,8 @@ 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 React from 'react'; +import Collapse from '../src/index'; import type { CollapseProps, ItemType } from '../src/interface'; describe('collapse', () => { @@ -24,6 +24,27 @@ describe('collapse', () => { } } + const items: ItemType[] = [ + { + key: '1', + header: 'collapse 1', + collapsible: 'disabled', + children: 'first', + }, + { + key: '2', + header: 'collapse 2', + extra: ExtraSpan, + children: 'second', + }, + { + key: '3', + header: 'collapse 3', + className: 'important', + children: 'third', + }, + ]; + function runNormalTest(element: any) { let collapse: RenderResult; @@ -115,21 +136,7 @@ describe('collapse', () => { describe('collapse', () => { const expandIcon = () => test{'>'}; - const element = ( - - - first - - ExtraSpan}> - second - - - third - - - ); - - runNormalTest(element); + runNormalTest(); it('controlled', () => { const onChangeSpy = jest.fn(); @@ -143,17 +150,27 @@ describe('collapse', () => { }; return ( - - - first - - - second - - - third - - + ); }; @@ -170,31 +187,32 @@ describe('collapse', () => { }); describe('it should support number key', () => { - const expandIcon = () => test{'>'}; - const element = ( - - - first - - ExtraSpan}> - second - - - third - - + runNormalTest( + test{'>'}} + items={items.map((item) => ({ + ...item, + key: Number(item.key), + }))} + />, ); - - runNormalTest(element); }); it('shoule support extra whit number 0', () => { const { container } = render( - - - zero - - , + , ); const extraNodes = container.querySelectorAll('.rc-collapse-extra'); @@ -204,17 +222,14 @@ describe('collapse', () => { it('should support activeKey number 0', () => { const { container } = render( - - - zero - - - first - - - second - - , + ({ + ...item, + key: index, + }))} + />, ); // activeKey number 0, should open one item @@ -223,17 +238,7 @@ describe('collapse', () => { it('click should toggle panel state', () => { const { container } = render( - - - first - - - second - - - third - - , + , ); const header = container.querySelectorAll('.rc-collapse-header')?.[1]; @@ -247,11 +252,29 @@ describe('collapse', () => { let collapse: RenderResult; beforeEach(() => { - collapse = render(element); - }); - - afterEach(() => { - collapse.unmount(); + collapse = render( + , + ); }); it('accordion content, should default open zero item', () => { @@ -309,22 +332,6 @@ describe('collapse', () => { expect(item).toBeTruthy(); expect(item!.getAttribute('role')).toBe('tabpanel'); }); - } - - describe('prop: accordion', () => { - runAccordionTest( - - - first - - - second - - - third - - , - ); }); describe('forceRender', () => { @@ -344,28 +351,42 @@ describe('collapse', () => { 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(); @@ -388,17 +409,26 @@ describe('collapse', () => { }; const { container } = render( - - - first - - - second - - - second - - , + , ); fireEvent.keyPress(container.querySelectorAll('.rc-collapse-header')?.[2], myKeyEvent); @@ -423,46 +453,57 @@ describe('collapse', () => { ); }); - describe('wrapped in Fragment', () => { - const expandIcon = () => test{'>'}; - const element = ( - - - - first - - ExtraSpan}> - second - - - - third - - - - - ); - - runNormalTest(element); - }); + // TODO: 移除 Panel 后需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 by @Wxh16144 + // describe.skip('wrapped in Fragment', () => { + // const expandIcon = () => test{'>'}; + // const element = ( + // + // + // + // first + // + // ExtraSpan}> + // second + // + // + // + // third + // + // + // + // + // ); + + // runNormalTest(element); + // }); it('should support return null icon', () => { const { container } = render( - null}> - - first - - , + null} + items={[ + { + key: '1', + header: 'title', + children: 'first', + }, + ]} + />, ); expect(container.querySelector('.rc-collapse-header')?.childNodes).toHaveLength(1); }); it('should support custom child', () => { const { container } = render( - - - first - + custom-child , ); @@ -472,44 +513,49 @@ describe('collapse', () => { // 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 - - , - ); - - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); - expect(container.querySelector('.rc-collapse-content')).toHaveClass( - 'rc-collapse-content-active', - ); - expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); - expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( - 1, - ); - fireEvent.click(container.querySelector('.rc-collapse-header')!); - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); - expect(container.querySelector('.rc-collapse-content')).toHaveClass( - 'rc-collapse-content-inactive', - ); - }); + // TODO: 移除 Panel 后需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 by @Wxh16144 + // it.skip('should support custom component', () => { + // const PanelElement = (props) => ( + // + //

test

+ //
+ // ); + // const { container } = render( + // + // + // + // second + // + // , + // ); + + // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); + // expect(container.querySelector('.rc-collapse-content')).toHaveClass( + // 'rc-collapse-content-active', + // ); + // expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); + // expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( + // 1, + // ); + // fireEvent.click(container.querySelector('.rc-collapse-header')!); + // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); + // expect(container.querySelector('.rc-collapse-content')).toHaveClass( + // 'rc-collapse-content-inactive', + // ); + // }); describe('prop: collapsible', () => { it('default', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); @@ -517,11 +563,16 @@ describe('collapse', () => { }); 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')!); @@ -531,11 +582,16 @@ describe('collapse', () => { }); 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')!); @@ -546,11 +602,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); @@ -560,11 +621,17 @@ describe('collapse', () => { it('the value of panel should be read first', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); @@ -576,11 +643,16 @@ describe('collapse', () => { it('icon trigger when collapsible equal header', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header .arrow')!); @@ -589,11 +661,16 @@ describe('collapse', () => { it('header not trigger when collapsible equal icon', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header-text')!); @@ -603,11 +680,16 @@ describe('collapse', () => { it('!showArrow', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelectorAll('.rc-collapse-expand-icon')).toHaveLength(0); @@ -616,46 +698,38 @@ describe('collapse', () => { it('Panel container dom can set event handler', () => { const clickHandler = jest.fn(); const { container } = render( - - -
Click this
-
-
, + Click this
, + }, + ]} + />, ); 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')); @@ -665,11 +739,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(); @@ -678,11 +757,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(); @@ -690,37 +775,25 @@ describe('collapse', () => { it('panel style should work', () => { const { container } = render( - - - first - - , + , + ); + + expect(window.getComputedStyle(container.querySelector('.rc-collapse-item'))).toHaveProperty( + 'color', + 'red', ); - expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); }); describe('props items', () => { - const items: ItemType[] = [ - { - key: '1', - label: 'collapse 1', - children: 'first', - collapsible: 'disabled', - }, - { - key: '2', - label: 'collapse 2', - children: 'second', - extra: ExtraSpan, - }, - { - key: '3', - label: 'collapse 3', - className: 'important', - children: 'third', - }, - ]; - runNormalTest( test{'>'}} items={items} />, ); From e3025c4f79f3101889628e10c0cafa8cee59f44b Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 17:46:30 +0800 Subject: [PATCH 3/5] Revert "test: remove `` because deprecated" This reverts commit aecd0c96da27fe96527b9b630431f98e270f2d48. --- src/Collapse.tsx | 60 +--- src/index.tsx | 10 +- tests/index.spec.tsx | 643 +++++++++++++++++++------------------------ 3 files changed, 302 insertions(+), 411 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index fd21619b..36267d29 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,5 +1,6 @@ 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'; @@ -22,6 +23,7 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, + children, collapsible, openMotion, expandIcon, @@ -72,57 +74,6 @@ const Collapse = React.forwardRef((props, ref) => activeKey, }); - // 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, - expandIcon: rawExpandIcon = expandIcon, - ...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} - - ); - }); - // ======================== Render ======================== return (
((props, ref) => ); }); -export default Collapse; +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 87e21e7e..8e963c68 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,2 +1,10 @@ -export { default } from './Collapse'; +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 6ca2c0ef..533df08b 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,8 +1,8 @@ import type { RenderResult } from '@testing-library/react'; import { fireEvent, render } from '@testing-library/react'; import KeyCode from 'rc-util/lib/KeyCode'; -import React from 'react'; -import Collapse from '../src/index'; +import React, { Fragment } from 'react'; +import Collapse, { Panel } from '../src/index'; import type { CollapseProps, ItemType } from '../src/interface'; describe('collapse', () => { @@ -24,27 +24,6 @@ describe('collapse', () => { } } - const items: ItemType[] = [ - { - key: '1', - header: 'collapse 1', - collapsible: 'disabled', - children: 'first', - }, - { - key: '2', - header: 'collapse 2', - extra: ExtraSpan, - children: 'second', - }, - { - key: '3', - header: 'collapse 3', - className: 'important', - children: 'third', - }, - ]; - function runNormalTest(element: any) { let collapse: RenderResult; @@ -136,7 +115,21 @@ describe('collapse', () => { describe('collapse', () => { const expandIcon = () => test{'>'}; - runNormalTest(); + const element = ( + + + first + + ExtraSpan}> + second + + + third + + + ); + + runNormalTest(element); it('controlled', () => { const onChangeSpy = jest.fn(); @@ -150,27 +143,17 @@ describe('collapse', () => { }; return ( - + + + first + + + second + + + third + + ); }; @@ -187,32 +170,31 @@ describe('collapse', () => { }); describe('it should support number key', () => { - runNormalTest( - test{'>'}} - items={items.map((item) => ({ - ...item, - key: Number(item.key), - }))} - />, + const expandIcon = () => test{'>'}; + const element = ( + + + first + + ExtraSpan}> + second + + + third + + ); + + runNormalTest(element); }); it('shoule support extra whit number 0', () => { const { container } = render( - , + + + zero + + , ); const extraNodes = container.querySelectorAll('.rc-collapse-extra'); @@ -222,14 +204,17 @@ describe('collapse', () => { it('should support activeKey number 0', () => { const { container } = render( - ({ - ...item, - key: index, - }))} - />, + + + zero + + + first + + + second + + , ); // activeKey number 0, should open one item @@ -238,7 +223,17 @@ describe('collapse', () => { it('click should toggle panel state', () => { const { container } = render( - , + + + first + + + second + + + third + + , ); const header = container.querySelectorAll('.rc-collapse-header')?.[1]; @@ -252,29 +247,11 @@ describe('collapse', () => { let collapse: RenderResult; beforeEach(() => { - collapse = render( - , - ); + collapse = render(element); + }); + + afterEach(() => { + collapse.unmount(); }); it('accordion content, should default open zero item', () => { @@ -332,6 +309,22 @@ describe('collapse', () => { expect(item).toBeTruthy(); expect(item!.getAttribute('role')).toBe('tabpanel'); }); + } + + describe('prop: accordion', () => { + runAccordionTest( + + + first + + + second + + + third + + , + ); }); describe('forceRender', () => { @@ -351,42 +344,28 @@ describe('collapse', () => { 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(); @@ -409,26 +388,17 @@ describe('collapse', () => { }; const { container } = render( - , + + + first + + + second + + + second + + , ); fireEvent.keyPress(container.querySelectorAll('.rc-collapse-header')?.[2], myKeyEvent); @@ -453,57 +423,46 @@ describe('collapse', () => { ); }); - // TODO: 移除 Panel 后需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 by @Wxh16144 - // describe.skip('wrapped in Fragment', () => { - // const expandIcon = () => test{'>'}; - // const element = ( - // - // - // - // first - // - // ExtraSpan}> - // second - // - // - // - // third - // - // - // - // - // ); - - // runNormalTest(element); - // }); + describe('wrapped in Fragment', () => { + const expandIcon = () => test{'>'}; + const element = ( + + + + first + + ExtraSpan}> + second + + + + third + + + + + ); + + runNormalTest(element); + }); it('should support return null icon', () => { const { container } = render( - null} - items={[ - { - key: '1', - header: 'title', - children: 'first', - }, - ]} - />, + null}> + + first + + , ); expect(container.querySelector('.rc-collapse-header')?.childNodes).toHaveLength(1); }); it('should support custom child', () => { const { container } = render( - + + + first + custom-child , ); @@ -513,49 +472,44 @@ describe('collapse', () => { // 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 - // TODO: 移除 Panel 后需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 by @Wxh16144 - // it.skip('should support custom component', () => { - // const PanelElement = (props) => ( - // - //

test

- //
- // ); - // const { container } = render( - // - // - // - // second - // - // , - // ); - - // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); - // expect(container.querySelector('.rc-collapse-content')).toHaveClass( - // 'rc-collapse-content-active', - // ); - // expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); - // expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( - // 1, - // ); - // fireEvent.click(container.querySelector('.rc-collapse-header')!); - // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); - // expect(container.querySelector('.rc-collapse-content')).toHaveClass( - // 'rc-collapse-content-inactive', - // ); - // }); + it('should support custom component', () => { + const PanelElement = (props) => ( + +

test

+
+ ); + const { container } = render( + + + + second + + , + ); + + expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); + expect(container.querySelector('.rc-collapse-content')).toHaveClass( + 'rc-collapse-content-active', + ); + expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); + expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( + 1, + ); + fireEvent.click(container.querySelector('.rc-collapse-header')!); + expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); + expect(container.querySelector('.rc-collapse-content')).toHaveClass( + 'rc-collapse-content-inactive', + ); + }); describe('prop: collapsible', () => { it('default', () => { const { container } = render( - , + + + first + + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); @@ -563,16 +517,11 @@ describe('collapse', () => { }); 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')!); @@ -582,16 +531,11 @@ describe('collapse', () => { }); 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')!); @@ -602,16 +546,11 @@ 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); @@ -621,17 +560,11 @@ describe('collapse', () => { it('the value of panel should be read first', () => { const { container } = render( - , + + + first + + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); @@ -643,16 +576,11 @@ describe('collapse', () => { it('icon trigger when collapsible equal header', () => { const { container } = render( - , + + + first + + , ); fireEvent.click(container.querySelector('.rc-collapse-header .arrow')!); @@ -661,16 +589,11 @@ describe('collapse', () => { it('header not trigger when collapsible equal icon', () => { const { container } = render( - , + + + first + + , ); fireEvent.click(container.querySelector('.rc-collapse-header-text')!); @@ -680,16 +603,11 @@ describe('collapse', () => { it('!showArrow', () => { const { container } = render( - , + + + first + + , ); expect(container.querySelectorAll('.rc-collapse-expand-icon')).toHaveLength(0); @@ -698,38 +616,46 @@ describe('collapse', () => { it('Panel container dom can set event handler', () => { const clickHandler = jest.fn(); const { container } = render( - Click this
, - }, - ]} - />, + + +
Click this
+
+
, ); 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')); @@ -739,16 +665,11 @@ describe('collapse', () => { it('onItemClick should work', () => { const onItemClick = jest.fn(); const { container } = render( - , + + + first + + , ); fireEvent.click(container.querySelector('.rc-collapse-header')!); expect(onItemClick).toHaveBeenCalled(); @@ -757,17 +678,11 @@ 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(); @@ -775,25 +690,37 @@ describe('collapse', () => { it('panel style should work', () => { const { container } = render( - , - ); - - expect(window.getComputedStyle(container.querySelector('.rc-collapse-item'))).toHaveProperty( - 'color', - 'red', + + + first + + , ); + expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); }); describe('props items', () => { + const items: ItemType[] = [ + { + key: '1', + label: 'collapse 1', + children: 'first', + collapsible: 'disabled', + }, + { + key: '2', + label: 'collapse 2', + children: 'second', + extra: ExtraSpan, + }, + { + key: '3', + label: 'collapse 3', + className: 'important', + children: 'third', + }, + ]; + runNormalTest( test{'>'}} items={items} />, ); From fb3888aa9262e26ff09b26e3f7b24be56c15f79c Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 17:50:07 +0800 Subject: [PATCH 4/5] refactor: remove deprecated --- src/Collapse.tsx | 9 +---- src/hooks/useItems.tsx | 87 +----------------------------------------- src/index.tsx | 10 +---- 3 files changed, 4 insertions(+), 102 deletions(-) diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 36267d29..52519ef9 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -1,6 +1,5 @@ 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'; @@ -23,7 +22,6 @@ const Collapse = React.forwardRef((props, ref) => style, accordion, className, - children, collapsible, openMotion, expandIcon, @@ -58,12 +56,7 @@ const Collapse = React.forwardRef((props, ref) => }); // ======================== Children ======================== - warning( - !children, - '`children` will be removed in next major version. Please use `items` instead.', - ); - - const mergedChildren = useItems(items, children, { + const mergedChildren = useItems(items, { prefixCls, accordion, openMotion, diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index 2bc5db7b..cc39737b 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'; @@ -31,8 +30,6 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { ...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; @@ -71,92 +68,12 @@ 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[] { +function useItems(items?: ItemType[], props?: Props) { if (Array.isArray(items)) { return convertItemsToNodes(items, props); } - return toArray(rawChildren).map((child, index) => getNewChild(child, index, props)); + return null; } export default useItems; diff --git a/src/index.tsx b/src/index.tsx index 8e963c68..87e21e7e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,2 @@ -import Collapse from './Collapse'; - +export { default } 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; From 2664cb0b7fbc408d502d50e731501e21ed764d71 Mon Sep 17 00:00:00 2001 From: wuxh Date: Fri, 14 Apr 2023 18:08:49 +0800 Subject: [PATCH 5/5] test: update test --- tests/index.spec.tsx | 670 +++++++++++++++++++++++-------------------- 1 file changed, 365 insertions(+), 305 deletions(-) diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 533df08b..cd2a5097 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,8 +1,8 @@ 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 React from 'react'; +import Collapse from '../src/index'; import type { CollapseProps, ItemType } from '../src/interface'; describe('collapse', () => { @@ -24,6 +24,27 @@ describe('collapse', () => { } } + const items: ItemType[] = [ + { + key: '1', + label: 'collapse 1', + collapsible: 'disabled', + children: 'first', + }, + { + key: '2', + label: 'collapse 2', + extra: ExtraSpan, + children: 'second', + }, + { + key: '3', + label: 'collapse 3', + className: 'important', + children: 'third', + }, + ]; + function runNormalTest(element: any) { let collapse: RenderResult; @@ -115,21 +136,7 @@ describe('collapse', () => { describe('collapse', () => { const expandIcon = () => test{'>'}; - const element = ( - - - first - - ExtraSpan}> - second - - - third - - - ); - - runNormalTest(element); + runNormalTest(); it('controlled', () => { const onChangeSpy = jest.fn(); @@ -143,17 +150,27 @@ describe('collapse', () => { }; return ( - - - first - - - second - - - third - - + ); }; @@ -170,31 +187,32 @@ describe('collapse', () => { }); describe('it should support number key', () => { - const expandIcon = () => test{'>'}; - const element = ( - - - first - - ExtraSpan}> - second - - - third - - + runNormalTest( + test{'>'}} + items={items.map((item) => ({ + ...item, + key: Number(item.key), + }))} + />, ); - - runNormalTest(element); }); it('shoule support extra whit number 0', () => { const { container } = render( - - - zero - - , + , ); const extraNodes = container.querySelectorAll('.rc-collapse-extra'); @@ -204,17 +222,14 @@ describe('collapse', () => { it('should support activeKey number 0', () => { const { container } = render( - - - zero - - - first - - - second - - , + ({ + ...item, + key: index, + }))} + />, ); // activeKey number 0, should open one item @@ -223,17 +238,7 @@ describe('collapse', () => { it('click should toggle panel state', () => { const { container } = render( - - - first - - - second - - - third - - , + , ); const header = container.querySelectorAll('.rc-collapse-header')?.[1]; @@ -313,59 +318,89 @@ 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(); @@ -388,17 +423,26 @@ describe('collapse', () => { }; const { container } = render( - - - first - - - second - - - second - - , + , ); fireEvent.keyPress(container.querySelectorAll('.rc-collapse-header')?.[2], myKeyEvent); @@ -423,46 +467,59 @@ describe('collapse', () => { ); }); - describe('wrapped in Fragment', () => { - const expandIcon = () => test{'>'}; - const element = ( - - - - first - - ExtraSpan}> - second - - - - third - - - - - ); - - runNormalTest(element); - }); + // TODO: 移除 Panel 后需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 assignee: @wxh16144 + // describe('wrapped in Fragment', () => { + // const expandIcon = () => test{'>'}; + // const element = ( + // + // + // + // first + // + // ExtraSpan}> + // second + // + // + // + // third + // + // + // + // + // ); + + // runNormalTest(element); + // }); 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', () => { + // TODO: 需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 assignee: @wxh16144 + // 按理说我们不允许用户这样用,但是为了兼容性,我们还是保留这个测试 + it.skip('should support custom child', () => { const { container } = render( - - - first - + custom-child , ); @@ -472,44 +529,49 @@ describe('collapse', () => { // 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 - - , - ); - - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); - expect(container.querySelector('.rc-collapse-content')).toHaveClass( - 'rc-collapse-content-active', - ); - expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); - expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( - 1, - ); - fireEvent.click(container.querySelector('.rc-collapse-header')!); - expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); - expect(container.querySelector('.rc-collapse-content')).toHaveClass( - 'rc-collapse-content-inactive', - ); - }); + // TODO: 需要在 Ant Design 里面测试,待 Ant Design 稳定后再移除 assignee: @wxh16144 + // it('should support custom component', () => { + // const PanelElement = (props) => ( + // + //

test

+ //
+ // ); + // const { container } = render( + // + // + // + // second + // + // , + // ); + + // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(1); + // expect(container.querySelector('.rc-collapse-content')).toHaveClass( + // 'rc-collapse-content-active', + // ); + // expect(container.querySelector('.rc-collapse-header')?.textContent).toBe('collapse 1'); + // expect(container.querySelector('.rc-collapse-header')?.querySelectorAll('.arrow')).toHaveLength( + // 1, + // ); + // fireEvent.click(container.querySelector('.rc-collapse-header')!); + // expect(container.querySelectorAll('.rc-collapse-content-active')).toHaveLength(0); + // expect(container.querySelector('.rc-collapse-content')).toHaveClass( + // 'rc-collapse-content-inactive', + // ); + // }); describe('prop: collapsible', () => { it('default', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); fireEvent.click(container.querySelector('.rc-collapse-header')!); @@ -517,11 +579,16 @@ describe('collapse', () => { }); 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')!); @@ -531,11 +598,16 @@ describe('collapse', () => { }); 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')!); @@ -546,11 +618,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); @@ -560,11 +637,17 @@ describe('collapse', () => { it('the value of panel should be read first', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelector('.rc-collapse-header-text')).toBeTruthy(); @@ -576,11 +659,16 @@ describe('collapse', () => { it('icon trigger when collapsible equal header', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header .arrow')!); @@ -589,11 +677,16 @@ describe('collapse', () => { it('header not trigger when collapsible equal icon', () => { const { container } = render( - - - first - - , + , ); fireEvent.click(container.querySelector('.rc-collapse-header-text')!); @@ -603,11 +696,16 @@ describe('collapse', () => { it('!showArrow', () => { const { container } = render( - - - first - - , + , ); expect(container.querySelectorAll('.rc-collapse-expand-icon')).toHaveLength(0); @@ -616,46 +714,38 @@ describe('collapse', () => { it('Panel container dom can set event handler', () => { const clickHandler = jest.fn(); const { container } = render( - - -
Click this
-
-
, + Click this, + }, + ]} + />, ); 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')); @@ -665,11 +755,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(); @@ -678,11 +773,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(); @@ -690,65 +791,24 @@ describe('collapse', () => { it('panel style should work', () => { const { container } = render( - - - first - - , - ); - expect(container.querySelector('.rc-collapse-item').style.color).toBe('red'); - }); - - describe('props items', () => { - const items: ItemType[] = [ - { - key: '1', - label: 'collapse 1', - children: 'first', - collapsible: 'disabled', - }, - { - key: '2', - label: 'collapse 2', - children: 'second', - extra: ExtraSpan, - }, - { - key: '3', - label: 'collapse 3', - className: 'important', - children: 'third', - }, - ]; - - runNormalTest( - test{'>'}} items={items} />, - ); - - runAccordionTest( , ); + expect(window.getComputedStyle(container.querySelector('.rc-collapse-item'))).toHaveProperty( + 'color', + 'red', + ); + }); + describe('props items', () => { it('should work with onItemClick', () => { const onItemClick = jest.fn(); const { container } = render(