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

Make the Combobox component nullable by default #3064

Merged
merged 14 commits into from
Mar 29, 2024
Merged
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
20 changes: 17 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@
"prettier-plugin-tailwindcss": "^0.5.7",
"rimraf": "^3.0.2",
"tslib": "^2.3.1",
"typescript": "^5.3.2"
"typescript": "^5.4.3"
}
}
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Attempt form submission when pressing `Enter` on the `<Listbox.Button />` component ([#2972](https://github.com/tailwindlabs/headlessui/pull/2972))
- Make the `Combobox` component `nullable` by default ([#3064](https://github.com/tailwindlabs/headlessui/pull/3064))

### Added

Expand Down
thecrypticace marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,13 @@ describe('Rendering', () => {
'should be possible to use completely new objects while rendering (single mode)',
suppressConsoleLogs(async () => {
function Example() {
let [value, setValue] = useState({ id: 2, name: 'Bob' })
let [value, setValue] = useState<{ id: number; name: string } | null>({
id: 2,
name: 'Bob',
})

return (
<Combobox value={value} onChange={(value) => setValue(value)} by="id">
<Combobox value={value} onChange={setValue} by="id">
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
<Combobox.Option value={{ id: 1, name: 'alice' }}>alice</Combobox.Option>
Expand Down Expand Up @@ -432,7 +435,7 @@ describe('Rendering', () => {
let [value, setValue] = useState([{ id: 2, name: 'Bob' }])

return (
<Combobox value={value} onChange={(value) => setValue(value)} by="id" multiple>
<Combobox value={value} onChange={setValue} by="id" multiple>
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
<Combobox.Option value={{ id: 1, name: 'alice' }}>alice</Combobox.Option>
Expand Down Expand Up @@ -472,7 +475,7 @@ describe('Rendering', () => {
]

render(
<Combobox name="assignee" by="id">
<Combobox<(typeof data)[number]> name="assignee" by="id">
<Combobox.Input
displayValue={(value: { name: string }) => value.name}
onChange={NOOP}
Expand All @@ -499,7 +502,7 @@ describe('Rendering', () => {
]

function Example() {
let [person, setPerson] = useState(data[1])
let [person, setPerson] = useState<(typeof data)[number] | null>(data[1])

return (
<Combobox value={person} onChange={setPerson} name="assignee" by="id">
Expand Down Expand Up @@ -546,7 +549,7 @@ describe('Rendering', () => {
]

render(
<Combobox name="assignee" by="id">
<Combobox<(typeof data)[number]> name="assignee" by="id">
<Combobox.Input onChange={NOOP} />
<Combobox.Button />
<Combobox.Options>
Expand Down Expand Up @@ -647,7 +650,7 @@ describe('Rendering', () => {
'selecting an option puts the display value into Combobox.Input when displayValue is provided (when value is undefined)',
suppressConsoleLogs(async () => {
function Example() {
let [value, setValue] = useState(undefined)
let [value, setValue] = useState<null | undefined>(undefined)

return (
<Combobox value={value} onChange={setValue}>
Expand Down Expand Up @@ -692,7 +695,7 @@ describe('Rendering', () => {

return (
<>
<Combobox value={value} onChange={setValue} nullable>
<Combobox value={value} onChange={setValue}>
<Combobox.Input
onChange={NOOP}
displayValue={(str?: string) =>
Expand Down Expand Up @@ -766,7 +769,7 @@ describe('Rendering', () => {
'should reflect the value in the input when the value changes and when you are typing',
suppressConsoleLogs(async () => {
function Example() {
let [value, setValue] = useState('bob')
let [value, setValue] = useState<string | null>('bob')
let [_query, setQuery] = useState('')

return (
Expand Down Expand Up @@ -1608,7 +1611,11 @@ describe('Rendering', () => {
handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))
}}
>
<Combobox name="assignee" defaultValue={{ id: 2, name: 'bob', label: 'Bob' }} by="id">
<Combobox<(typeof data)[number]>
name="assignee"
defaultValue={{ id: 2, name: 'bob', label: 'Bob' }}
by="id"
>
<Combobox.Button>{({ value }) => value?.name ?? 'Trigger'}</Combobox.Button>
<Combobox.Input
onChange={NOOP}
Expand Down Expand Up @@ -4093,42 +4100,20 @@ describe.each([{ virtual: true }, { virtual: false }])(

describe('`Backspace` key', () => {
it(
'should reset the value when the last character is removed, when in `nullable` mode',
'should reset the value when the last character is removed',
suppressConsoleLogs(async () => {
let handleChange = jest.fn()
function Example() {
let [value, setValue] = useState<string | null>('bob')
let [, setQuery] = useState<string>('')

// return (
// <MyCombobox
// options={[
// { value: 'alice', children: 'Alice' },
// { value: 'bob', children: 'Bob' },
// { value: 'charlie', children: 'Charlie' },
// ]}
// comboboxProps={{
// value,
// onChange: (value: any) => {
// setValue(value)
// handleChange(value)
// },
// nullable: true,
// }}
// inputProps={{
// onChange: (event: any) => setQuery(event.target.value),
// }}
// />
// )

return (
<Combobox
value={value}
onChange={(value) => {
setValue(value)
handleChange(value)
}}
nullable
>
<Combobox.Input onChange={(event) => setQuery(event.target.value)} />
<Combobox.Button>Trigger</Combobox.Button>
Expand Down Expand Up @@ -4175,7 +4160,7 @@ describe.each([{ virtual: true }, { virtual: false }])(
await press(Keys.Backspace)
expect(getComboboxInput()?.value).toBe('')

// Verify that we don't have an selected option anymore since we are in `nullable` mode
// Verify that we don't have an selected option anymore
assertNotActiveComboboxOption(options[1])
assertNoSelectedComboboxOption()

Expand All @@ -4189,7 +4174,7 @@ describe.each([{ virtual: true }, { virtual: false }])(
describe('`Any` key aka search', () => {
type Option = { value: string; name: string; disabled: boolean }
function Example(props: { people: { value: string; name: string; disabled: boolean }[] }) {
let [value, setValue] = useState<Option | undefined>(undefined)
let [value, setValue] = useState<Option | null | undefined>(undefined)
let [query, setQuery] = useState<string>('')
let filteredPeople =
query === ''
Expand All @@ -4207,7 +4192,7 @@ describe.each([{ virtual: true }, { virtual: false }])(
}}
value={value}
by="value"
onChange={(value) => setValue(value)}
onChange={setValue}
>
<Combobox.Input onChange={(event) => setQuery(event.target.value)} />
<Combobox.Button>Trigger</Combobox.Button>
Expand Down Expand Up @@ -5502,7 +5487,7 @@ describe('Multi-select', () => {
let [value, setValue] = useState<string[]>(['bob', 'charlie'])

return (
<Combobox value={value} onChange={(value) => setValue(value)} multiple>
<Combobox value={value} onChange={setValue} multiple>
<Combobox.Input onChange={() => {}} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
Expand Down Expand Up @@ -5538,7 +5523,7 @@ describe('Multi-select', () => {
let [value, setValue] = useState<string[]>(['bob', 'charlie'])

return (
<Combobox value={value} onChange={(value) => setValue(value)} multiple>
<Combobox value={value} onChange={setValue} multiple>
<Combobox.Input onChange={() => {}} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
Expand Down Expand Up @@ -5567,7 +5552,7 @@ describe('Multi-select', () => {
let [value, setValue] = useState<string[]>(['bob', 'charlie'])

return (
<Combobox value={value} onChange={(value) => setValue(value)} multiple>
<Combobox value={value} onChange={setValue} multiple>
<Combobox.Input onChange={() => {}} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
Expand Down Expand Up @@ -5600,7 +5585,7 @@ describe('Multi-select', () => {
let [value, setValue] = useState<string[]>(['bob', 'charlie'])

return (
<Combobox value={value} onChange={(value) => setValue(value)} multiple>
<Combobox value={value} onChange={setValue} multiple>
<Combobox.Input onChange={() => {}} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
Expand Down Expand Up @@ -5648,7 +5633,7 @@ describe('Multi-select', () => {
let [value, setValue] = useState<string[]>([])

return (
<Combobox value={value} onChange={(value) => setValue(value)} multiple>
<Combobox value={value} onChange={setValue} multiple>
<Combobox.Input onChange={() => {}} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Options>
Expand Down Expand Up @@ -5798,7 +5783,7 @@ describe('Form compatibility', () => {
let submits = jest.fn()

function Example() {
let [value, setValue] = useState('home-delivery')
let [value, setValue] = useState<string | null>('home-delivery')
return (
<form
onSubmit={(event) => {
Expand Down Expand Up @@ -5860,7 +5845,7 @@ describe('Form compatibility', () => {
]

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

return (
<form
Expand Down
Loading
Loading