From 4dd761770a6921880cc715169d4d2fc9a57e96cd Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Thu, 18 Jun 2020 10:57:03 +0800 Subject: [PATCH 01/10] fix: can't separate input when mode=tags and open=false --- examples/tags.tsx | 30 ++++++++++++++++++++- src/Selector/MultipleSelector.tsx | 2 +- tests/Multiple.test.tsx | 44 ++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/examples/tags.tsx b/examples/tags.tsx index 5e8d67af5..3e2763189 100644 --- a/examples/tags.tsx +++ b/examples/tags.tsx @@ -44,7 +44,7 @@ const Test: React.FC = () => { onDeselect={(val, option) => { console.log('deselected', val, option); }} - tokenSeparators={[' ', ',', '\n']} + tokenSeparators={[' ', ',']} onFocus={() => console.log('focus')} onBlur={() => console.log('blur')} > @@ -70,6 +70,34 @@ const Test: React.FC = () => { toggle maxTagCount (null)

+

tags select with open = false

+
+ +
); }; diff --git a/src/Selector/MultipleSelector.tsx b/src/Selector/MultipleSelector.tsx index 7e0b6b29f..43eea3a6f 100644 --- a/src/Selector/MultipleSelector.tsx +++ b/src/Selector/MultipleSelector.tsx @@ -74,7 +74,7 @@ const SelectSelector: React.FC = props => { }, []); // ===================== Search ====================== - const inputValue = open ? searchValue : ''; + const inputValue = open || mode === 'tags' ? searchValue : ''; const inputEditable: boolean = mode === 'tags' || (open && showSearch); // We measure width and set to the input immediately diff --git a/tests/Multiple.test.tsx b/tests/Multiple.test.tsx index 10bdbbd3c..bcb014495 100644 --- a/tests/Multiple.test.tsx +++ b/tests/Multiple.test.tsx @@ -79,7 +79,49 @@ describe('Select.Multiple', () => { expectOpen(wrapper, false); }); - it(`shouldn't separate words when compositing`, () => { + it('tokenize input when mode=tags and open=false', () => { + const handleChange = jest.fn(); + const handleSelect = jest.fn(); + const wrapper = mount( + , + ); + + wrapper.find('input').simulate('change', { + target: { + value: 'One', + }, + }); + expect(handleChange).not.toHaveBeenCalled(); + + handleChange.mockReset(); + wrapper.find('input').simulate('change', { + target: { + value: 'One,Two,Three,', + }, + }); + expect(handleChange).toHaveBeenCalledWith(['One', 'Two', 'Three'], expect.anything()); + + handleChange.mockReset(); + wrapper.find('input').simulate('change', { + target: { + value: 'One,Two,', + }, + }); + expect(handleChange).toHaveBeenCalledWith(['One', 'Two', 'Three'], expect.anything()); + + expect(wrapper.find('input').props().value).toBe(''); + }); + + it('shouldn\'t separate words when compositing', () => { const handleChange = jest.fn(); const handleSelect = jest.fn(); const wrapper = mount( From 6795251ca4dd5cecc82a6f02219f9bba052d9d73 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Thu, 18 Jun 2020 12:04:02 +0800 Subject: [PATCH 02/10] chore --- index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.js b/index.js index 3151c2a2a..fd4d7e5cf 100644 --- a/index.js +++ b/index.js @@ -1,3 +1 @@ -'use strict'; - module.exports = require('./src/'); From d87b0467e2c9e78cfe64ed528c50978681a7ddde Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Sun, 28 Jun 2020 15:19:02 +0800 Subject: [PATCH 03/10] fix: keydown enter not work on tags mode when closed --- examples/tags.tsx | 2 +- src/Selector/index.tsx | 6 ++++++ src/generate.tsx | 15 +++++++++++++++ tests/Tags.test.tsx | 37 ++++++++++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/examples/tags.tsx b/examples/tags.tsx index 3e2763189..20368ce26 100644 --- a/examples/tags.tsx +++ b/examples/tags.tsx @@ -44,7 +44,7 @@ const Test: React.FC = () => { onDeselect={(val, option) => { console.log('deselected', val, option); }} - tokenSeparators={[' ', ',']} + tokenSeparators={[' ', ',', '\n']} onFocus={() => console.log('focus')} onBlur={() => console.log('blur')} > diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index 698896de9..19ead600b 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -78,6 +78,7 @@ export interface SelectorProps { onToggleOpen: (open?: boolean) => void; /** `onSearch` returns go next step boolean to check if need do toggle open */ onSearch: (searchText: string, fromTyping: boolean, isCompositing: boolean) => boolean; + onSearchEnter: (searchText: string, isCompositing: boolean) => void; onSelect: (value: RawValueType, option: { selected: boolean }) => void; onInputKeyDown?: React.KeyboardEventHandler; @@ -100,6 +101,7 @@ const Selector: React.RefForwardingComponent = showSearch, onSearch, + onSearchEnter, onToggleOpen, onInputKeyDown, @@ -130,6 +132,10 @@ const Selector: React.RefForwardingComponent = onInputKeyDown(event); } + if (which === KeyCode.ENTER) { + onSearchEnter((event.target as HTMLInputElement).value, compositionStatusRef.current); + } + if (![KeyCode.SHIFT, KeyCode.TAB, KeyCode.BACKSPACE, KeyCode.ESC].includes(which)) { onToggleOpen(true); } diff --git a/src/generate.tsx b/src/generate.tsx index 5336b1bfb..eebdbfaa5 100644 --- a/src/generate.tsx +++ b/src/generate.tsx @@ -673,6 +673,20 @@ export default function generateSelector< return ret; }; + // Menu closed & mode is tags + const onSearchEnter = + mode === 'tags' && !mergedOpen + ? (searchText: string, isCompositing: boolean) => { + if (isCompositing) return; + const newRawValues = Array.from(new Set([...mergedRawValue, searchText])); + triggerChange(newRawValues); + newRawValues.forEach(newRawValue => { + triggerSelect(newRawValue, true, 'input'); + }); + setInnerSearchValue(''); + } + : () => {}; + // Close dropdown when disabled change useEffect(() => { if (innerOpen && !!disabled) { @@ -1014,6 +1028,7 @@ export default function generateSelector< searchValue={mergedSearchValue} activeValue={activeValue} onSearch={triggerSearch} + onSearchEnter={onSearchEnter} onSelect={onInternalSelectionSelect} /> diff --git a/tests/Tags.test.tsx b/tests/Tags.test.tsx index fea2d30af..b016e1fd4 100644 --- a/tests/Tags.test.tsx +++ b/tests/Tags.test.tsx @@ -73,8 +73,6 @@ describe('Select.Tags', () => { , ); - (wrapper.find('input').instance() as any).focus = jest.fn(); - wrapper.find('input').simulate('change', { target: { value: '2,3,4' } }); expect(handleChange).toHaveBeenCalledWith(['2', '3', '4'], expect.anything()); @@ -90,15 +88,13 @@ describe('Select.Tags', () => { it("shounld't separate words when compositing", () => { const handleChange = jest.fn(); const handleSelect = jest.fn(); - const option2 = ; const wrapper = mount( , ); - (wrapper.find('input').instance() as any).focus = jest.fn(); wrapper.find('input').simulate('compositionstart'); wrapper.find('input').simulate('change', { target: { value: '2,3,4' } }); expect(handleChange).not.toHaveBeenCalled(); @@ -115,6 +111,35 @@ describe('Select.Tags', () => { expectOpen(wrapper, false); }); + it('should work when menu is closed', () => { + const handleChange = jest.fn(); + const handleSelect = jest.fn(); + const wrapper = mount( + , + ); + wrapper.find('input').simulate('compositionstart'); + wrapper.find('input').simulate('change', { target: { value: 'Star Kirby' } }); + wrapper.find('input').simulate('keydown', { which: KeyCode.ENTER }); + expect(handleChange).not.toHaveBeenCalled(); + handleChange.mockReset(); + wrapper.find('input').simulate('compositionend'); + wrapper.find('input').simulate('keydown', { which: KeyCode.ENTER }); + expect(handleChange).toHaveBeenCalledWith(['Star Kirby'], expect.anything()); + expect(handleSelect).toHaveBeenCalledTimes(1); + expect(findSelection(wrapper).text()).toEqual('Star Kirby'); + expect(wrapper.find('input').props().value).toBe(''); + expectOpen(wrapper, false); + }); + it('paste content to split', () => { const onChange = jest.fn(); const wrapper = mount( @@ -221,8 +246,6 @@ describe('Select.Tags', () => { }; const wrapper = mount(