Components
Listbox
Listboxes allow a user to select a value or multiple values from a custom-styled dropdown.
Listboxes should only be used when multiselect or custom styles are needed. If you can get by with using the browser styles provided by the {' '} component, you should use it first.
While Listbox is similar to the component, they differ in that listboxes represent a selection by the user. Items in a listbox do not have their own individual actions. You can use the ListboxButton component to easily present a listbox in a popout with a button that matches the appearance of our component.
import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {const { value, onChange } = useSelect({ initialValue: null })return (<Box display="flex" alignItems="center"><Text id="mylabel" mr={300} fontSize={300}>Favorite Fruit</Text><ListboxButtonminWidth="150px"aria-labelledby="mylabel"content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group></Listbox>}>{value || "Select a fruit"}</ListboxButton></Box>)}
Properties
Name | Type | Default | Description | Required? |
---|---|---|---|---|
children | React.Node | |||
value | ?string | string[] | Any item(s) matching this value will be rendered in a selected state | ||
onChange | (event) => () | Called whenever a new value is selected. | ||
innerRef | React.Ref | Used if you need to compose Listbox with Downshift | ||
multiselect | boolean | false | Whether or not the listbox should allow multiple selections | |
footerContent | React.Node | Content to display below the Listbox.Item s. Any interactive elements that you want to display at the bottom of the Listbox should be passed here, not as children. |
Accessible labels
There are two ways to make sure a Listbox has an accessible label.
- Use
aria-labelledby
with the same value as anid
on either text nearby that serves as a label or you can use VisuallyHidden text. Note that theid
of the button component will be appended to the value ofaria-labelledby
for accessibility. This example will read "Favorite Fruit Select a Fruit".
import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {const { value, onChange } = useSelect({ initialValue: null })return (<Box display="flex" alignItems="center"><Text id="mylabel" mr={300} fontSize={300}>Favorite Fruit</Text><ListboxButtonminWidth="150px"aria-labelledby="mylabel"content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group></Listbox>}>{value || "Select a fruit"}</ListboxButton></Box>)}
- Use
aria-label
with a text value that describes the function of the control. Note that the value of the button will be appended to the value ofaria-label
for accessibility. This example will also read "Favorite Fruit Select a Fruit".
import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {const { value, onChange } = useSelect({ initialValue: null })return (<ListboxButtonminWidth="150px"aria-label="Favorite Fruit"content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group></Listbox>}>{value || "Select a fruit"}</ListboxButton>)}
Subcomponents
Listbox group
Groups are used to provide default padding and spacing around items, and can optionally render a visual label for a group of items.
<Listbox.Group title="Fruit"><Box bg="container.background.base" border={500} borderColor="container.border.base" p={400} /></Listbox.Group>
Name | Type | Default | Description | Required? |
---|---|---|---|---|
children | React.Node | |||
title | React.Ref | If provided, a group header will be rendered in the listbox | ||
appearance | default , flush | default | Use the flush appearance if you're using the listbox group with a title outside of a popout | |
disabled | boolean | false | If all of the items in a group are disabled, the group title should also be disabled |
Listbox item
Represents a unique value within a listbox. Selected items are indicated with a check icon and text.
<Box><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange" selected>Orange</Listbox.Item></Box>
Name | Type | Default | Description | Required? |
---|---|---|---|---|
children | React.Node | |||
disabled | boolean | false | ||
value | string | |||
elemBefore | React.Node | A component to be rendered before the children of the menu item | ||
elemAfter | React.Node | A component to be rendered after the children of the menu item |
Listbox Checkbox
A listbox item with a visible representing a selection.
<Listbox.Checkbox value="Apple">Apple</Listbox.Checkbox>
Listbox Radio
A listbox item with a visible representing a selection.
<Listbox.Radio value="Apple">Apple</Listbox.Radio>
Listbox Switch
A Listbox Item with a visible representing a toggleable option. Use the placement
prop to determine whether the Switch is positioned before its children or after the children. It will override the elemBefore
or elemAfter
prop of Listbox.Item accordingly.
() => {const [isChecked, setIsChecked] = useState(true);return (<Listbox.Switch placement="after" onClick={() => setIsChecked(!isChecked)} selected={isChecked}>Show Archived Tags</Listbox.Switch>);}
Switches may also be used directly in the footer of the Listbox using when appropriate. Do not use Listbox.Switch in this case.
Listbox.Switch accepts the following props in addition to Listbox.Item's props:
Name | Type | Default | Description | Required? |
---|---|---|---|---|
placement | before ,after | Should the Switch be rendered in the elemBefore or elemAfter position? | ||
switchProps | Omit<TypeSwitchProps, 'onClick'> | Any additional props to pass to the Switch. onClick is omitted because the top-level onClick prop should be used instead. | ||
labelProps | Omit<TypeTextProps, 'children'> | Any additional props to add to the Switch's label. children is omitted because Listbox.Switch's children is rendered as the label's children. |
Listbox filter input
A special version of that can be used to allow the user to filter items within a listbox.
() => {const { value, onChange } = useSelect({ initialValue: null })return (<ListboxButtonminWidth="200px"aria-label="Favorite fruit"content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.FilterInput mb={300} /><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group></Listbox>}>{value || "Select a fruit"}</ListboxButton>)}
Listbox divider
A horizontal line with the "separator" role. Accepts the same props as
Recipes
Changing the listbox button size
ListboxButton offers a small
and large
size for matching the size of other elements in a form. The following example shows the small
size:
() => {const { value, onChange } = useSelect({ initialValue: null })return (<ListboxButtonminWidth="150px"aria-label="Favorite fruit"size='small'content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group></Listbox>}>{value || 'Select a fruit'}</ListboxButton>)}
Multiselect
() => {const { value, onChange } = useMultiselect()const currentOption =value.length > 1 ? value.length + ' selected' : value.length ? value : nullreturn (<ListboxButtonminWidth="150px"aria-label="Favorite fruit"content={<Listbox multiselect onChange={onChange} value={value} width="200px"><Listbox.Group><Listbox.Checkbox value="Apple">Apple</Listbox.Checkbox><Listbox.Checkbox value="Orange">Orange</Listbox.Checkbox><Listbox.Checkbox value="Banana">Banana</Listbox.Checkbox></Listbox.Group></Listbox>}>{currentOption || "Select a fruit"}</ListboxButton>)}
Groups
() => {const { value, onChange } = useSelect({ initialValue: null })return (<ListboxButtonminWidth="150px"aria-label="Favorite fruit"content={<Listbox onChange={onChange} value={value} width="200px"><Listbox.Group title="Fruit"><Listbox.Item value="Apple">Apple</Listbox.Item><Listbox.Item value="Orange">Orange</Listbox.Item><Listbox.Item value="Banana">Banana</Listbox.Item></Listbox.Group><Listbox.Group title="More fruit"><Listbox.Item value="Kiwi">Kiwi</Listbox.Item><Listbox.Item value="Papaya">Papaya</Listbox.Item><Listbox.Item value="Mango">Mango</Listbox.Item></Listbox.Group></Listbox>}>{value || "Select a fruit"}</ListboxButton>)}
Using dividers and footerContent
One use for Listbox.Divider is to separate Listbox items from the footer. Any information that you would like to display below the items should be passed to Listbox as . It is especially important to use footerContent when including interactive content, as it will not be accessible to keyboard users if passed as children.
<Boxwidth="220px"border={500}borderRadius="outer"borderColor="container.border.base"boxShadow='medium'><ListboxfooterContent={<Box p={350}><Link onClick={() => {}}>View Fruit Help</Link></Box>}><Listbox.Group title="Fruit"><Listbox.Item onClick={() => alert('Apple!')}>Apple</Listbox.Item><Listbox.Item onClick={() => alert('Orange!')}>Orange</Listbox.Item><Listbox.Item onClick={() => alert('Banana!')}>Banana</Listbox.Item></Listbox.Group><Listbox.Divider /></Listbox></Box>