Ensure the exposed activeIndex is up to date for the Combobox component (#2463)
* ensure the exposed `activeIndex` is up to date * update changelog
This commit is contained in:
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Fix memory leak in `Popover` component ([#2430](https://github.com/tailwindlabs/headlessui/pull/2430))
|
||||
- Ensure `FocusTrap` is only active when the given `enabled` value is `true` ([#2456](https://github.com/tailwindlabs/headlessui/pull/2456))
|
||||
- Ensure the exposed `activeIndex` is up to date for the `Combobox` component ([#2463](https://github.com/tailwindlabs/headlessui/pull/2463))
|
||||
|
||||
## [1.7.13] - 2023-04-12
|
||||
|
||||
|
||||
@@ -4670,6 +4670,75 @@ describe('Keyboard interactions', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it(
|
||||
'should sync the active index properly',
|
||||
suppressConsoleLogs(async () => {
|
||||
renderTemplate({
|
||||
template: html`
|
||||
<Combobox v-model="value" v-slot="{ activeIndex }">
|
||||
<ComboboxInput @input="filter" />
|
||||
<ComboboxButton>Trigger</ComboboxButton>
|
||||
<span data-test="idx">{{ activeIndex }}</span>
|
||||
<ComboboxOptions>
|
||||
<ComboboxOption v-for="option in options" :value="option" :key="option"
|
||||
>{{ option }}</ComboboxOption
|
||||
>
|
||||
</ComboboxOptions>
|
||||
</Combobox>
|
||||
`,
|
||||
setup: () => {
|
||||
let value = ref(null)
|
||||
let options = ref(['Option A', 'Option B', 'Option C', 'Option D'])
|
||||
|
||||
let query = ref('')
|
||||
let filteredOptions = computed(() => {
|
||||
return query.value === ''
|
||||
? options.value
|
||||
: options.value.filter((option) => option.includes(query.value))
|
||||
})
|
||||
|
||||
function filter(event: Event & { target: HTMLInputElement }) {
|
||||
query.value = event.target.value
|
||||
}
|
||||
|
||||
return { value, options: filteredOptions, filter }
|
||||
},
|
||||
})
|
||||
|
||||
// Open combobox
|
||||
await click(getComboboxButton())
|
||||
|
||||
let activeIndexEl = document.querySelector('[data-test="idx"]')
|
||||
function activeIndex() {
|
||||
return Number(activeIndexEl?.innerHTML)
|
||||
}
|
||||
|
||||
expect(activeIndex()).toEqual(0)
|
||||
|
||||
let options: ReturnType<typeof getComboboxOptions>
|
||||
|
||||
await focus(getComboboxInput())
|
||||
await type(word('Option B'))
|
||||
|
||||
// Option B should be active
|
||||
options = getComboboxOptions()
|
||||
expect(options[0]).toHaveTextContent('Option B')
|
||||
assertActiveComboboxOption(options[0])
|
||||
|
||||
expect(activeIndex()).toEqual(0)
|
||||
|
||||
// Reveal all options again
|
||||
await type(word('Option'))
|
||||
|
||||
// Option B should still be active
|
||||
options = getComboboxOptions()
|
||||
expect(options[1]).toHaveTextContent('Option B')
|
||||
assertActiveComboboxOption(options[1])
|
||||
|
||||
expect(activeIndex()).toEqual(1)
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
describe('Mouse interactions', () => {
|
||||
|
||||
@@ -232,7 +232,7 @@ export let Combobox = defineComponent({
|
||||
) {
|
||||
let localActiveOptionIndex = options.value.findIndex((option) => !option.dataRef.disabled)
|
||||
if (localActiveOptionIndex !== -1) {
|
||||
return localActiveOptionIndex
|
||||
activeOptionIndex.value = localActiveOptionIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +391,15 @@ export let Combobox = defineComponent({
|
||||
options.value = adjustedState.options
|
||||
activeOptionIndex.value = adjustedState.activeOptionIndex
|
||||
activationTrigger.value = ActivationTrigger.Other
|
||||
|
||||
// If some of the DOM elements aren't ready yet, then we can retry in the next tick.
|
||||
if (adjustedState.options.some((option) => !dom(option.dataRef.domRef))) {
|
||||
requestAnimationFrame(() => {
|
||||
let adjustedState = adjustOrderedState()
|
||||
options.value = adjustedState.options
|
||||
activeOptionIndex.value = adjustedState.activeOptionIndex
|
||||
})
|
||||
}
|
||||
},
|
||||
unregisterOption(id: string) {
|
||||
// When we are unregistering the currently active option, then we also have to make sure to
|
||||
|
||||
Reference in New Issue
Block a user