Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add defaultToFirstOption prop to Combobox #3564

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/@headlessui-vue/src/components/combobox/combobox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2708,6 +2708,46 @@ describe.each([{ virtual: true }, { virtual: false }])(
assertNoActiveComboboxOption()
})
)

it(
'should be possible to go to the first item if no value is set and first option is not active by default',
suppressConsoleLogs(async () => {
renderTemplate({
template: html`
<Combobox v-model="value" :default-to-first-option="false" as="div">
<ComboboxInput />
<ComboboxButton>Trigger</ComboboxButton>
<ComboboxOptions>
<ComboboxOption value="a">Option A</ComboboxOption>
<ComboboxOption value="b">Option B</ComboboxOption>
<ComboboxOption value="c">Option C</ComboboxOption>
</ComboboxOptions>
</Combobox>
`,
setup: () => ({ value: ref(null) }),
})

assertComboboxButton({
state: ComboboxState.InvisibleUnmounted,
attributes: { id: 'headlessui-combobox-button-2' },
})
assertComboboxList({ state: ComboboxState.InvisibleUnmounted })

// Open combobox
await click(getComboboxButton())

let options = getComboboxOptions()

// Verify that we have no active option
assertNoActiveComboboxOption()

// Go down once
await press(Keys.ArrowDown)

// We should be on the next item
assertActiveComboboxOption(options[0])
})
)
})

describe('`ArrowUp` key', () => {
Expand Down
6 changes: 4 additions & 2 deletions packages/@headlessui-vue/src/components/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export let Combobox = defineComponent({
>,
default: undefined,
},
defaultToFirstOption: { type: [Boolean], default: false },
form: { type: String, optional: true },
name: { type: String, optional: true },
nullable: { type: Boolean, default: false },
Expand Down Expand Up @@ -459,7 +460,7 @@ export let Combobox = defineComponent({
activeOptionIndex.value = null
},
openCombobox() {
defaultToFirstOption.value = true
defaultToFirstOption.value = props.defaultToFirstOption

if (props.disabled) return
if (comboboxState.value === ComboboxStates.Open) return
Expand Down Expand Up @@ -526,7 +527,7 @@ export let Combobox = defineComponent({
// It's possible that the activeOptionIndex is set to `null` internally, but
// this means that we will fallback to the first non-disabled option by default.
// We have to take this into account.
if (adjustedState.activeOptionIndex === null) {
if (adjustedState.activeOptionIndex === null && props.defaultToFirstOption) {
let localActiveOptionIndex = adjustedState.options.findIndex(
(option) => !option.dataRef.disabled
)
Expand Down Expand Up @@ -743,6 +744,7 @@ export let Combobox = defineComponent({
...omit(theirProps, [
'by',
'defaultValue',
'defaultToFirstOption',
'immediate',
'modelValue',
'multiple',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default defineComponent({
},
setup() {
let query = ref('')
let activePerson = ref(everybody[2]) // everybody[Math.floor(Math.random() * everybody.length)]
let activePerson = ref(null)
let filteredPeople = computed(() => {
return query.value === ''
? everybody
Expand All @@ -42,11 +42,6 @@ export default defineComponent({
})
})

// Choose a random person on mount
onMounted(() => {
activePerson.value = everybody[Math.floor(Math.random() * everybody.length)]
})

watch(activePerson, () => {
query.value = ''
})
Expand All @@ -67,7 +62,7 @@ export default defineComponent({
Selected person: {{ activePerson?.name ?? 'Nobody yet' }}
</div>
<div class="space-y-1">
<Combobox v-model="activePerson" as="div">
<Combobox v-model="activePerson" as="div" nullable :default-to-first-option="false">
<ComboboxLabel class="block text-sm font-medium leading-5 text-gray-700">
Assigned to
</ComboboxLabel>
Expand Down