Skip to content

Commit

Permalink
Merge pull request #1970 from adevinta/combobox-controlled-open-state
Browse files Browse the repository at this point in the history
feat(combobox): controlled combobox open state
  • Loading branch information
Powerplex authored Mar 15, 2024
2 parents 2a6b17f + 72cc803 commit 4a9b25a
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 31 deletions.
1 change: 1 addition & 0 deletions package-lock.json

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

8 changes: 7 additions & 1 deletion packages/components/combobox/src/Combobox.doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ AutoSuggest is used as the default behaviour. The user can type anything in the

### Controlled

Use `value` and `onValueChange` props to control the value of the combobox.
Use `value` and `onValueChange` props to control the selected item of the combobox.

Use `open` and `onOpenChange` props to control the opened state of the combobox menu.

<Canvas of={stories.Controlled} />

Expand Down Expand Up @@ -178,6 +180,10 @@ This is up to the developer to make it clear to the user which items are selecte

### Controlled

Use `value` and `onValueChange` props to control the selected items of the combobox.

Use `open` and `onOpenChange` props to control the opened state of the combobox menu.

<Canvas of={stories.MultipleSelectionControlled} />

### Disabled
Expand Down
84 changes: 61 additions & 23 deletions packages/components/combobox/src/Combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Checkbox, CheckboxGroup } from '@spark-ui/checkbox'
import { FormField } from '@spark-ui/form-field'
import { PenOutline } from '@spark-ui/icons/dist/icons/PenOutline'
import { RadioGroup } from '@spark-ui/radio-group'
import { Switch } from '@spark-ui/switch'
import { Tag } from '@spark-ui/tag'
import { VisuallyHidden } from '@spark-ui/visually-hidden'
import { Meta, StoryFn } from '@storybook/react'
Expand Down Expand Up @@ -47,12 +48,39 @@ export const Default: StoryFn = _args => {
)
}

export const Controlled: StoryFn = _args => {
export const Controlled: StoryFn = () => {
const [value, setValue] = useState<string | undefined>('book-2')
const [open, setOpen] = useState(true)

return (
<div className="flex flex-wrap gap-lg pb-[300px]">
<Combobox autoFilter={false} value={value} onValueChange={setValue}>
<div className="flex flex-col gap-lg">
<FormField>
<FormField.Label className="font-bold">Opened state:</FormField.Label>
<Switch checked={open} onCheckedChange={setOpen}>
{open ? 'Opened' : 'Closed'}
</Switch>
</FormField>
<FormField>
<FormField.Label className="font-bold">Selected item:</FormField.Label>
<RadioGroup value={value || ''} onValueChange={setValue}>
<RadioGroup.Radio value="book-1">To Kill a Mockingbird</RadioGroup.Radio>
<RadioGroup.Radio value="book-2">War and Peace</RadioGroup.Radio>
<RadioGroup.Radio value="book-3">The Idiot</RadioGroup.Radio>
<RadioGroup.Radio value="book-4">A Picture of Dorian Gray</RadioGroup.Radio>
<RadioGroup.Radio value="book-5">1984</RadioGroup.Radio>
<RadioGroup.Radio value="book-6">Pride and Prejudice</RadioGroup.Radio>
</RadioGroup>
</FormField>
</div>

<Combobox
open={open}
onOpenChange={setOpen}
value={value}
onValueChange={setValue}
autoFilter={false}
>
<Combobox.Trigger className="grow">
<Combobox.LeadingIcon>
<PenOutline />
Expand All @@ -74,15 +102,6 @@ export const Controlled: StoryFn = _args => {
</Combobox.Items>
</Combobox.Popover>
</Combobox>

<RadioGroup value={value || ''} onValueChange={setValue} className="grow">
<RadioGroup.Radio value="book-1">To Kill a Mockingbird</RadioGroup.Radio>
<RadioGroup.Radio value="book-2">War and Peace</RadioGroup.Radio>
<RadioGroup.Radio value="book-3">The Idiot</RadioGroup.Radio>
<RadioGroup.Radio value="book-4">A Picture of Dorian Gray</RadioGroup.Radio>
<RadioGroup.Radio value="book-5">1984</RadioGroup.Radio>
<RadioGroup.Radio value="book-6">Pride and Prejudice</RadioGroup.Radio>
</RadioGroup>
</div>
)
}
Expand Down Expand Up @@ -439,21 +458,40 @@ export const MultipleSelection: StoryFn = _args => {
)
}

export const MultipleSelectionControlled: StoryFn = _args => {
export const MultipleSelectionControlled: StoryFn = () => {
const [value, setValue] = useState<string[]>(['book-2'])
const [open, setOpen] = useState(false)

return (
<div className="flex flex-col gap-lg pb-[300px]">
<CheckboxGroup value={value} onCheckedChange={setValue} className="grow">
<Checkbox value="book-1">To Kill a Mockingbird</Checkbox>
<Checkbox value="book-2">War and Peace</Checkbox>
<Checkbox value="book-3">The Idiot</Checkbox>
<Checkbox value="book-4">A Picture of Dorian Gray</Checkbox>
<Checkbox value="book-5">1984</Checkbox>
<Checkbox value="book-6">Pride and Prejudice</Checkbox>
</CheckboxGroup>

<Combobox multiple autoFilter={false} value={value} onValueChange={setValue}>
<div className="flex flex-wrap gap-lg pb-[300px]">
<div className="flex flex-col gap-lg">
<FormField>
<FormField.Label className="font-bold">Opened state:</FormField.Label>
<Switch checked={open} onCheckedChange={setOpen}>
{open ? 'Opened' : 'Closed'}
</Switch>
</FormField>
<FormField>
<FormField.Label className="font-bold">Selected items:</FormField.Label>
<CheckboxGroup value={value} onCheckedChange={setValue} className="grow">
<Checkbox value="book-1">To Kill a Mockingbird</Checkbox>
<Checkbox value="book-2">War and Peace</Checkbox>
<Checkbox value="book-3">The Idiot</Checkbox>
<Checkbox value="book-4">A Picture of Dorian Gray</Checkbox>
<Checkbox value="book-5">1984</Checkbox>
<Checkbox value="book-6">Pride and Prejudice</Checkbox>
</CheckboxGroup>
</FormField>
</div>

<Combobox
multiple
autoFilter={false}
value={value}
onValueChange={setValue}
open={open}
onOpenChange={setOpen}
>
<Combobox.Trigger>
<Combobox.SelectedItems />
<Combobox.Input aria-label="Book" placeholder="Pick a book" />
Expand Down
23 changes: 16 additions & 7 deletions packages/components/combobox/src/ComboboxContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,22 @@ const getFilteredItemsMap = (map: ItemsMap, inputValue: string | undefined): Ite
}

export const ComboboxProvider = ({
children,
state: stateProp,
allowCustomValue = false,
autoFilter = true,
children,
defaultOpen,
defaultValue,
disabled: disabledProp = false,
multiple = false,
onValueChange,
readOnly: readOnlyProp = false,
state: stateProp,
value: controlledValue,
wrap = true,
// Value
value: controlledValue,
defaultValue,
onValueChange,
// Open
open: controlledOpen,
defaultOpen,
onOpenChange,
}: ComboboxContextProps) => {
const isMounted = useRef(false)

Expand Down Expand Up @@ -281,10 +285,15 @@ export const ComboboxProvider = ({
const downshift = useCombobox<ComboboxItem>({
items: filteredItems,
selectedItem,
// initialSelectedItem: comboboxValue ? itemsMap.get(comboboxValue as string) : undefined,
id,
labelId,
inputValue,
...(controlledOpen != null && { isOpen: controlledOpen }),
onIsOpenChange: changes => {
if (changes.isOpen != null) {
onOpenChange?.(changes.isOpen)
}
},
initialIsOpen: defaultOpen,
...(multiple && { selectedItem: undefined }),
itemToString: item => {
Expand Down

0 comments on commit 4a9b25a

Please sign in to comment.