From 72cc8031e71ed3d9d83e87661dc3bd8e9cb12740 Mon Sep 17 00:00:00 2001 From: Powerplex Date: Fri, 15 Mar 2024 15:59:44 +0100 Subject: [PATCH] feat(combobox): controlled combobox open state --- package-lock.json | 1 + .../components/combobox/src/Combobox.doc.mdx | 8 +- .../combobox/src/Combobox.stories.tsx | 84 ++++++++++++++----- .../combobox/src/ComboboxContext.tsx | 23 +++-- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cec9ec3b..8a7686ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2400,6 +2400,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/packages/components/combobox/src/Combobox.doc.mdx b/packages/components/combobox/src/Combobox.doc.mdx index 187c47635..6475b4295 100644 --- a/packages/components/combobox/src/Combobox.doc.mdx +++ b/packages/components/combobox/src/Combobox.doc.mdx @@ -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. @@ -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. + ### Disabled diff --git a/packages/components/combobox/src/Combobox.stories.tsx b/packages/components/combobox/src/Combobox.stories.tsx index 906c192d6..c63984fcc 100644 --- a/packages/components/combobox/src/Combobox.stories.tsx +++ b/packages/components/combobox/src/Combobox.stories.tsx @@ -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' @@ -47,12 +48,39 @@ export const Default: StoryFn = _args => { ) } -export const Controlled: StoryFn = _args => { +export const Controlled: StoryFn = () => { const [value, setValue] = useState('book-2') + const [open, setOpen] = useState(true) return (
- +
+ + Opened state: + + {open ? 'Opened' : 'Closed'} + + + + Selected item: + + To Kill a Mockingbird + War and Peace + The Idiot + A Picture of Dorian Gray + 1984 + Pride and Prejudice + + +
+ + @@ -74,15 +102,6 @@ export const Controlled: StoryFn = _args => { - - - To Kill a Mockingbird - War and Peace - The Idiot - A Picture of Dorian Gray - 1984 - Pride and Prejudice -
) } @@ -439,21 +458,40 @@ export const MultipleSelection: StoryFn = _args => { ) } -export const MultipleSelectionControlled: StoryFn = _args => { +export const MultipleSelectionControlled: StoryFn = () => { const [value, setValue] = useState(['book-2']) + const [open, setOpen] = useState(false) return ( -
- - To Kill a Mockingbird - War and Peace - The Idiot - A Picture of Dorian Gray - 1984 - Pride and Prejudice - - - +
+
+ + Opened state: + + {open ? 'Opened' : 'Closed'} + + + + Selected items: + + To Kill a Mockingbird + War and Peace + The Idiot + A Picture of Dorian Gray + 1984 + Pride and Prejudice + + +
+ + diff --git a/packages/components/combobox/src/ComboboxContext.tsx b/packages/components/combobox/src/ComboboxContext.tsx index b178eabd1..7fc51c92f 100644 --- a/packages/components/combobox/src/ComboboxContext.tsx +++ b/packages/components/combobox/src/ComboboxContext.tsx @@ -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) @@ -281,10 +285,15 @@ export const ComboboxProvider = ({ const downshift = useCombobox({ 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 => {