-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react): add Combobox component (#1180)
- Loading branch information
Showing
17 changed files
with
2,469 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,352 @@ | ||
--- | ||
title: Combobox | ||
description: An input component to display a list of suggested options. | ||
source: https://github.com/dequelabs/cauldron/tree/develop/packages/react/src/components/Combobox/Combobox.tsx | ||
--- | ||
|
||
import { useState } from 'react' | ||
import { Combobox, ComboboxOption, ComboboxGroup, FieldWrap } from '@deque/cauldron-react' | ||
|
||
```js | ||
import { | ||
Combobox, | ||
ComboboxOption, | ||
ComboboxGroup | ||
} from '@deque/cauldron-react' | ||
``` | ||
|
||
<Note> | ||
Use [FieldWrap](./FieldWrap) to wrap all input components. The `Combobox` component has been designed specifically to only be used when rendered as a child of this component, and may not be completely accessible if rendered outside of `FieldWrap`. | ||
</Note> | ||
|
||
## Examples | ||
|
||
### With Options | ||
|
||
Options can be rendered from an array of options with a label and an optional value by setting the `options` prop: | ||
|
||
```jsx example | ||
function ComboboxWithOptionsPropExample() { | ||
const options = [ | ||
{ label: 'Red', value: 'Red' }, | ||
{ label: 'Orange', value: 'Orange' }, | ||
{ label: 'Yellow', value: 'Yellow' }, | ||
{ label: 'Green', value: 'Green' }, | ||
{ label: 'Blue', value: 'Blue' } | ||
] | ||
return ( | ||
<FieldWrap> | ||
<Combobox label="Favorite Color" options={options} /> | ||
</FieldWrap> | ||
) | ||
} | ||
``` | ||
|
||
### With Children | ||
|
||
For more advanced scenarios where the rendering of the option label needs to be customized, `ComboboxOption` can be passed in to `Combobox` as `children`. | ||
|
||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Fruit"> | ||
<ComboboxOption value="Apple">🍎 Apple</ComboboxOption> | ||
<ComboboxOption value="Banana">🍌 Banana</ComboboxOption> | ||
<ComboboxOption value="Cucumber">🥒 Cucumber</ComboboxOption> | ||
<ComboboxOption value="Orange">🍊 Orange</ComboboxOption> | ||
<ComboboxOption value="Peach">🍑 Peach</ComboboxOption> | ||
<ComboboxOption value="Pear">🍐 Pear</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
|
||
### Option Descriptions | ||
|
||
Combobox options can optionally include a description, which can be set via a property on the array of options, or as a property on the `ComboboxOption` component. | ||
|
||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Select a Drink"> | ||
<ComboboxOption | ||
description="A monster of a barleywine, with a footprint of caramel and toffee." | ||
value="Bigfoot's Barleywine" | ||
> | ||
Bigfoot's Barleywine | ||
</ComboboxOption> | ||
<ComboboxOption | ||
description="A hoppy explosion of citrus and pine, a true hopocalypse." | ||
value="Hopocalypse IPA" | ||
> | ||
Hopocalypse IPA | ||
</ComboboxOption> | ||
<ComboboxOption | ||
description="A pot of liquid gold at the end of the rainbow." | ||
value="Leprechaun's Gold Lager" | ||
> | ||
Leprechaun's Gold Lager | ||
</ComboboxOption> | ||
<ComboboxOption | ||
description="Delicate, effervescent, with a hint of mythical magic in every sip." | ||
value="Unicorn Tears Saison" | ||
> | ||
Unicorn Tears Saison | ||
</ComboboxOption> | ||
<ComboboxOption | ||
description="Revive your taste buds with this undead burst of citrusy hops." | ||
value="Zombie Zest Pale Ale" | ||
> | ||
Zombie Zest Pale Ale | ||
</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Required | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Is this combobox required?" required> | ||
<ComboboxOption>Yes</ComboboxOption> | ||
<ComboboxOption>No</ComboboxOption> | ||
<ComboboxOption>I'm not sure</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Error | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox | ||
label="Does this combobox have an error?" | ||
error="You must select an option." | ||
required | ||
> | ||
<ComboboxOption>Yes</ComboboxOption> | ||
<ComboboxOption>No</ComboboxOption> | ||
<ComboboxOption>I'm not sure</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Grouping | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Countries"> | ||
<ComboboxGroup label="North America"> | ||
<ComboboxOption>Canada</ComboboxOption> | ||
<ComboboxOption>Mexico</ComboboxOption> | ||
<ComboboxOption>United States</ComboboxOption> | ||
</ComboboxGroup> | ||
<ComboboxGroup label="South America"> | ||
<ComboboxOption>Argentina</ComboboxOption> | ||
<ComboboxOption>Bolivia</ComboboxOption> | ||
<ComboboxOption>Brazil</ComboboxOption> | ||
<ComboboxOption>Chile</ComboboxOption> | ||
<ComboboxOption>Columbia</ComboboxOption> | ||
<ComboboxOption>Ecuador</ComboboxOption> | ||
<ComboboxOption>Falkland Islands</ComboboxOption> | ||
<ComboboxOption>French Guiana</ComboboxOption> | ||
<ComboboxOption>Guyana</ComboboxOption> | ||
<ComboboxOption>Paraguay</ComboboxOption> | ||
<ComboboxOption>Peru</ComboboxOption> | ||
<ComboboxOption>Suriname</ComboboxOption> | ||
<ComboboxOption>Uruguay</ComboboxOption> | ||
<ComboboxOption>Venezuela</ComboboxOption> | ||
</ComboboxGroup> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Uncontrolled | ||
To set the initial value for uncontrolled Comboboxes, use `defaultValue`. | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Fruit" defaultValue="Peach"> | ||
<ComboboxOption value="Apple">🍎 Apple</ComboboxOption> | ||
<ComboboxOption value="Banana">🍌 Banana</ComboboxOption> | ||
<ComboboxOption value="Cucumber">🥒 Cucumber</ComboboxOption> | ||
<ComboboxOption value="Orange">🍊 Orange</ComboboxOption> | ||
<ComboboxOption value="Peach">🍑 Peach</ComboboxOption> | ||
<ComboboxOption value="Pear">🍐 Pear</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Controlled | ||
Comboboxes can also be controlled, by setting the `value` prop and updating the value from event callbacks. | ||
<Note> | ||
`onChange` and `onSelectionChange` have different behaviors, and special consideration needs to be made when `Combobox` is controlled. `onChange` gets called when the value of the input field changes from user input or selections while `onSelectionChange` is only called when a selection changes from the listbox. | ||
</Note> | ||
```jsx example | ||
function ComboboxControlledExample() { | ||
const [value, setValue] = useState('Peach'); | ||
const handleChange = ({ target }) => { | ||
setValue(target.value); | ||
}; | ||
const handleSelectionChange = ({ value }) => { | ||
setValue(value); | ||
}; | ||
return ( | ||
<FieldWrap> | ||
<Combobox | ||
label="Fruit" | ||
value={value} | ||
onChange={handleChange} | ||
onSelectionChange={handleSelectionChange} | ||
> | ||
<ComboboxOption value="Apple">🍎 Apple</ComboboxOption> | ||
<ComboboxOption value="Banana">🍌 Banana</ComboboxOption> | ||
<ComboboxOption value="Cucumber">🥒 Cucumber</ComboboxOption> | ||
<ComboboxOption value="Orange">🍊 Orange</ComboboxOption> | ||
<ComboboxOption value="Peach">🍑 Peach</ComboboxOption> | ||
<ComboboxOption value="Pear">🍐 Pear</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
); | ||
} | ||
``` | ||
## Autocomplete | ||
There are a couple of different methods to control the auto completion of the options in the listbox. By default, autocomplete is set to "manual" meaning selections will be filtered based on the value of the input, and a value will need to be manually selected via click or keypress to change the value of the input field. | ||
### None | ||
When autocomplete is set to "none" the listbox will provide the full list of options and will not change based on the value of the input. | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Auto Completion (None)" autocomplete="none"> | ||
<ComboboxOption>None</ComboboxOption> | ||
<ComboboxOption>Manual</ComboboxOption> | ||
<ComboboxOption>Automatic</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
### Automatic | ||
When autocomplete is set to "automatic" the listbox will provide a filtered list of options with the first value active by default. The active value will become the value of the input when the listbox closes. | ||
```jsx example | ||
<FieldWrap> | ||
<Combobox label="Auto Completion (Automatic)" autocomplete="automatic"> | ||
<ComboboxOption>None</ComboboxOption> | ||
<ComboboxOption>Manual</ComboboxOption> | ||
<ComboboxOption>Automatic</ComboboxOption> | ||
</Combobox> | ||
</FieldWrap> | ||
``` | ||
## Props | ||
### Combobox | ||
<ComponentProps className={true} refType="HTMLDivElement" props={[ | ||
{ | ||
name: 'label', | ||
type: ['string', 'number', 'ReactElement', 'ReactFragment', 'ReactPortal'], | ||
required: true, | ||
description: 'Label for the Combobox input field.' | ||
}, | ||
{ | ||
name: 'options', | ||
type: 'object', | ||
description: 'Array of objects mapping to Combobox options. Alternatively, "ComboboxOption" can be used as children instead of this prop.' | ||
}, | ||
{ | ||
name: 'value', | ||
type: 'string', | ||
description: 'Initial value to be applied to the combobox. Optionally used for "uncontrolled" comboboxes.' | ||
}, | ||
{ | ||
name: 'defaultValue', | ||
type: 'string', | ||
description: 'Value to be applied to the combobox. Optionally used for "controlled" comboboxes.' | ||
}, | ||
{ | ||
name: 'autocomplete', | ||
type: ['none', 'manual', 'automatic'], | ||
defaultValue: 'manual', | ||
description: 'Sets the autocompletion type of the Combobox listbox.' | ||
}, | ||
{ | ||
name: 'onChange', | ||
type: 'function', | ||
description: 'Callback function that gets invoked when the combobox input value is changed.' | ||
}, | ||
{ | ||
name: 'onSelectionChange', | ||
type: 'function', | ||
description: 'Callback function that gets invoked when the selected value is changed.' | ||
}, | ||
{ | ||
name: 'onActiveChange', | ||
type: 'function', | ||
description: 'Callback function that gets invoked when the current active option was changed.' | ||
}, | ||
{ | ||
name: 'error', | ||
type: 'React.ReactNode', | ||
description: 'Error message for the field, when needed.' | ||
}, | ||
{ | ||
name: 'requiredText', | ||
type: 'string', | ||
defaultValue: 'Required', | ||
description: 'Custom "required" text. Useful for localization.' | ||
}, | ||
{ | ||
name: 'renderNoResults', | ||
type: ['() => JSX.Element', 'React.Element'], | ||
description: 'Render prop to customize the output when there are no matching results. Useful for localization.' | ||
}, | ||
{ | ||
name: 'portal', | ||
type: ['React.Ref<HTMLElement>', 'HTMLElement'], | ||
description: 'Alternative placement of combobox listbox. When not set, the listbox will be absolutely positioned inline.' | ||
} | ||
]} /> | ||
### ComboboxOption | ||
<ComponentProps className={true} refType="HTMLLIElement" props={[ | ||
{ | ||
name: 'value', | ||
type: 'string', | ||
description: 'Value to be applied to the combobox option. When omitted, value will default to the text content of the option.' | ||
}, | ||
{ | ||
name: 'disabled', | ||
type: 'boolean', | ||
description: 'When set, sets the combobox option as "aria-disabled="true" and removes the element from key navigation.' | ||
}, | ||
{ | ||
name: 'description', | ||
type: ['string', 'number', 'ReactElement', 'ReactFragment', 'ReactPortal'], | ||
description: 'Additional text to display for the ComboboxOption.' | ||
} | ||
]} /> | ||
### ComboboxGroup | ||
<ComponentProps className={true} refType="HTMLUListElement" props={[ | ||
{ | ||
name: 'label', | ||
required: true, | ||
type: ['string', 'number', 'ReactElement', 'ReactFragment', 'ReactPortal'], | ||
description: 'Label for the group of options.' | ||
} | ||
]} /> | ||
## Related Components | ||
- [Listbox](./Listbox) | ||
- [FieldWrap](./FieldWrap) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.