diff --git a/packages/eui/changelogs/upcoming/8335.md b/packages/eui/changelogs/upcoming/8335.md new file mode 100644 index 00000000000..a0a3c2c022b --- /dev/null +++ b/packages/eui/changelogs/upcoming/8335.md @@ -0,0 +1,3 @@ +**Bug fixes** + +- Fixed `EuiComboBox` by cleaning duplicated values when having a delimiter prop. diff --git a/packages/eui/src/components/combo_box/combo_box.spec.tsx b/packages/eui/src/components/combo_box/combo_box.spec.tsx index 9b0b563fa45..06aa8e035fd 100644 --- a/packages/eui/src/components/combo_box/combo_box.spec.tsx +++ b/packages/eui/src/components/combo_box/combo_box.spec.tsx @@ -36,6 +36,7 @@ describe('EuiComboBox', () => { const onChange = (selectedOptions: typeof options) => { setSelected(selectedOptions); }; + return ( { ); }; + const CreateComboBox = () => { + const [options, setOptions] = useState([]); + const [selectedOptions, setSelected] = useState([]); + + const onCreateOption = (searchValue: string) => { + const newOption = { label: searchValue }; + + setOptions([...options, newOption]); + + setSelected((prevSelected) => [...prevSelected, newOption]); + }; + + return ( + + ); + }; + describe('focus management', () => { it('keeps focus on the input box when clicking a disabled item', () => { cy.realMount( @@ -410,6 +433,25 @@ describe('EuiComboBox', () => { 'Item 1' ); }); + + it('adds only one item when pasting duplicated elements', () => { + cy.realMount(); + cy.get('[data-test-subj="euiComboBoxPill"]').should('not.exist'); + + cy.get('[data-test-subj="comboBoxSearchInput"]').click(); + + // Simulate pasting text + cy.get('[data-test-subj="comboBoxSearchInput"]') + .clear() + .invoke('val', 'a, a, a, a') + .trigger('input'); + cy.get('[data-test-subj="comboBoxSearchInput"]').type(' {backspace}'); + + cy.realPress('Enter'); + + cy.get('[data-test-subj="euiComboBoxPill"]').should('have.length', 1); + cy.get('[data-test-subj="euiComboBoxPill"]').should('have.text', 'a'); + }); }); describe('single selection', () => { diff --git a/packages/eui/src/components/combo_box/combo_box.test.tsx b/packages/eui/src/components/combo_box/combo_box.test.tsx index 825153f2e60..ac8031bc4f2 100644 --- a/packages/eui/src/components/combo_box/combo_box.test.tsx +++ b/packages/eui/src/components/combo_box/combo_box.test.tsx @@ -516,6 +516,26 @@ describe('EuiComboBox', () => { expect(onCreateOptionHandler).not.toHaveBeenCalled(); }); + + it('fires the callback only once when adding duplicated values', () => { + const onCreateOptionHandler = jest.fn(); + + const { getByTestSubject } = render( + + ); + const input = getByTestSubject('comboBoxSearchInput'); + + fireEvent.change(input, { target: { value: 'a, a, a, a' } }); + fireEvent.keyDown(input, { key: 'Enter' }); + + expect(onCreateOptionHandler).toHaveBeenCalledTimes(1); + expect(onCreateOptionHandler).toHaveBeenCalledWith('a', options); + }); }); }); diff --git a/packages/eui/src/components/combo_box/combo_box.tsx b/packages/eui/src/components/combo_box/combo_box.tsx index e4660e7bf75..09bf2dafaf7 100644 --- a/packages/eui/src/components/combo_box/combo_box.tsx +++ b/packages/eui/src/components/combo_box/combo_box.tsx @@ -460,7 +460,10 @@ export class EuiComboBox extends Component< const { searchValue } = this.state; const { delimiter } = this.props; if (delimiter) { - searchValue.split(delimiter).forEach((option: string) => { + const trimmed = searchValue.split(delimiter).map((value) => value.trim()); + const values = [...new Set([...trimmed])]; + + values.forEach((option: string) => { if (option.length > 0) this.addCustomOption(isContainerBlur, option); }); } else {