From 4e6a60863536576a60180417b4ff9aa0fa7b148c Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Thu, 11 Jun 2020 19:24:41 +0800 Subject: [PATCH 1/4] fix: not seperating words while compositing --- src/Selector/Input.tsx | 22 ++++++++++++++++++++++ src/Selector/MultipleSelector.tsx | 4 ++++ src/Selector/SingleSelector.tsx | 4 ++++ src/Selector/index.tsx | 19 ++++++++++++++++--- src/generate.tsx | 12 +++++++----- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index 71e909c1d..8de5b42cf 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -22,6 +22,12 @@ interface InputProps { onMouseDown: React.MouseEventHandler; onChange: React.ChangeEventHandler; onPaste: React.ClipboardEventHandler; + onCompositionStart: React.CompositionEventHandler< + HTMLInputElement | HTMLTextAreaElement | HTMLElement + >; + onCompositionEnd: React.CompositionEventHandler< + HTMLInputElement | HTMLTextAreaElement | HTMLElement + >; } const Input: React.RefForwardingComponent = ( @@ -40,6 +46,8 @@ const Input: React.RefForwardingComponent = ( onMouseDown, onChange, onPaste, + onCompositionStart, + onCompositionEnd, open, attrs, }, @@ -53,6 +61,8 @@ const Input: React.RefForwardingComponent = ( onKeyDown: onOriginKeyDown, onChange: onOriginChange, onMouseDown: onOriginMouseDown, + onCompositionStart: onOriginCompositionStart, + onCompositionEnd: onOriginCompositionEnd, style, }, } = inputNode; @@ -95,6 +105,18 @@ const Input: React.RefForwardingComponent = ( onOriginChange(event); } }, + onCompositionStart(event: React.CompositionEvent) { + onCompositionStart(event); + if (onOriginCompositionStart) { + onOriginCompositionStart(event); + } + }, + onCompositionEnd(event: React.CompositionEvent) { + onCompositionEnd(event); + if (onOriginCompositionEnd) { + onOriginCompositionEnd(event); + } + }, onPaste, }); diff --git a/src/Selector/MultipleSelector.tsx b/src/Selector/MultipleSelector.tsx index baef8aca9..857c98f69 100644 --- a/src/Selector/MultipleSelector.tsx +++ b/src/Selector/MultipleSelector.tsx @@ -60,6 +60,8 @@ const SelectSelector: React.FC = props => { onInputPaste, onInputKeyDown, onInputMouseDown, + onCompositionStart, + onCompositionEnd, } = props; const [motionAppear, setMotionAppear] = React.useState(false); @@ -196,6 +198,8 @@ const SelectSelector: React.FC = props => { onMouseDown={onInputMouseDown} onChange={onInputChange} onPaste={onInputPaste} + onCompositionStart={onCompositionStart} + onCompositionEnd={onCompositionEnd} tabIndex={tabIndex} attrs={pickAttrs(props, true)} /> diff --git a/src/Selector/SingleSelector.tsx b/src/Selector/SingleSelector.tsx index 037ea9ad8..764549175 100644 --- a/src/Selector/SingleSelector.tsx +++ b/src/Selector/SingleSelector.tsx @@ -33,6 +33,8 @@ const SingleSelector: React.FC = props => { onInputMouseDown, onInputChange, onInputPaste, + onCompositionStart, + onCompositionEnd, } = props; const combobox = mode === 'combobox'; @@ -67,6 +69,8 @@ const SingleSelector: React.FC = props => { onMouseDown={onInputMouseDown} onChange={onInputChange} onPaste={onInputPaste} + onCompositionStart={onCompositionStart} + onCompositionEnd={onCompositionEnd} tabIndex={tabIndex} attrs={pickAttrs(props, true)} /> diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index 752e8edc1..0cd2200b3 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -37,6 +37,8 @@ export interface InnerSelectorProps { onInputMouseDown: React.MouseEventHandler; onInputChange: React.ChangeEventHandler; onInputPaste: React.ClipboardEventHandler; + onCompositionStart: React.CompositionEventHandler; + onCompositionEnd: React.CompositionEventHandler; } export interface RefSelectorProps { @@ -75,7 +77,7 @@ export interface SelectorProps { onToggleOpen: (open?: boolean) => void; /** `onSearch` returns go next step boolean to check if need do toggle open */ - onSearch: (searchValue: string) => boolean; + onSearch: (searchText: string, fromTyping: boolean, isCompositing: boolean) => boolean; onSelect: (value: RawValueType, option: { selected: boolean }) => void; onInputKeyDown?: React.KeyboardEventHandler; @@ -88,6 +90,7 @@ export interface SelectorProps { const Selector: React.RefForwardingComponent = (props, ref) => { const inputRef = React.useRef(null); + const compositionStatusRef = React.useRef(false); const { prefixCls, @@ -144,11 +147,19 @@ const Selector: React.RefForwardingComponent = const pasteClearRef = React.useRef(false); const triggerOnSearch = (value: string) => { - if (onSearch(value) !== false) { + if (onSearch(value, true, compositionStatusRef.current) !== false) { onToggleOpen(true); } }; + const onCompositionStart = () => { + compositionStatusRef.current = true; + }; + + const onCompositionEnd = () => { + compositionStatusRef.current = false; + }; + const onInputChange = ({ target: { value } }) => { if (pasteClearRef.current) { pasteClearRef.current = false; @@ -191,7 +202,7 @@ const Selector: React.RefForwardingComponent = if ((mode !== 'combobox' && (!showSearch || !inputMouseDown)) || !open) { if (open) { - onSearch(''); + onSearch('', true, false); } onToggleOpen(); } @@ -204,6 +215,8 @@ const Selector: React.RefForwardingComponent = onInputMouseDown: onInternalInputMouseDown, onInputChange, onInputPaste, + onCompositionStart, + onCompositionEnd, }; const selectNode = multiple ? ( diff --git a/src/generate.tsx b/src/generate.tsx index 8219a60ab..7c989a353 100644 --- a/src/generate.tsx +++ b/src/generate.tsx @@ -619,13 +619,15 @@ export default function generateSelector< ); // ============================= Search ============================= - const triggerSearch = (searchText: string, fromTyping: boolean = true) => { + const triggerSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { let ret = true; let newSearchText = searchText; setActiveValue(null); // Check if match the `tokenSeparators` - const patchLabels: string[] = getSeparatedContent(searchText, tokenSeparators); + const patchLabels: string[] = isCompositing + ? null + : getSeparatedContent(searchText, tokenSeparators); let patchRawValues: RawValueType[] = patchLabels; if (mode === 'combobox') { @@ -681,7 +683,7 @@ export default function generateSelector< // Close will clean up single mode search text React.useEffect(() => { if (!mergedOpen && !isMultiple && mode !== 'combobox') { - triggerSearch('', false); + triggerSearch('', false, false); } }, [mergedOpen]); @@ -776,7 +778,7 @@ export default function generateSelector< if (mergedSearchValue) { // `tags` mode should move `searchValue` into values if (mode === 'tags') { - triggerSearch('', false); + triggerSearch('', false, false); triggerChange(Array.from(new Set([...mergedRawValue, mergedSearchValue]))); } else if (mode === 'multiple') { // `multiple` mode only clean the search value but not trigger event @@ -885,7 +887,7 @@ export default function generateSelector< } triggerChange([]); - triggerSearch('', false); + triggerSearch('', false, false); }; if (!disabled && allowClear && (mergedRawValue.length || mergedSearchValue)) { From a9d3fcd2ae4696ac04f0c47d8d7f7fb968c10fe8 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Fri, 12 Jun 2020 11:37:56 +0800 Subject: [PATCH 2/4] test: not seperating words while compositing --- tests/Multiple.test.tsx | 58 +++++++++++++++++++++++++++++++++++++++++ tests/Tags.test.tsx | 34 +++++++++++++++++++++--- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/tests/Multiple.test.tsx b/tests/Multiple.test.tsx index 92f352362..a6e11cfd5 100644 --- a/tests/Multiple.test.tsx +++ b/tests/Multiple.test.tsx @@ -79,6 +79,64 @@ describe('Select.Multiple', () => { expectOpen(wrapper, false); }); + it('shouldn\'t separate words when compositing', () => { + 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('compositionstart'); + wrapper.find('input').simulate('change', { + target: { + value: 'One,Two,Three', + }, + }); + expect(handleChange).not.toHaveBeenCalled(); + + handleChange.mockReset(); + wrapper.find('input').simulate('compositionend'); + wrapper.find('input').simulate('change', { + target: { + value: 'One,Two,Three', + }, + }); + expect(handleChange).toHaveBeenCalledWith(['1', '2'], expect.anything()); + + handleChange.mockReset(); + wrapper.find('input').simulate('change', { + target: { + value: 'One,Two', + }, + }); + expect(handleChange).toHaveBeenCalledWith(['1', '2'], expect.anything()); + + expect(wrapper.find('input').props().value).toBe(''); + wrapper.update(); + expectOpen(wrapper, false); + }); + it('focus', () => { jest.useFakeTimers(); const handleFocus = jest.fn(); diff --git a/tests/Tags.test.tsx b/tests/Tags.test.tsx index c0253f9ac..1e8199bda 100644 --- a/tests/Tags.test.tsx +++ b/tests/Tags.test.tsx @@ -73,7 +73,7 @@ describe('Select.Tags', () => { , ); - wrapper.find('input').instance().focus = jest.fn(); + (wrapper.find('input').instance() as any).focus = jest.fn(); wrapper.find('input').simulate('change', { target: { value: '2,3,4' } }); @@ -87,6 +87,34 @@ describe('Select.Tags', () => { expectOpen(wrapper, false); }); + 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(); + handleChange.mockReset(); + wrapper.find('input').simulate('compositionend'); + wrapper.find('input').simulate('change', { target: { value: '2,3,4' } }); + expect(handleChange).toHaveBeenCalledWith(['2', '3', '4'], expect.anything()); + expect(handleSelect).toHaveBeenCalledTimes(3); + expect(handleSelect).toHaveBeenLastCalledWith('4', expect.anything()); + expect(findSelection(wrapper).text()).toEqual('2'); + expect(findSelection(wrapper, 1).text()).toEqual('3'); + expect(findSelection(wrapper, 2).text()).toEqual('4'); + expect(wrapper.find('input').props().value).toBe(''); + expectOpen(wrapper, false); + }); + it('paste content to split', () => { const onChange = jest.fn(); const wrapper = mount( @@ -193,7 +221,7 @@ describe('Select.Tags', () => { }; const wrapper = mount(