Skip to content

Commit

Permalink
Improve Combobox Input value (#1248)
Browse files Browse the repository at this point in the history
* improve Combobox Input value

* update changelog
  • Loading branch information
RobinMalfait authored Mar 16, 2022
1 parent 40fee45 commit c9883f6
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use `ownerDocument` instead of `document` ([#1158](https://github.com/tailwindlabs/headlessui/pull/1158))
- Ensure focus trap, Tabs and Dialog play well together ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231))
- Add `multi` value support for Listbox & Combobox ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243))
- Improve Combobox Input value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248))

### Added

Expand Down Expand Up @@ -56,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Re-expose `el` ([#1230](https://github.com/tailwindlabs/headlessui/pull/1230))
- Ensure focus trap, Tabs and Dialog play well together ([#1231](https://github.com/tailwindlabs/headlessui/pull/1231))
- Add `multi` value support for Listbox & Combobox ([#1243](https://github.com/tailwindlabs/headlessui/pull/1243))
- Improve Combobox Input value ([#1248](https://github.com/tailwindlabs/headlessui/pull/1248))

### Added

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4393,6 +4393,59 @@ describe('Mouse interactions', () => {
expect(getComboboxInput()?.value).toBe('')
})
)

it(
'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)',
suppressConsoleLogs(async () => {
let people = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
]

function Example() {
let [value, setValue] = useState<typeof people[number] | null>(people[1])

return (
<>
<Combobox value={value} onChange={setValue}>
<Combobox.Input
onChange={NOOP}
displayValue={(person: typeof people[number]) => person?.name}
/>
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
{people.map((person) => (
<Combobox.Option key={person.id} value={person}>
{person.name}
</Combobox.Option>
))}
</Combobox.Options>
</Combobox>
<button onClick={() => setValue(null)}>reset</button>
</>
)
}

render(<Example />)

// Open combobox
await click(getComboboxButton())

// Verify the input has the selected value
expect(getComboboxInput()?.value).toBe('Bob')

// Override the input by typing something
await type(word('test'), getComboboxInput())
expect(getComboboxInput()?.value).toBe('test')

// Reset from outside
await click(getByText('reset'))

// Verify the input is reset correctly
expect(getComboboxInput()?.value).toBe('')
})
)
})

describe('Multi-select', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,10 @@ let ComboboxRoot = forwardRefWithAs(function Combobox<

let syncInputValue = useCallback(() => {
if (!inputRef.current) return
if (value === undefined) return
let displayValue = inputPropsRef.current.displayValue

if (typeof displayValue === 'function') {
inputRef.current.value = displayValue(value)
inputRef.current.value = displayValue(value) ?? ''
} else if (typeof value === 'string') {
inputRef.current.value = value
} else {
Expand Down
48 changes: 48 additions & 0 deletions packages/@headlessui-vue/src/components/combobox/combobox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4615,6 +4615,54 @@ describe('Mouse interactions', () => {
expect(getComboboxInput()?.value).toBe('')
})
)

it(
'should sync the input field correctly and reset it when resetting the value from outside (when using displayValue)',
suppressConsoleLogs(async () => {
renderTemplate({
template: html`
<Combobox v-model="value">
<ComboboxInput :displayValue="person => person?.name" />
<ComboboxButton>Trigger</ComboboxButton>
<ComboboxOptions>
<ComboboxOption v-for="person in people" :key="person.id" :value="person"
>{{ person.name }}</ComboboxOption
>
</ComboboxOptions>
</Combobox>
<button @click="value = null">reset</button>
`,
setup: () => {
let people = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
]

return {
people,
value: ref(people[1]),
}
},
})

// Open combobox
await click(getComboboxButton())

// Verify the input has the selected value
expect(getComboboxInput()?.value).toBe('Bob')

// Override the input by typing something
await type(word('test'), getComboboxInput())
expect(getComboboxInput()?.value).toBe('test')

// Reset from outside
await click(getByText('reset'))

// Verify the input is reset correctly
expect(getComboboxInput()?.value).toBe('')
})
)
})

describe('Multi-select', () => {
Expand Down
3 changes: 1 addition & 2 deletions packages/@headlessui-vue/src/components/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,10 @@ export let Combobox = defineComponent({
syncInputValue() {
let value = api.value.value
if (!dom(api.inputRef)) return
if (value === undefined) return
let displayValue = api.inputPropsRef.value.displayValue

if (typeof displayValue === 'function') {
api.inputRef!.value!.value = displayValue(value)
api.inputRef!.value!.value = displayValue(value) ?? ''
} else if (typeof value === 'string') {
api.inputRef!.value!.value = value
} else {
Expand Down

0 comments on commit c9883f6

Please sign in to comment.