diff --git a/docs/demo/custom-tokenize.md b/docs/demo/custom-tokenize.md new file mode 100644 index 000000000..c7ebb2a49 --- /dev/null +++ b/docs/demo/custom-tokenize.md @@ -0,0 +1,8 @@ +--- +title: custom-tokenize +nav: + title: Demo + path: /demo +--- + + diff --git a/docs/examples/custom-tokenize.tsx b/docs/examples/custom-tokenize.tsx new file mode 100644 index 000000000..b9d8c9cb9 --- /dev/null +++ b/docs/examples/custom-tokenize.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import Select from '@rc-component/select'; +import '../../assets/index.less'; + +const tokenize = (input: string): string[] => { + const tokens: string[] = []; + const regex = /"([^"]*)"|([^,\n]+)/g; + let m: RegExpExecArray | null = regex.exec(input); + while (m !== null) { + tokens.push((m[1] ?? m[2]).trim()); + m = regex.exec(input); + } + return tokens.filter(Boolean); +}; + +const Demo: React.FC = () => ( + <> +

自定义分词(引号感知)

+ + + + + + + + , + ); + fireEvent.paste(container.querySelector('input'), { + clipboardData: { getData: () => 'One,Two,Unknown' }, + }); + fireEvent.change(container.querySelector('input'), { + target: { value: 'One,Two,Unknown' }, + }); + expect(handleChange).toHaveBeenCalledWith(['1', '2'], expect.anything()); + expect(container.querySelector('input').value).toBe(''); + }); + it('tokenize input when mode=tags and open=false', () => { const handleChange = jest.fn(); const handleSelect = jest.fn(); diff --git a/tests/Tags.test.tsx b/tests/Tags.test.tsx index 74da89955..172e76f9e 100644 --- a/tests/Tags.test.tsx +++ b/tests/Tags.test.tsx @@ -82,6 +82,71 @@ describe('Select.Tags', () => { expectOpen(container, false); }); + it('tokenize prop overrides tokenSeparators with quote-aware splitting', () => { + const handleChange = jest.fn(); + const tokenize = jest.fn((input: string) => { + const tokens: string[] = []; + const regex = /"([^"]*)"|([^,\n]+)/g; + let m: RegExpExecArray | null = regex.exec(input); + while (m !== null) { + tokens.push((m[1] ?? m[2]).trim()); + m = regex.exec(input); + } + return tokens.filter(Boolean); + }); + const { container } = render( + , + ); + + fireEvent.change(container.querySelector('input'), { target: { value: '"a, b", c' } }); + + expect(tokenize).toHaveBeenCalled(); + expect(handleChange).toHaveBeenCalledWith(['a, b', 'c'], expect.anything()); + expect(container.querySelector('input').value).toBe(''); + }); + + it('tokenize prop respects maxCount', () => { + const handleChange = jest.fn(); + const tokenize = () => ['a', 'b', 'c', 'd', 'e']; + const { container } = render( + + + , + ); + fireEvent.compositionStart(container.querySelector('input')); + fireEvent.change(container.querySelector('input'), { target: { value: '2,3,4' } }); + expect(handleChange).not.toHaveBeenCalled(); + handleChange.mockReset(); + fireEvent.compositionEnd(container.querySelector('input')); + fireEvent.change(container.querySelector('input'), { target: { value: '2,3,4' } }); + expect(handleChange).toHaveBeenCalledWith(['2', '3', '4'], expect.anything()); + }); + + it('tokenize prop returning [input] keeps typing', () => { + const handleChange = jest.fn(); + const tokenize = (input: string) => [input]; + const { container } = render( + , + ); + fireEvent.change(container.querySelector('input'), { target: { value: 'hello' } }); + expect(handleChange).not.toHaveBeenCalled(); + expect(container.querySelector('input').value).toBe('hello'); + }); + it('should not separate words when compositing but trigger after composition end', () => { const handleChange = jest.fn(); const handleSelect = jest.fn();