Select
Select allows users to choose one option from a list of items in a Menu.
Anatomy

- Label: Title of the input.
- Input Container: Rectangular container that houses the icon and placeholder text.
- Placeholder Text (Optional): Placeholder text like “Select One” is typically displayed in the Select field. After the user makes a selection, the placeholder text is replaced with the user’s selection.
- Icon: Caret icon positioned to the right of the container visually distinguishes this as a Select input.
Usage Guidance
- Clicking or tapping anywhere in a Select opens the Menu.
- A checkmark icon indicates which value is currently selected in the list.
- Each Menu option should be distinct. If the option isn’t discrete, combine it with another option.
- The list of Menu options should be sorted in a logical order alphabetically, chronologically, or by order of importance.
When to Use
- Use Select as a form element where users are only allowed to select one item from a list of more than 7 predefined options.
- Typically, Selects work best when the list is between 7 to 15 items to prevent overwhelming the user with too many options.
When to Use Something Else
- Consider using a Switch if the only options are yes or no.
- For a list between 2 to 7 predefined options, consider using Radio Buttons to select one option or Checkboxes to select multiple options. Radio and Checkbox groups display all options upfront and do not require the user to interact with the input to view the list of options.
- Use a Prompt when the number of list items is large or unknown. Prompts have search capabilities and folders which provide users with the means to browse options. Prompts can be configured to support single or multi-select.
Examples
Basic Example
Select supports a
dynamic API where you
pass an array of items via the items prop and provide a render function to display the items. The
items may be provided as an
array of strings or an
array of objects.
Select should be used in tandem with Form Field where the
Select wraps the FormField element and the FormField element wraps the children of Select to
meet accessibility standards. This ensures the label text from FormField is attached to the
Select.Input and read out as a group for voiceover.
<Select items={options}>
<FormField label="Your Label">
<Select.Input onChange={e => handleChange(e)} id="contact-select" />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item.id}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</FormField>
</Select>import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
'Thisisalongstringwithnobreaksandwillwrap',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log('change', event.currentTarget.value);
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options}>
<FormField.Input as={Select.Input} onChange={handleChange} />
<Select.Popper>
<Select.Card>
<Select.List>
{item => {
return <Select.Item>{item}</Select.Item>;
}}
</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
Selected Value: {value}
</Flex>
);
};
Our example uses React state to track the
value of the Select.
Hoisted Model
By default, Select will create and use its own model internally. Alternatively, you may configure
your own model with useSelectModel and pass it to Select via the model prop. This pattern is
referred to as
hoisting the model
and provides direct access to its state and events outside of the Select component.
In this example, we set up external observation of the model state and create an external button to trigger an event to change the selected item.
Note: If your array of objects uses an id property and a text property there is no need to use
the helper functions of getId or getTextValue. The collection system and the Select use these
properties by default for keyboard navigation and selected the id based on the item clicked.
Selected Value: fax-3
import React from 'react';
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select, useSelectModel} from '@workday/canvas-kit-react/select';
import {BodyText} from '@workday/canvas-kit-react/text';
const options = [
{text: 'E-mail', id: 'email-1'},
{text: 'Phone', id: 'phone-2'},
{text: 'Fax', id: 'fax-3'},
{text: 'Mail', id: 'mail-4'},
{text: 'Mobile Phone', id: 'mobile-phone-5'},
];
export default () => {
const model = useSelectModel({
items: options,
initialSelectedIds: ['fax-3'],
});
return (
<>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select model={model}>
<FormField.Input as={Select.Input} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item.text}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<BodyText size="small">Selected Value: {model.state.selectedIds[0]}</BodyText>
<SecondaryButton
onClick={() => {
model.events.select({id: 'phone-2'});
}}
>
Select Phone Item
</SecondaryButton>
</>
);
};
Label Position Horizontal
Set the orientation prop of the Form Field to designate the position of the label relative to the
input component. By default, the orientation will be set to vertical.
Choose a form of contact
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select, useSelectModel} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const model = useSelectModel({
items: options,
});
return (
<Flex>
<FormField orientation="horizontalStart">
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select model={model}>
<FormField.Input as={Select.Input} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
<FormField.Hint>Choose a form of contact</FormField.Hint>
</Select>
</FormField.Field>
</FormField>
</Flex>
);
};
Required
Set the required prop of the wrapping FormField to true to indicate that the field is
required. Labels for required fields are suffixed by a red asterisk.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField isRequired>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options}>
<FormField.Input as={Select.Input} onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
Selected Value: {value}
</Flex>
);
};
Disabled
Set the disabled prop of Select.Input to prevent users from interacting with it.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax (disabled)',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [_, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options} nonInteractiveIds={['Fax (disabled)']}>
<FormField.Input as={Select.Input} disabled onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>
{item => (
<Select.Item aria-disabled={item === 'Fax (disabled)' ? true : undefined}>
{item}
</Select.Item>
)}
</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
</Flex>
);
};
Disabled Items
In order to disable items and prevent users from interacting with them:
-
Set the
nonInteractiveIdsprop ofSelectto an array of disabled itemids. If your items are an array ofstringsthis will be just the text value. If your items are an array ofobjects, this will be that value of theidproperty. This will disable interaction for those items and exclude them from type-ahead. -
Set the
aria-disabledattribute of all disabledSelect.Items totrue. This ensures the items are styled as disabled.
The following example adds the string value of the items we want disable to nonInteractiveIds and
sets aria-disabled for the disabled items.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax (disabled)',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options} nonInteractiveIds={['Fax (disabled)', 'Mobile Phone']}>
<FormField.Input as={Select.Input} onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>
{item => (
<Select.Item
aria-disabled={
item === 'Mobile Phone' || item === 'Fax (disabled)' ? true : undefined
}
>
{item}
</Select.Item>
)}
</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
Selected Value: {value}
</Flex>
);
};
With Icons
Use Select.Item.Icon to render an icon for a Select.Item. The icon prop for Select.Item.Icon
accepts system icons from @workday/canvas-system-icons-web.
In order to render the icon for the selected item in the Select.Input:
- Obtain a reference to the
modelby registering youritemswithuseSelectModel. - Get the selected item:
const selectedItem = model.navigation.getItem(model.state.selectedIds[0], model) - Pass the icon for the selected item to the input:
<Select.Input inputStartIcon={selectedItem.value.icon}>
Note:
data-idonSelect.Itemmust match theidproperty in your array of objects. This ensures proper keyboard handling and type-ahead.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select, useSelectModel} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {
activityStreamIcon,
avatarIcon,
uploadCloudIcon,
userIcon,
} from '@workday/canvas-system-icons-web';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const styleOverrides = {
formfieldInputStyles: createStyles({
width: px2rem(300),
}),
selectCardStyles: createStyles({
maxHeight: px2rem(200),
}),
};
const customOptions = [
{text: 'Activity Stream', id: 'activity-stream', icon: activityStreamIcon},
{text: 'Avatar', id: 'avatar', icon: avatarIcon},
{text: 'Upload Cloud', id: 'upload-cloud', icon: uploadCloudIcon},
{text: 'User', id: 'user', icon: userIcon},
];
export default () => {
const model = useSelectModel({
items: customOptions,
});
const selectedItem = model.navigation.getItem(model.state.selectedIds[0], model);
return (
<Flex>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select model={model}>
<FormField.Input
as={Select.Input}
cs={styleOverrides.formfieldInputStyles}
inputStartIcon={selectedItem?.value.icon}
/>
<Select.Popper>
<Select.Card cs={styleOverrides.selectCardStyles}>
{model.state.items.length > 0 && (
<Select.List>
{item => (
<Select.Item data-id={item.id}>
<Select.Item.Icon icon={item.icon} />
{item.text}
</Select.Item>
)}
</Select.List>
)}
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
</Flex>
);
};
Note: that Select.Input will only render an icon if an item is selected.
Grow
Set the grow prop of the wrapping FormField to true to configure the Select.Input to expand
to the width of its container.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select, useSelectModel} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const model = useSelectModel({
items: options,
});
return (
<Flex>
<FormField grow>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select model={model}>
<FormField.Input as={Select.Input} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
</Flex>
);
};
Menu Height
Select.Card has a default maximum height of 300px to restrict the height of the dropdown menu.
Set its maxHeight prop to override this value.
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Box} from '@workday/canvas-kit-react/layout';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const selectCardStyles = createStyles({
maxHeight: px2rem(200),
});
const cities = [
'Atlanta (United States)',
'Amsterdam (Europe)',
'Austin (United States)',
'Beaverton (United States)',
'Belfast (Europe)',
'Berlin (Europe)',
'Boston (United States)',
'Boulder (United States)',
'Chicago (United States)',
'Dallas (United States)',
'Denver (United States)',
'Dublin (Europe)',
'Irvine (United States)',
'Minneapolis (United States)',
'New York (United States)',
'Orlando (United States)',
'Palo Alto (United States)',
'Philadelphia (United States)',
'Pleasanton (United States)',
'Raleigh (United States)',
'San Francisco (United States)',
'San Mateo (United States)',
'Stockholm (Europe)',
'Toronto (Canada)',
'Victoria (Canada)',
'Vienna (Europe)',
'Warsaw (Europe)',
'Washington, DC (United States)',
'Zurich (Europe)',
];
export default () => {
return (
<Box>
<FormField>
<FormField.Label>Choose a City</FormField.Label>
<FormField.Field>
<Select items={cities}>
<FormField.Input as={Select.Input} />
<Select.Popper>
<Select.Card cs={selectCardStyles}>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
</Box>
);
};
Ref Forwarding
Select.Input supports ref forwarding. It will
forward ref to its underlying <input type="text" role="combobox"> element.
import React from 'react';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
// @ts-ignore
const [value, setValue] = React.useState('medium');
const ref = React.useRef(null);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
const handleClick = () => {
if (ref && ref.current) {
console.log(ref);
ref.current.focus();
}
};
return (
<>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options}>
<FormField.Input as={Select.Input} ref={ref} onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<PrimaryButton onClick={handleClick}>Focus Select</PrimaryButton>
</>
);
};
Error States
Set the error prop of the wrapping FormField to "caution" or
"error" to set the Select to the caution or error state, respectively. You will
also need to set the hintId and hintText props on the FormField to meet accessibility
standards. You must set an id attribute on the Select.Input element that matches the value of
inputId set on the FormField element. These attributes ensure that the caution message is
associated to the Select and read out by voiceover.
Note: The Select container component, Select, must wrap FormField to ensure Select.Input is
styled correctly.
<Select items={options}>
<FormField label="Contact" inputId="contact-id-formfield">
<Select.Input id="contact-id-formfield" />
...
</FormField>
</Select>Caution
Use the alert state when a selection is valid but there is additional information.
Please choose a form of contact.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField error="caution">
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options}>
<FormField.Input as={Select.Input} onChange={e => handleChange(e)} id="alert-select" />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
<FormField.Hint>Please choose a form of contact.</FormField.Hint>
</Select>
</FormField.Field>
</FormField>
Selected value: {value}
</Flex>
);
};
Error
Use the error state when the selection is no longer valid.
Fax is disabled. Please choose a different option.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax (disabled)',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField error="error">
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options} nonInteractiveIds={['Fax (disabled)']}>
<FormField.Input as={Select.Input} onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>
{item => (
<Select.Item aria-disabled={item === 'Fax (disabled)' ? true : undefined}>
{item}
</Select.Item>
)}
</Select.List>
</Select.Card>
</Select.Popper>
<FormField.Hint>Fax is disabled. Please choose a different option.</FormField.Hint>
</Select>
</FormField.Field>
</FormField>
Selected Value: {value}
</Flex>
);
};
Initial Selected Item
You can set initialSelectedIds to the value that you want initially selected.
Id: da594226446c11de98360015c5e6daf6
Value: English (United States)
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
{
id: 'b310c757b2d341f99d40d76f4d563c5b',
descriptor: 'Arabic',
languageCode: 'ar',
label: 'Arabic',
nativeLanguageName: 'العربية',
},
{
id: 'a675a6b6e22d100017d7fe2a784d1255',
descriptor: 'Bulgarian (Bulgaria)',
languageCode: 'bg_BG',
label: 'Bulgarian (Bulgaria)',
nativeLanguageName: 'български (Република България)',
},
{
id: 'da594226446c11de98360015c5e6daf6',
descriptor: 'English (United States)',
languageCode: 'en_US',
label: 'English (United States)',
nativeLanguageName: 'English',
},
];
export default () => {
const [value, setValue] = React.useState('English (United States)');
const [id, setId] = React.useState('da594226446c11de98360015c5e6daf6');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setId(event.target.value);
setValue(options.find(item => item.id === event.target.value).label);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select
items={options}
initialSelectedIds={['da594226446c11de98360015c5e6daf6']}
getId={item => item.id}
getTextValue={item => item.label}
>
<Select.Input onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item.label}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<p>Id: {id}</p>
<p>Value: {value}</p>
</Flex>
);
};
Placeholder
You can change the placeholder text by passing in a string value to the placeholder attribute on
the Select.Input.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
'E-mail',
'Phone',
'Fax',
'Mail',
'Mobile Phone',
'The Ontologically Anthropocentric Sensory Immersive Simulation',
];
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options}>
<Select.Input placeholder="Make a Selection" onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>
{item => {
return <Select.Item>{item}</Select.Item>;
}}
</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
Selected Value: {value}
</Flex>
);
};
Fetching Dynamic Items
It’s common to load items from a server call. Hoisting the model and setting your items on state
allows you to pass those items to your model. You can leverage React state to set your items on
load as well as displaying a placeholder indicating when items are loaded.
Note: In this case we need to use getId and getTextValue because our data doesn’t have the
properties of id or text. Using these helper functions sets the serverId to be id and
label to be text.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select, useSelectModel} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {useMount} from '@workday/canvas-kit-react/common';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
maxWidth: px2rem(300),
});
const movieListItems = [
{
label: 'The Lion King',
serverId: '123',
Year: '2019',
Runtime: '118 min',
},
{
label: 'Mowgli: Legend of the Jungle',
serverId: '234',
Year: '2018',
Runtime: '104 min',
},
{
label: 'Doctor Strange',
serverId: '345',
Year: '2016',
Runtime: '115 min',
},
{
label: 'John Wick',
Year: '2014',
serverId: '456',
Runtime: '101 min',
},
{
label: 'The Notebook',
serverId: '567',
Year: '2004',
Runtime: '123 min',
},
];
export default () => {
const [id, setId] = React.useState('456');
const [moviesLists, setMoviesList] = React.useState<typeof movieListItems>([]);
const [loadingStatus, setLoadingStatus] = React.useState<'idle' | 'loading' | 'success'>('idle');
const loadingRef = React.useRef<ReturnType<typeof setTimeout>>();
const model = useSelectModel({
items: moviesLists,
getTextValue: item => item.label,
getId: item => item.serverId,
initialSelectedIds: [id],
});
const stringValue = moviesLists.find(item => item.serverId === id)?.label || '';
function loadItems() {
setLoadingStatus('loading');
loadingRef.current = setTimeout(() => {
setLoadingStatus('success');
setMoviesList(movieListItems);
}, 1500);
}
useMount(() => {
return () => {
clearTimeout(loadingRef.current);
};
});
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Choose a Film</FormField.Label>
<FormField.Field>
<Select model={model}>
<FormField.Input
as={Select.Input}
onChange={e => {
setId(e.target.value);
}}
placeholder={loadingStatus}
/>
<Select.Popper>
<Select.Card>
<Select.List>
{item => {
return <Select.Item>{item.label}</Select.Item>;
}}
</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<div data-testid="selected-id">Selected Id: {id}</div>
<div data-testid="selected-value">Selected value: {stringValue}</div>
<PrimaryButton
onClick={() => {
loadItems();
}}
>
Get Items
</PrimaryButton>
</Flex>
);
};
Complex
When registering items in an array of objects, it’s common to have the text that is displayed to the
user be different than an id. In this example, serverId and label properties need to be remapped
to id and text hence the usage of getId and getTextValue. If your object has the properties
text and id, there would be no need for this.
Id:
Value:
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
{serverId: 'email', label: 'E-mail'},
{serverId: 'phone', label: 'Phone'},
{serverId: 'fax', label: 'Fax'},
{serverId: 'mail', label: 'Mail'},
{serverId: 'mobile', label: 'Mobile Phone'},
{
serverId: 'oasis',
label: 'The Ontologically Anthropocentric Sensory Immersive Simulation',
},
];
export default () => {
const [value, setValue] = React.useState('');
const [id, setId] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setId(event.target.value);
setValue(options.find(item => item.serverId === event.target.value)!.label);
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options} getId={item => item.serverId} getTextValue={item => item.label}>
<FormField.Input as={Select.Input} onChange={e => handleChange(e)} />
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item.label}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<p>Id: {id}</p>
<p>Value: {value}</p>
</Flex>
);
};
Note: By default, the identifier and text value are id and text properties respectively. If
your data object for each item is different, provide a getId or getTextValue function to the
model config. For example:
const items = [
{
serverId: '1',
label: 'First Option',
},
];
<Select items={items} getId={item => item.serverId} getTextValue={item => item.label}>
{/* etc */}
</Select>;Controlled
The Select can be a
controlled input
component by passing the value and onChange to either the <Select> component or the
<Select.Input> component. Internally, the Select.Input watches for changes on the value React
prop as well as the value DOM property and will update the model accordingly.
Id:
Label:
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {Select} from '@workday/canvas-kit-react/select';
import {Flex} from '@workday/canvas-kit-react/layout';
import {createStyles} from '@workday/canvas-kit-styling';
import {SecondaryButton} from '@workday/canvas-kit-react/button';
const parentContainerStyles = createStyles({
flexDirection: 'column',
});
const options = [
{serverId: 'email', label: 'E-mail'},
{serverId: 'phone', label: 'Phone'},
{serverId: 'fax', label: 'Fax'},
{serverId: 'mail', label: 'Mail'},
{serverId: 'mobile', label: 'Mobile Phone'},
{
serverId: 'oasis',
label: 'The Ontologically Anthropocentric Sensory Immersive Simulation',
},
];
export default () => {
const [value, setValue] = React.useState('');
const [label, setLabel] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.currentTarget.value);
setLabel(options.find(item => item.serverId === event.currentTarget.value)?.label || '');
};
return (
<Flex cs={parentContainerStyles}>
<FormField>
<FormField.Label>Contact</FormField.Label>
<FormField.Field>
<Select items={options} getId={item => item.serverId} getTextValue={item => item.label}>
<FormField.Input
as={Select.Input}
onChange={handleChange}
value={value}
name="contact"
/>
<Select.Popper>
<Select.Card>
<Select.List>{item => <Select.Item>{item.label}</Select.Item>}</Select.List>
</Select.Card>
</Select.Popper>
</Select>
</FormField.Field>
</FormField>
<p>Id: {value}</p>
<p>Label: {label}</p>
<Flex gap="s">
<SecondaryButton
onClick={e => {
setValue('fax');
}}
>
Set to "Fax"
</SecondaryButton>
<SecondaryButton
onClick={e => {
setValue('');
}}
>
Clear
</SecondaryButton>
</Flex>
</Flex>
);
};
When to use getId, or getTextValue
-
getId: This is an optional function to return the id of an item. If not provided, the default function will return theidproperty from the object of each item. If you did not provideitems, do not override this function. Instead provide static items via JSX. the list will create an internal array of items whereidis the only property and the defaultgetIdwill return the desired result. Note: If your array of objects has a different property forid, likeserverId, use this function to set the id.const options = [{text: 'Pizza', serverId: 'pizza-1'}, {text: 'Cheeseburger', serverId: 'cheeseburger'}] <Select items={options} getId={(item) => item.serverId}> <FormField label="Your Label"> <Select.Input onChange={e => handleChange(e)} id="contact-select" /> <Select.Popper> <Select.Card> <Select.List>{item => <Select.Item>{item.text}</Select.Item>}</Select.List> </Select.Card> </Select.Popper> </FormField> </Select> -
getTextValue: Optional function to return the text representation of an item. If not provided, the default function will return thetextproperty of the object of each item or an empty string if there is notextproperty. If you did not provideitems, do not override this function. Note: If your array of objects has a different property fortext, likelabel, use this function to set the text.const options = [{label: 'Pizza', id: 'pizza-1'}, {label: 'Cheeseburger', id: 'cheeseburger'}] <Select items={options} getTextValue={(item) => item.label}> <FormField label="Your Label"> <Select.Input onChange={e => handleChange(e)} id="contact-select" /> <Select.Popper> <Select.Card> <Select.List>{item => <Select.Item>{item.label}</Select.Item>}</Select.List> </Select.Card> </Select.Popper> </FormField> </Select>
Custom Styles
Select and its subcomponents support custom styling via the cs prop. For more information, check
our
“How To Customize Styles”.
Component API
Select
Use Select to allow users to choose an option from a list or type characters to select a matching option.
Note: Select must wrap FormField and FormField must wrap all Select children to ensure proper accessibility.
<Select items={options}>
<FormField label="Your Label">
<Select.Input onChange={e => handleChange(e)} id="contact-select" />
<Select.Popper>
<Select.Card>
<Select.List>
{item => <Select.Item>{item.id}</Select.Item>}
</Select.List>
</Select.Card>
</Select.Popper>
</FormField>
</Select>
Props
Props extend from . If a model is passed, props from SelectModelConfig are ignored.
| Name | Type | Description | Default |
|---|---|---|---|
children | ReactNode | Children of the | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
Select.Input
Select.Input renders a that handles keyboard navigation and interaction defined by WAI.
This component can either be controlled or uncontrolled.
<Select items={options}>
<FormField label="Contact">
<Select.Input onChange={(event) => handleChange(event)}>
...
</FormField>
</Select>
Props
Props extend from . Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
inputStartIcon | | The Icon to render at the start of the | |
error | | The type of error associated with the TextInput (if applicable). | |
width | number | string | The width of the TextInput. | |
grow | boolean | True if the component should grow to its container's width. False otherwise. | |
cs | | The | |
children | React.ReactNode | ||
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | |
ref | React.Ref<R = > | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
useSelectInput
useSelectInput extends and and adds type ahead functionality and Select-specific keyboard support.
(
(
model: ,
elemProps: {
keySoFar: string;
placeholder: string;
value: string;
},
ref: React.Ref
) => {
onKeyDown: (event: ) => void;
autoComplete: 'off';
keySoFar: null;
ref: (instance: | null) => void;
},
,
,
,
,
)Select.Card
Select.Card renders a . You have access to all Card props.
Note: The card will be the width of its corresponding Select.Input.
<Select items={options}>
<FormField label="Your Label">
<Select.Input onChange={(event) => handleChange(event)}>
<Select.Popper>
<Select.Card>
...
</Select.Card>
</Select.Popper>
</FormField>
</Select>
Layout Component
Select.Card supports all props from thelayout component.
Props
Props extend from div. Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
variant | 'borderless' | 'filled' | The variant of the Card. Can be | 'default' |
cs | | The | |
children | React.ReactNode | ||
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | div |
ref | React.Ref<R = div> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
useSelectCard
Sets the width of the SelectCard to the Select.Input width.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
width: number;
}Select.Item
Select.Item renders a with aria role of option. You can optionally render a Icon.
<Select items={options}>
<FormField label="Your Label">
<Select.Input onChange={(event) => handleChange(event)}>
<Select.Popper>
<Select.Card>
<Select.List>
{(item) => <Select.Item><Select.Item.Icon icon={icon} />{item}</Select.Item>}
</Select.List
</Select.Card>
</Select.Popper>
</FormField>
</Select>
Props
Props extend from li. Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
index | number | Optionally pass index to menu item. This should be done if | |
children | ReactNode | The label text of the MenuItem. | |
data-id | string | The name of the menu item. This name will be used in the | |
aria-disabled | boolean |
| |
isDisabled | boolean | If true, set the StyledMenuItem to the disabled state so it is not clickable. | false |
cs | | The | |
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | li |
ref | React.Ref<R = li> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
Model
useSelectModel
SelectModel extends the {@link ComboboxModel }. Selecting items from
the menu will dispatch an
input event on the
input which should work with form libraries, automation and autofill.
const model = useSelectModel({items: ['Mobile', 'Phone', 'E-Mail']})
<Select model={model}>
...
</Select>
useSelectModel (config: ): Specifications
Accessibility Guidelines
Keyboard Interaction
Each Select must have a focus indicator that is highly visible against the background and against the non-focused state. Refer to Accessible Colors for more information.
Select must support the following keyboard interactions:
Tab: focus the Select componentEnterorSpace: open the Select list box and focus the first optionEsc: dismiss the Select list box and focus the Select componentUp ArroworDown Arrow: focus the previous or next option respectivelyCharacter Key: focus options matching character keyHomeorFn + Up Arrow: focus first optionEndorFn + Down Arrow: focus last option
Screen Reader Interaction
Select must communicate the following to users:
- This component is a “combo” or “combobox”
- The associated label
- The currently selected value
- The “collapsed” or “expanded” state
- When opened, there is a list of ‘X’ items
- When opened, the name of the active option
- When opened, the position “X of Y” for the active option
Design Annotations Needed
No design annotations required for Select.
Implementation Markup Needed
The Select component is built around the
ARIA Combobox v1.2 specification for Select-Only.
Support for this component may be limited by browser vendors and/or screen readers. Also, note
any reference to “menu” on this page does not refer to an ARIA menu pattern. The Select component
is using an ARIA listbox pattern for the options.
- A
<label>element must be used with aforattribute referencing the uniqueidvalue of the Select input. - Select input must have a
requiredattribute when the field is required for form submission. - Select input must have an
aria-describedbyattribute referencing the uniqueidvalue of the inline hint text below the field. - Select input must have an
aria-invalid=”true”attribute when the field has an error condition. - Select input must have a
disabledattribute when the field is disabled.
Content Guidelines
- The list of Menu items should be scannable, with concise labels written in title case. Don’t write sentences and omit articles (a, an, the) unless needed for clarity. For more detailed information on how to write Menu items, refer to the Menus section of the Content Style Guide.
- Placeholder text for a Select must begin with the verb “Select”. Refer to the guidelines on Placeholder Text in the Content Style Guide for more tips on how to write placeholder text.
Can't Find What You Need?
Check out our FAQ section which may help you find the information you're looking for. For further information, contact the #ask-canvas-design or #ask-canvas-kitchannels on Slack.