Dropdown
Dropdown binds a value to an option collection without exposing its internal interaction library.
Basic selection
Section titled “Basic selection”import { useState } from 'react';import { Dropdown } from '@cratis/components/Dropdown';
export function RolePicker() { const [role, setRole] = useState<string>();
return ( <Dropdown value={role} options={[ { label: 'Administrator', value: 'admin' }, { label: 'Advisor', value: 'advisor' }, ]} onChange={setRole} aria-label='Role' /> );}onChange receives the selected option’s value (here 'admin' or 'advisor'), not the option object and not a DOM event, so a state setter can be passed directly. The later examples on this page reuse role, setRole, and a roles array of the same shape.
When option objects contain label and value, those fields are used automatically. Use optionLabel and optionValue for another shape.
Choosing an option — with Enter on the keyboard-active option, or with a click — reports it through onChange once, closes the list, and shows that option. The Dropdown completes the selection itself, so a value that is not fed straight back, or that resolves to none of the options, never leaves the list hanging open over stale filter text. A value supplied afterwards still decides what is selected.
Label the control
Section titled “Label the control”An external native label associates with the primary button, filter input, or multiple-select control through htmlFor and the Dropdown’s id:
<label htmlFor='project-role'>Project role</label><Dropdown id='project-role' value={role} options={roles} onChange={setRole} />The single-select button announces the field label before its selected value (for example, “Project role Developer”), rather than announcing only “Developer.” Filter inputs and native multiple selects retain the field label as their accessible name. A label without an id receives a stable generated one after mounting; existing label IDs are preserved. Clicking the label still focuses the control. Independently inserted, replaced, or reassociated labels stay synchronized after mounting; generated IDs do not depend on a label’s position. A shared observer watches only potential label-association changes and disconnects when the last Dropdown in that DOM root unmounts.
Explicit aria-labelledby or aria-label props take precedence over automatic native-label discovery. For an accessible name in server-rendered HTML before hydration, provide the label reference explicitly:
<label id='project-role-label' htmlFor='project-role'>Project role</label><Dropdown id='project-role' aria-labelledby='project-role-label' value={role} options={roles} onChange={setRole}/>Filtered selection
Section titled “Filtered selection”<Dropdown value={role} options={roles} filter placeholder='Select a role' filterPlaceholder='Search roles' onChange={setRole} aria-label='Role'/>Filtered selection follows the combobox pattern. A non-filtered single select follows the button/listbox pattern, so tests should query by accessible name rather than assume role="combobox" for both.
Multiple selection
Section titled “Multiple selection”Set multiple and bind an array. Components uses native multiple selection when filtering is off and an accessible multi-value combobox when filter is enabled. Use a specialized collection picker when a large dataset needs virtualized search or chip collapsing.
Localizing show-options and clear-selection
Section titled “Localizing show-options and clear-selection”The show-options trigger (the filtered combobox path) and the clear-selection action (showClear) carry
their own accessible names, resolved with the same precedence everywhere Dropdown renders them: pt.trigger['aria-label']
(or pt.select['aria-label'] for the trigger) wins, then the CratisComponentsProvider’s
messages.dropdown.showOptions / messages.dropdown.clearSelection, then the English defaults 'Show options' /
'Clear selection'. Configure them once for every Dropdown in the application through the provider, or per instance
through pt.
<Dropdown value={role} options={roles} filter showClear aria-label='Role' pt={{ trigger: { 'aria-label': 'Show role options' } }}/>| Prop | Purpose |
|---|---|
value | Selected value or array for multiple selection. |
options | Option collection. |
optionLabel / optionValue | Field names for object options. |
placeholder | Empty trigger text. |
filter / filterPlaceholder | Searchable combobox mode. |
multiple | Native multiple-selection mode. |
showClear | Clear action for a single selection. |
invalid / disabled | Control state. |
className / style | Class and inline style for the outer wrapper. |
id, name, tabIndex, aria-* | Identity, form, and accessibility attributes. |
onChange / onBlur | Selection and focus callbacks. |
pt | Cratis-owned stable part attributes. |
Stable parts
Section titled “Stable parts”root, input, select, trigger, value, clear, indicator, filter, popover, listbox, option, and multiple.
The popup is portaled outside modal clipping contexts and carries a z-index above Cratis dialogs. Styling uses the --cratis-* tokens and stable parts; no renderer selectors are required.