Skip to content

Commit

Permalink
Add frozen value to ComboboxOptions component (#3126)
Browse files Browse the repository at this point in the history
* add frozen state to `Combobox` component

Once you choose an option, the `selected` state remains on the "old"
value until the combobox is fully closed. This way the potential visual
indicators such as a check mark doesn't move around while the Combobox
is closing (when using transitions)

Same as the `Listbox`, this is purely about visual state and exposed
data from the `ComboboxOptions` component and down that tree. The
top-level `Combobox` and `ComboboxInput` components still know the
correct (new) value and will update the `aria-activedescendant`
correctly.

This is achieved by storing the old data (only in single value mode),
and overriding the `isSelected` check function via context provided by
the `ComboboxOptions` component.

* remove check that verified that no `aria-selected` was present

But now with this change, it will be present.

* update changelog

* Update CHANGELOG.md
  • Loading branch information
RobinMalfait committed Apr 24, 2024
1 parent b6aa1d6 commit d56a77b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 11 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Expand Up @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add new `CloseButton` component and `useClose` hook ([#3096](https://github.com/tailwindlabs/headlessui/pull/3096))
- Allow passing a boolean to the `anchor` prop ([#3121](https://github.com/tailwindlabs/headlessui/pull/3121))
- Add `portal` prop to `Combobox`, `Listbox`, `Menu` and `Popover` components ([#3124](https://github.com/tailwindlabs/headlessui/pull/3124))
- Add frozen value to `ComboboxOptions` component ([#3126](https://github.com/tailwindlabs/headlessui/pull/3126))

## [1.7.19] - 2024-04-15

Expand Down
Expand Up @@ -4289,7 +4289,6 @@ describe.each([{ virtual: true }, { virtual: false }])(

// Verify that we don't have an selected option anymore
assertNotActiveComboboxOption(options[1])
assertNoSelectedComboboxOption()

// Verify that we saw the `null` change coming in
expect(handleChange).toHaveBeenCalledTimes(1)
Expand Down
38 changes: 28 additions & 10 deletions packages/@headlessui-react/src/components/combobox/combobox.tsx
Expand Up @@ -1646,17 +1646,35 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
})
}

// Frozen state, the selected value will only update visually when the user re-opens the <Combobox />
let [frozenValue, setFrozenValue] = useState(data.value)
if (
data.value !== frozenValue &&
data.comboboxState === ComboboxState.Open &&
data.mode !== ValueMode.Multi
) {
setFrozenValue(data.value)
}

let isSelected = useEvent((compareValue: unknown) => {
return data.compare(frozenValue, compareValue)
})

return (
<Portal enabled={visible && portal}>
{render({
ourProps,
theirProps,
slot,
defaultTag: DEFAULT_OPTIONS_TAG,
features: OptionsRenderFeatures,
visible,
name: 'Combobox.Options',
})}
<ComboboxDataContext.Provider
value={data.mode === ValueMode.Multi ? data : { ...data, isSelected }}
>
{render({
ourProps,
theirProps,
slot,
defaultTag: DEFAULT_OPTIONS_TAG,
features: OptionsRenderFeatures,
visible,
name: 'Combobox.Options',
})}
</ComboboxDataContext.Provider>
</Portal>
)
}
Expand Down Expand Up @@ -1796,7 +1814,7 @@ function OptionFn<
}

if (data.mode === ValueMode.Single) {
requestAnimationFrame(() => actions.closeCombobox())
actions.closeCombobox()
}
})

Expand Down

0 comments on commit d56a77b

Please sign in to comment.