From 43d7d0024796d5a4dcaca24f70dbd5245dac6ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Fern=C3=A1ndez?= Date: Tue, 11 Feb 2025 14:13:42 +0100 Subject: [PATCH] Combobox: fix check for existing options when creating a custom value (#100123) --- .../src/components/Combobox/Combobox.test.tsx | 14 +++++++++++++- .../src/components/Combobox/Combobox.tsx | 6 +++--- .../src/components/Combobox/useOptions.ts | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx index e99a2568faa..6127c007e56 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.test.tsx @@ -243,7 +243,19 @@ describe('Combobox', () => { await userEvent.keyboard('{Enter}'); expect(screen.getByDisplayValue('Use custom value')).toBeInTheDocument(); - expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ value: 'Use custom value' })); + expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ description: 'Use custom value' })); + }); + + it('should not allow creating a custom value when it is an existing value', async () => { + const onChangeHandler = jest.fn(); + render(); + const input = screen.getByRole('combobox'); + await userEvent.type(input, '4'); + await userEvent.keyboard('{Enter}'); + expect(screen.queryByDisplayValue('Use custom value')).not.toBeInTheDocument(); + expect(screen.getByDisplayValue('Option 4')).toBeInTheDocument(); + expect(onChangeHandler).toHaveBeenCalledWith(expect.objectContaining({ value: '4' })); + expect(onChangeHandler).not.toHaveBeenCalledWith(expect.objectContaining({ description: 'Use custom value' })); }); it('should provide custom string when all options are numbers', async () => { diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.tsx index 4c2f6a97b63..471bb22fe5d 100644 --- a/packages/grafana-ui/src/components/Combobox/Combobox.tsx +++ b/packages/grafana-ui/src/components/Combobox/Combobox.tsx @@ -129,9 +129,9 @@ export const Combobox = (props: ComboboxProps) => let itemsToSet = items; logOptions(itemsToSet.length, RECOMMENDED_ITEMS_AMOUNT, id, ariaLabelledBy); if (inputValue && createCustomValue) { - const optionMatchingInput = items.find( - (opt) => opt.label === 'Custom value: ' + inputValue || opt.value === inputValue - ); + //Since the label of a normal option does not have to match its value and a custom option has the same value and label, + //we just focus on the value to check if the option already exists + const optionMatchingInput = items.find((opt) => opt.value === inputValue); if (!optionMatchingInput) { const customValueOption = { diff --git a/packages/grafana-ui/src/components/Combobox/useOptions.ts b/packages/grafana-ui/src/components/Combobox/useOptions.ts index a66e60a2747..b76339cf54e 100644 --- a/packages/grafana-ui/src/components/Combobox/useOptions.ts +++ b/packages/grafana-ui/src/components/Combobox/useOptions.ts @@ -62,6 +62,8 @@ export function useOptions(rawOptions: AsyncOptions>) => { let currentOptions: Array> = opts; if (createCustomValue && userTypedSearch) { + //Since the label of a normal option does not have to match its value and a custom option has the same value and label, + //we just focus on the value to check if the option already exists const customValueExists = opts.some((opt) => opt.value === userTypedSearch); if (!customValueExists) { currentOptions = [