ensure options in listbox can be objects
This commit is contained in:
@@ -579,6 +579,59 @@ describe('Keyboard interactions', () => {
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should be possible to open the listbox with Enter, and focus the selected option (with a list of objects)',
|
||||
suppressConsoleLogs(async () => {
|
||||
const myOptions = [
|
||||
{ id: 'a', name: 'Option A' },
|
||||
{ id: 'b', name: 'Option B' },
|
||||
{ id: 'c', name: 'Option C' },
|
||||
]
|
||||
const selectedOption = myOptions[1]
|
||||
render(
|
||||
<Listbox value={selectedOption} onChange={console.log}>
|
||||
<Listbox.Button>Trigger</Listbox.Button>
|
||||
<Listbox.Options>
|
||||
{myOptions.map(myOption => (
|
||||
<Listbox.Option key={myOption.id} value={myOption}>
|
||||
{myOption.name}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</Listbox>
|
||||
)
|
||||
|
||||
assertListboxButton({
|
||||
state: ListboxState.Closed,
|
||||
attributes: { id: 'headlessui-listbox-button-1' },
|
||||
})
|
||||
assertListbox({ state: ListboxState.Closed })
|
||||
|
||||
// Focus the button
|
||||
getListboxButton()?.focus()
|
||||
|
||||
// Open listbox
|
||||
await press(Keys.Enter)
|
||||
|
||||
// Verify it is open
|
||||
assertListboxButton({ state: ListboxState.Open })
|
||||
assertListbox({
|
||||
state: ListboxState.Open,
|
||||
attributes: { id: 'headlessui-listbox-options-2' },
|
||||
})
|
||||
assertActiveElement(getListbox())
|
||||
assertListboxButtonLinkedWithListbox()
|
||||
|
||||
// Verify we have listbox options
|
||||
const options = getListboxOptions()
|
||||
expect(options).toHaveLength(3)
|
||||
options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))
|
||||
|
||||
// Verify that the second listbox option is active (because it is already selected)
|
||||
assertActiveListboxOption(options[1])
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should have no active listbox option when there are no listbox options at all',
|
||||
suppressConsoleLogs(async () => {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<ListboxButton
|
||||
class="relative w-full py-2 pl-3 pr-10 text-left transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md cursor-default focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm sm:leading-5"
|
||||
>
|
||||
<span class="block truncate">{{ active }}</span>
|
||||
<span class="block truncate">{{ active.name }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
@@ -36,9 +36,9 @@
|
||||
class="py-1 overflow-auto text-base leading-6 rounded-md shadow-xs max-h-60 focus:outline-none sm:text-sm sm:leading-5"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="name in people"
|
||||
:key="name"
|
||||
:value="name"
|
||||
v-for="person in people"
|
||||
:key="person.id"
|
||||
:value="person"
|
||||
:className="resolveListboxOptionClassName"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
@@ -47,7 +47,7 @@
|
||||
classNames('block truncate', selected ? 'font-semibold' : 'font-normal')
|
||||
"
|
||||
>
|
||||
{{ name }}
|
||||
{{ person.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="selected"
|
||||
@@ -94,16 +94,16 @@ export default {
|
||||
components: { Listbox, ListboxLabel, ListboxButton, ListboxOptions, ListboxOption },
|
||||
setup(props, context) {
|
||||
const people = [
|
||||
'Wade Cooper',
|
||||
'Arlene Mccoy',
|
||||
'Devon Webb',
|
||||
'Tom Cook',
|
||||
'Tanya Fox',
|
||||
'Hellen Schmidt',
|
||||
'Caroline Schultz',
|
||||
'Mason Heaney',
|
||||
'Claudie Smitham',
|
||||
'Emil Schaefer',
|
||||
{ id: 1, name: 'Wade Cooper' },
|
||||
{ id: 2, name: 'Arlene Mccoy' },
|
||||
{ id: 3, name: 'Devon Webb' },
|
||||
{ id: 4, name: 'Tom Cook' },
|
||||
{ id: 5, name: 'Tanya Fox' },
|
||||
{ id: 6, name: 'Hellen Schmidt' },
|
||||
{ id: 7, name: 'Caroline Schultz' },
|
||||
{ id: 8, name: 'Mason Heaney' },
|
||||
{ id: 9, name: 'Claudie Smitham' },
|
||||
{ id: 10, name: 'Emil Schaefer' },
|
||||
]
|
||||
|
||||
const active = ref(people[Math.floor(Math.random() * people.length)])
|
||||
|
||||
@@ -622,6 +622,61 @@ describe('Keyboard interactions', () => {
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should be possible to open the listbox with Enter, and focus the selected option (with a list of objects)',
|
||||
suppressConsoleLogs(async () => {
|
||||
renderTemplate({
|
||||
template: `
|
||||
<Listbox v-model="value">
|
||||
<ListboxButton>Trigger</ListboxButton>
|
||||
<ListboxOptions>
|
||||
<ListboxOption v-for="option in options" key="option.id" :value="option">{{ option.name }}</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</Listbox>
|
||||
`,
|
||||
setup: () => {
|
||||
const options = [
|
||||
{ id: 'a', name: 'Option A' },
|
||||
{ id: 'b', name: 'Option B' },
|
||||
{ id: 'c', name: 'Option C' },
|
||||
]
|
||||
const value = ref(options[1])
|
||||
|
||||
return { value, options }
|
||||
},
|
||||
})
|
||||
|
||||
assertListboxButton({
|
||||
state: ListboxState.Closed,
|
||||
attributes: { id: 'headlessui-listbox-button-1' },
|
||||
})
|
||||
assertListbox({ state: ListboxState.Closed })
|
||||
|
||||
// Focus the button
|
||||
getListboxButton()?.focus()
|
||||
|
||||
// Open listbox
|
||||
await press(Keys.Enter)
|
||||
|
||||
// Verify it is open
|
||||
assertListboxButton({ state: ListboxState.Open })
|
||||
assertListbox({
|
||||
state: ListboxState.Open,
|
||||
attributes: { id: 'headlessui-listbox-options-2' },
|
||||
})
|
||||
assertActiveElement(getListbox())
|
||||
assertListboxButtonLinkedWithListbox()
|
||||
|
||||
// Verify we have listbox options
|
||||
const options = getListboxOptions()
|
||||
expect(options).toHaveLength(3)
|
||||
options.forEach((option, i) => assertListboxOption(option, { selected: i === 1 }))
|
||||
|
||||
// Verify that the second listbox option is active (because it is already selected)
|
||||
assertActiveListboxOption(options[1])
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should have no active listbox option when there are no listbox options at all',
|
||||
suppressConsoleLogs(async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Ref,
|
||||
ComputedRef,
|
||||
watchEffect,
|
||||
toRaw,
|
||||
} from 'vue'
|
||||
import { match } from '../../utils/match'
|
||||
import { render } from '../../utils/render'
|
||||
@@ -476,7 +477,7 @@ export const ListboxOption = defineComponent({
|
||||
: false
|
||||
})
|
||||
|
||||
const selected = computed(() => api.value.value === value)
|
||||
const selected = computed(() => toRaw(api.value.value) === toRaw(value))
|
||||
|
||||
const dataRef = ref<ListboxOptionDataRef['value']>({ disabled, value, textValue: '' })
|
||||
onMounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user