Add frozen value to ComboboxOptions component (#3126)
* 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
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1796,7 +1814,7 @@ function OptionFn<
|
||||
}
|
||||
|
||||
if (data.mode === ValueMode.Single) {
|
||||
requestAnimationFrame(() => actions.closeCombobox())
|
||||
actions.closeCombobox()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user