diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cccddc1b0..a41b7dae54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `ownerDocument` instead of `document` ([#1158](https://github.com/tailwindlabs/headlessui/pull/1158)) - Ensure focus trap, Tabs and Dialog play well together ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231)) - Add `multi` value support for Listbox & Combobox ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243)) +- Improve Combobox Input value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248)) ### Added @@ -56,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Re-expose `el` ([#1230](https://github.com/tailwindlabs/headlessui/pull/1230)) - Ensure focus trap, Tabs and Dialog play well together ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231)) - Add `multi` value support for Listbox & Combobox ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243)) +- Improve Combobox Input value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248)) ### Added diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index c520dba6da..2f7553d98d 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -4393,6 +4393,59 @@ describe('Mouse interactions', () => { expect(getComboboxInput()?.value).toBe('') }) ) + + it( + 'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)', + suppressConsoleLogs(async () => { + let people = [ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' }, + { id: 3, name: 'Charlie' }, + ] + + function Example() { + let [value, setValue] = useState(people[1]) + + return ( + <> + + person?.name} + /> + Trigger + + {people.map((person) => ( + + {person.name} + + ))} + + + + + ) + } + + render() + + // Open combobox + await click(getComboboxButton()) + + // Verify the input has the selected value + expect(getComboboxInput()?.value).toBe('Bob') + + // Override the input by typing something + await type(word('test'), getComboboxInput()) + expect(getComboboxInput()?.value).toBe('test') + + // Reset from outside + await click(getByText('reset')) + + // Verify the input is reset correctly + expect(getComboboxInput()?.value).toBe('') + }) + ) }) describe('Multi-select', () => { diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index f279e4e7f5..eb02856588 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -386,11 +386,10 @@ let ComboboxRoot = forwardRefWithAs(function Combobox< let syncInputValue = useCallback(() => { if (!inputRef.current) return - if (value === undefined) return let displayValue = inputPropsRef.current.displayValue if (typeof displayValue === 'function') { - inputRef.current.value = displayValue(value) + inputRef.current.value = displayValue(value) ?? '' } else if (typeof value === 'string') { inputRef.current.value = value } else { diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts index 2742c42740..0a13653473 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts @@ -4615,6 +4615,54 @@ describe('Mouse interactions', () => { expect(getComboboxInput()?.value).toBe('') }) ) + + it( + 'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)', + suppressConsoleLogs(async () => { + renderTemplate({ + template: html` + + + Trigger + + {{ person.name }} + + + + `, + setup: () => { + let people = [ + { id: 1, name: 'Alice' }, + { id: 2, name: 'Bob' }, + { id: 3, name: 'Charlie' }, + ] + + return { + people, + value: ref(people[1]), + } + }, + }) + + // Open combobox + await click(getComboboxButton()) + + // Verify the input has the selected value + expect(getComboboxInput()?.value).toBe('Bob') + + // Override the input by typing something + await type(word('test'), getComboboxInput()) + expect(getComboboxInput()?.value).toBe('test') + + // Reset from outside + await click(getByText('reset')) + + // Verify the input is reset correctly + expect(getComboboxInput()?.value).toBe('') + }) + ) }) describe('Multi-select', () => { diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index 4363c6c0a0..45581ff4d6 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -216,11 +216,10 @@ export let Combobox = defineComponent({ syncInputValue() { let value = api.value.value if (!dom(api.inputRef)) return - if (value === undefined) return let displayValue = api.inputPropsRef.value.displayValue if (typeof displayValue === 'function') { - api.inputRef!.value!.value = displayValue(value) + api.inputRef!.value!.value = displayValue(value) ?? '' } else if (typeof value === 'string') { api.inputRef!.value!.value = value } else {