Skip to content

Commit

Permalink
Ensure the exposed activeIndex is up to date for the Combobox com…
Browse files Browse the repository at this point in the history
…ponent (#2463)

* ensure the exposed `activeIndex` is up to date

* update changelog
  • Loading branch information
RobinMalfait committed Apr 28, 2023
1 parent 4c10294 commit 8e558a7
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/@headlessui-vue/CHANGELOG.md
Expand Up @@ -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

Expand Down
69 changes: 69 additions & 0 deletions packages/@headlessui-vue/src/components/combobox/combobox.test.ts
Expand Up @@ -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', () => {
Expand Down
11 changes: 10 additions & 1 deletion packages/@headlessui-vue/src/components/combobox/combobox.ts
Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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
Expand Down

2 comments on commit 8e558a7

@vercel
Copy link

@vercel vercel bot commented on 8e558a7 Apr 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

headlessui-vue – ./packages/playground-vue

headlessui-vue-git-main-tailwindlabs.vercel.app
headlessui-vue-tailwindlabs.vercel.app
headlessui-vue.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 8e558a7 Apr 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

headlessui-react – ./packages/playground-react

headlessui-react-tailwindlabs.vercel.app
headlessui-react.vercel.app
headlessui-react-git-main-tailwindlabs.vercel.app

Please sign in to comment.