diff --git a/examples/tags.tsx b/examples/tags.tsx index 5e8d67af5..20368ce26 100644 --- a/examples/tags.tsx +++ b/examples/tags.tsx @@ -70,6 +70,34 @@ const Test: React.FC = () => { toggle maxTagCount (null)

+

tags select with open = false

+
+ +
); }; diff --git a/index.js b/index.js deleted file mode 100644 index 3151c2a2a..000000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('./src/'); 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/src/Selector/index.tsx b/src/Selector/index.tsx index 698896de9..7dc36c7a5 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; + onSearchSubmit: (searchText: string) => void; onSelect: (value: RawValueType, option: { selected: boolean }) => void; onInputKeyDown?: React.KeyboardEventHandler; @@ -100,6 +101,7 @@ const Selector: React.RefForwardingComponent = showSearch, onSearch, + onSearchSubmit, onToggleOpen, onInputKeyDown, @@ -130,6 +132,12 @@ const Selector: React.RefForwardingComponent = onInputKeyDown(event); } + if (which === KeyCode.ENTER && mode === 'tags' && !compositionStatusRef.current && !open) { + // When menu isn't open, OptionList won't trigger a value change + // So when enter is pressed, the tag's input value should be emitted here to let selector know + onSearchSubmit((event.target as HTMLInputElement).value); + } + 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..cf0631288 100644 --- a/src/generate.tsx +++ b/src/generate.tsx @@ -673,6 +673,18 @@ export default function generateSelector< return ret; }; + // Only triggered when menu is closed & mode is tags + // If menu is open, OptionList will take charge + // If mode isn't tags, press enter is not meaningful when you can't see any option + const onSearchSubmit = (searchText: string) => { + 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 +1026,7 @@ export default function generateSelector< searchValue={mergedSearchValue} activeValue={activeValue} onSearch={triggerSearch} + onSearchSubmit={onSearchSubmit} onSelect={onInternalSelectionSelect} /> 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( 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(