From 449d3bb623dd217464cc6c9573cceef93ecec931 Mon Sep 17 00:00:00 2001 From: z1399 Date: Mon, 12 Jul 2021 14:56:25 +0800 Subject: [PATCH 1/5] feat: add api to change visibility of popup overlay when finishing cascader select --- README.md | 6 ++++++ src/Cascader.tsx | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85536464..81e487df 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,12 @@ React.render( > specific the default loading icon + + popupVisibleAfterSelect + Boolean + >false + visibility of popup overlay when finishing cascader select + diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 37360c84..e9d282e0 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -50,6 +50,7 @@ export interface CascaderProps extends Pick { filedNames?: CascaderFieldNames; // typo but for compatibility expandIcon?: React.ReactNode; loadingIcon?: React.ReactNode; + popupVisibleAfterSelect?: boolean; } interface CascaderState { @@ -103,6 +104,7 @@ class Cascader extends React.Component { expandTrigger: 'click', fieldNames: { label: 'label', value: 'value', children: 'children' }, expandIcon: '>', + popupVisibleAfterSelect: false, }; static getDerivedStateFromProps(nextProps: CascaderProps, prevState: CascaderState) { @@ -190,7 +192,8 @@ class Cascader extends React.Component { options.map((o) => o[this.getFieldName('value')]), options, ); - this.setPopupVisible(visible); + const { popupVisibleAfterSelect } = this.props; + this.setPopupVisible(visible || popupVisibleAfterSelect); } }; From 7e418ac56e7b729ede0e7421b1b6e8f837417016 Mon Sep 17 00:00:00 2001 From: z1399 Date: Mon, 12 Jul 2021 19:30:14 +0800 Subject: [PATCH 2/5] improve: Change the API name from popupVisibleAfterSelect to hidePopupOnSelect --- README.md | 6 +++--- src/Cascader.tsx | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 81e487df..8e1fece3 100644 --- a/README.md +++ b/README.md @@ -209,10 +209,10 @@ React.render( specific the default loading icon - popupVisibleAfterSelect + hidePopupOnSelect Boolean - >false - visibility of popup overlay when finishing cascader select + >true + hide popup on select diff --git a/src/Cascader.tsx b/src/Cascader.tsx index e9d282e0..cecfd53c 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -50,7 +50,7 @@ export interface CascaderProps extends Pick { filedNames?: CascaderFieldNames; // typo but for compatibility expandIcon?: React.ReactNode; loadingIcon?: React.ReactNode; - popupVisibleAfterSelect?: boolean; + hidePopupOnSelect?: boolean; } interface CascaderState { @@ -104,7 +104,7 @@ class Cascader extends React.Component { expandTrigger: 'click', fieldNames: { label: 'label', value: 'value', children: 'children' }, expandIcon: '>', - popupVisibleAfterSelect: false, + hidePopupOnSelect: true, }; static getDerivedStateFromProps(nextProps: CascaderProps, prevState: CascaderState) { @@ -192,8 +192,7 @@ class Cascader extends React.Component { options.map((o) => o[this.getFieldName('value')]), options, ); - const { popupVisibleAfterSelect } = this.props; - this.setPopupVisible(visible || popupVisibleAfterSelect); + this.setPopupVisible(visible); } }; @@ -211,7 +210,7 @@ class Cascader extends React.Component { if (triggerNode && triggerNode.focus) { triggerNode.focus(); } - const { changeOnSelect, loadData, expandTrigger } = this.props; + const { changeOnSelect, loadData, expandTrigger, hidePopupOnSelect } = this.props; if (!targetOption || targetOption.disabled) { return; } @@ -232,13 +231,13 @@ class Cascader extends React.Component { !targetOption[this.getFieldName('children')] || !targetOption[this.getFieldName('children')].length ) { - this.handleChange(activeOptions, { visible: false }, e); + this.handleChange(activeOptions, { visible: !hidePopupOnSelect }, e); // set value to activeValue when select leaf option newState.value = activeValue; // add e.type judgement to prevent `onChange` being triggered by mouseEnter } else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) { if (expandTrigger === 'hover') { - this.handleChange(activeOptions, { visible: false }, e); + this.handleChange(activeOptions, { visible: !hidePopupOnSelect }, e); } else { this.handleChange(activeOptions, { visible: true }, e); } From 24835927d4bee43952edae900e7e870fe455dc7d Mon Sep 17 00:00:00 2001 From: z1399 Date: Tue, 13 Jul 2021 12:35:08 +0800 Subject: [PATCH 3/5] test: add test case of hidePopupOnSelect --- tests/__snapshots__/index.spec.js.snap | 1 + tests/index.spec.js | 34 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/tests/__snapshots__/index.spec.js.snap b/tests/__snapshots__/index.spec.js.snap index f0cd3253..da7f0379 100644 --- a/tests/__snapshots__/index.spec.js.snap +++ b/tests/__snapshots__/index.spec.js.snap @@ -147,6 +147,7 @@ exports[`Cascader should render custom dropdown correctly 1`] = ` "value": "value", } } + hidePopupOnSelect={true} onChange={[Function]} onItemDoubleClick={[Function]} onPopupVisibleChange={[Function]} diff --git a/tests/index.spec.js b/tests/index.spec.js index dfbd8789..7dea62b7 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -630,4 +630,38 @@ describe('Cascader', () => { const menus = wrapper.find('.rc-cascader-menus'); expect(menus).toMatchSnapshot(); }); + + it('should display after select, when hidePopupOnSelect is false', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + expect(menu1Items.length).toBe(3); + menu1Items.at(2).simulate('click'); + expect( + wrapper.find('.rc-cascader-menu-item').at(2).hasClass('rc-cascader-menu-item-active'), + ).toBe(true); + + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + expect(menu2Items.length).toBe(2); + + menu2Items.at(0).simulate('click'); + expect( + wrapper + .find('.rc-cascader-menu') + .at(1) + .find('.rc-cascader-menu-item') + .first() + .hasClass('rc-cascader-menu-item-active'), + ).toBe(true); + + expect(wrapper.state().popupVisible).toBeTruthy(); + }); }); From b55e983eca70c30afdcbfcf869aea7fdff2417b4 Mon Sep 17 00:00:00 2001 From: z1399 Date: Tue, 13 Jul 2021 14:20:32 +0800 Subject: [PATCH 4/5] test: update test case of hidePopupOnSelect --- tests/index.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/index.spec.js b/tests/index.spec.js index 7dea62b7..19627467 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -663,5 +663,20 @@ describe('Cascader', () => { ).toBe(true); expect(wrapper.state().popupVisible).toBeTruthy(); + wrapper.find('input').simulate('click'); + expect(wrapper.state().popupVisible).toBeFalsy(); + }); + + it('should toggle select panel when click it, even if hidePopupOnSelect is false', () => { + const wrapper = mount( + + + , + ); + expect(wrapper.state().popupVisible).toBeFalsy(); + wrapper.find('input').simulate('click'); + expect(wrapper.state().popupVisible).toBeTruthy(); + wrapper.find('input').simulate('click'); + expect(wrapper.state().popupVisible).toBeFalsy(); }); }); From 8921029ad7bb38ae4269e0a277f1aea51290c228 Mon Sep 17 00:00:00 2001 From: z1399 Date: Tue, 13 Jul 2021 14:56:19 +0800 Subject: [PATCH 5/5] test: detect a click outside --- tests/index.spec.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/index.spec.js b/tests/index.spec.js index 19627467..ded5f1e4 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -9,6 +9,21 @@ import { addressOptionsForFieldNames, } from './demoOptions'; +export const createDocumentListenersMock = () => { + const listeners = {}; + const handler = (domEl, event) => listeners?.[event]?.({ target: domEl }); + document.addEventListener = jest.fn((event, cb) => { + listeners[event] = cb; + }); + document.removeEventListener = jest.fn((event) => { + delete listeners[event]; + }); + return { + mouseDown: (domEl) => handler(domEl, 'mousedown'), + click: (domEl) => handler(domEl, 'click'), + }; +}; + describe('Cascader', () => { let selectedValue; const onChange = function onChange(value) { @@ -24,6 +39,8 @@ describe('Cascader', () => { jest.useRealTimers(); }); + const fireEvent = createDocumentListenersMock(); + it('should toggle select panel when click it', () => { const wrapper = mount( @@ -663,7 +680,7 @@ describe('Cascader', () => { ).toBe(true); expect(wrapper.state().popupVisible).toBeTruthy(); - wrapper.find('input').simulate('click'); + fireEvent.mouseDown(document.body); expect(wrapper.state().popupVisible).toBeFalsy(); });