diff --git a/packages/core/src/components/tag-input/tagInput.tsx b/packages/core/src/components/tag-input/tagInput.tsx index dfa0a8fced..90ae8b48e1 100644 --- a/packages/core/src/components/tag-input/tagInput.tsx +++ b/packages/core/src/components/tag-input/tagInput.tsx @@ -24,8 +24,14 @@ export interface ITagInputProps extends IProps { addOnBlur?: boolean; /** - * If true, `onAdd` will be invoked when the user pastes text into the - * input. Otherwise, pasted text will remain in the input. + * If true, `onAdd` will be invoked when the user pastes text containing the `separator` + * into the input. Otherwise, pasted text will remain in the input. + * + * __Note:__ For example, if `addOnPaste=true` and `separator="\n"` (new line), then: + * - Pasting `"hello"` will _not_ invoke `onAdd` + * - Pasting `"hello\n"` will invoke `onAdd` with `["hello"]` + * - Pasting `"hello\nworld"` will invoke `onAdd` with `["hello", "world"]` + * * @default true */ addOnPaste?: boolean; @@ -385,11 +391,21 @@ export class TagInput extends AbstractPureComponent) => { + const { separator } = this.props; const value = event.clipboardData.getData("text"); - if (this.props.addOnPaste && value.length > 0) { - event.preventDefault(); - this.addTags(value); + + if (!this.props.addOnPaste || value.length === 0) { + return; + } + + // special case as a UX nicety: if the user pasted only one value with no delimiters in it, leave that value in + // the input field so that the user can refine it before converting it to a tag manually. + if (separator === false || value.split(separator).length === 1) { + return; } + + event.preventDefault(); + this.addTags(value); }; private handleRemoveTag = (event: React.MouseEvent) => { diff --git a/packages/core/test/tag-input/tagInputTests.tsx b/packages/core/test/tag-input/tagInputTests.tsx index 7bbce56fa4..dd36ba9dfa 100644 --- a/packages/core/test/tag-input/tagInputTests.tsx +++ b/packages/core/test/tag-input/tagInputTests.tsx @@ -150,13 +150,32 @@ describe("", () => { }); }); - it("is invoked on paste when addOnPaste=true", () => { - const text = "pasted\ntext"; - const onAdd = sinon.stub(); - const wrapper = mount(); - wrapper.find("input").simulate("paste", { clipboardData: { getData: () => text } }); - assert.isTrue(onAdd.calledOnce); - assert.deepEqual(onAdd.args[0][0], text.split("\n")); + describe("when addOnPaste=true", () => { + it("is invoked on paste if the text contains a delimiter between values", () => { + const text = "pasted\ntext"; + const onAdd = sinon.stub(); + const wrapper = mount(); + wrapper.find("input").simulate("paste", { clipboardData: { getData: () => text } }); + assert.isTrue(onAdd.calledOnce); + assert.deepEqual(onAdd.args[0][0], ["pasted", "text"]); + }); + + it("is invoked on paste if the text contains a trailing delimiter", () => { + const text = "pasted\n"; + const onAdd = sinon.stub(); + const wrapper = mount(); + wrapper.find("input").simulate("paste", { clipboardData: { getData: () => text } }); + assert.isTrue(onAdd.calledOnce); + assert.deepEqual(onAdd.args[0][0], ["pasted"]); + }); + + it("is not invoked on paste if the text does not include a delimiter", () => { + const text = "pasted"; + const onAdd = sinon.stub(); + const wrapper = mount(); + wrapper.find("input").simulate("paste", { clipboardData: { getData: () => text } }); + assert.isTrue(onAdd.notCalled); + }); }); it("is not invoked on paste when addOnPaste=false", () => {