From 1f9fbfe30d58a557fe5c9ea5c74f144a177a47a2 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 5 Sep 2025 13:28:43 +0200 Subject: [PATCH 1/3] add failing test --- .../src/components/combobox/combobox.test.tsx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index 5390696e3..4d6b38be6 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -3035,6 +3035,56 @@ describe.each([{ virtual: true }, { virtual: false }])( assertActiveElement(document.querySelector('#before-combobox')) }) ) + + it( + 'pressing Tab should sync the ComboboxInput value again', + suppressConsoleLogs(async () => { + function Example() { + let [value, setValue] = useState(null) + + return ( + <> + value ?? '', + }} + /> + + + ) + } + + render() + + assertComboboxButton({ state: ComboboxState.InvisibleUnmounted }) + assertComboboxList({ state: ComboboxState.InvisibleUnmounted }) + + // Open combobox + await click(getComboboxButton()) + + // Select the 2nd option + await press(Keys.ArrowDown) + + // Tab to the next DOM node + await press(Keys.Tab) + + // Verify it is closed + assertComboboxButton({ state: ComboboxState.InvisibleUnmounted }) + assertComboboxList({ state: ComboboxState.InvisibleUnmounted }) + + // Verify the selected value was the highlighted one + expect(getComboboxInput()?.value).toBe('Option B') + + // Clear the option + await click(document.querySelector('button#clear') as HTMLButtonElement) + + // Verify the input value is cleared + expect(getComboboxInput()?.value).toBe('') + }) + ) }) describe('`Escape` key', () => { From 57b8c89a7384f409363b9e4c60dd877d5d9c300d Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 5 Sep 2025 13:13:46 +0200 Subject: [PATCH 2/3] we're not typing anymore when `Tab` is used We prevent syncing the `ComboboxInput` state while you are typing. However, when you press `Tab` there is keyboard event, but since we are tabbing away from the input we should not be "typing" anymore. This ensures that the `input` can be updated with whatever the current value should be. Co-authored-by: Daniil Savitskii --- packages/@headlessui-react/src/components/combobox/combobox.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index 882d7a3a3..d7c94111d 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -785,6 +785,7 @@ function InputFn< return machine.actions.closeCombobox() case Keys.Tab: + machine.actions.setIsTyping(false) if (machine.state.comboboxState !== ComboboxState.Open) return if ( data.mode === ValueMode.Single && From 55f8e9ac9dd72efe1ee6bbe37e644a527d63c8f9 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 5 Sep 2025 13:29:17 +0200 Subject: [PATCH 3/3] update changelog --- packages/@headlessui-react/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 1008b1923..dfa77c43d 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure `onChange` types are contravariant instead of bivariant ([#3781](https://github.com/tailwindlabs/headlessui/pull/3781)) - Support `` as a focusable element inside `
` ([#3389](https://github.com/tailwindlabs/headlessui/pull/3389)) - Fix `Maximum update depth exceeded` crash when using `transition` prop ([#3782](https://github.com/tailwindlabs/headlessui/pull/3782)) +- Ensure pressing `Tab` in the `ComboboxInput`, correctly syncs the input value ([#3785](https://github.com/tailwindlabs/headlessui/pull/3785)) ## [2.2.7] - 2025-07-30