Data Options
Single Select
<State initial={{text: 'Bob Ross', value: 2}}>
{([value, setValue]) => (
<Form.AnvilSelect
value={value}
onChange={setValue}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4}
]}
label="Technician"
trigger={{placeholder: 'Select a Technician'}}
open
autoFlipVertically={false}
closeOnClickOutside={false}
/>
)}
</State>
Multiselect
Selects can also allow for multiple selections. Each option in a multiselect has a built-in Checkbox as a visual aid.
<State initial={[{text: 'Bob Ross', value: 2},{text: 'Jackie Robinson', value: 3}]}>
{([value, setValue]) => (
<AnvilSelect
multiple
value={value}
onChange={setValue}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4}
]}
trigger={{placeholder: 'Select Technicians'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
)}
</State>
Grouped Options
Options in a Select can be grouped together.
<AnvilSelect
closeOnClickOutside={false}
autoFlipVertically={false}
options={[
{
text: 'Western Division',
options: [
{
content: 'Plumbing',
text: 'Plumbing Western Division',
value: '1',
}, {
text: 'HVAC',
text: 'HVAC Western Division',
value: '2'
}
]
},
{
text: 'Eastern Division',
options: [
{
content: 'Plumbing',
text: 'Plumbing Eastern Division',
value: '3',
}, {
text: 'HVAC Install',
text: 'HVAC Install Eastern Division',
value: '4'
}, {
text: 'Electrician',
text: 'Electrician Eastern Division',
value: '5'
}
]
}
]}
open
trigger={{ placeholder: 'Select a Unit' }}
/>
Customized Options
The choices in the Select can be customized to produce many unique stylings. This can be useful when additional information can enhance the functionality of the Select.
<Form.AnvilSelect
label="Select a Technician"
options={
[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4},
{text: 'Zack Bower', value: 5}
].map(option => ({
text: option.text,
value: option.value,
content: (
<Stack alignItems="center" spacing={1}>
<Avatar size="s" autoColor name={option.text} />
<BodyText>{option.text}</BodyText>
</Stack>
)
}))
}
trigger={{placeholder: 'Technicians'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
<Form.AnvilSelect
label="Unit Selection"
options={[
{
key: 1,
text: 'A/C Unit I ($2950)',
value: 1,
content: (
<Stack alignItems='center' justifyContent='space-between' className="w-100">
<BodyText size='small' className="lh-1">A/C Unit I</BodyText>
<Tag compact badge color="success" subtle>$2950</Tag>
</Stack>
)
},
{
key: 2,
text: 'A/C Unit II ($3450)',
value: 2,
content: (
<Stack alignItems='center' justifyContent='space-between'>
<BodyText size='small' className="lh-1">A/C Unit II</BodyText>
<Tag compact badge color="success" subtle>$3450</Tag>
</Stack>
)
},
{
key: 3,
text: 'A/C Unit III ($4830)',
value: 3,
content: (
<Stack alignItems='center' justifyContent='space-between'>
<Stack.Item><BodyText size='small' className="lh-1 d-ib m-r-half">A/C Unit III</BodyText> <Tag compact subtle>Best Value</Tag></Stack.Item>
<Tag compact badge color="success" subtle>$4830</Tag>
</Stack>
)
},
]}
trigger={{placeholder: 'Technicians'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
() => {
const Content = (props) => (
<div className="m-l-half">
<BodyText size='small' className="lh-display m-b-half">{props.t1}</BodyText>
<BodyText size='xsmall' subdued className="lh-display">{props.t2}</BodyText>
</div>
);
return (
<Form.AnvilSelect
multiple
label="Email"
options={[
{
key: 1,
text: 'First Time',
value: 1,
content: <Content t1="First time users" t2="Recently subscribed to system" />
},
{
key: 2,
text: 'Low Response',
value: 2,
content: <Content t1="Low response" t2="No response in the last 60 days" />
},
{
key: 3,
text: 'Active Engagement',
value: 3,
content: <Content t1="Active engagement" t2="Frequent responders to campaigns" />
},
]}
trigger={{placeholder: 'Technicians'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
);
}
Secondary Actions
A secondary action can be performed on individual options. Example actions include editing, deleting, or navigating to more information. Secondary actions are only visible on hover.
<AnvilSelect
options={[
{
text: 'Jane Doe',
value: 1,
secondaryAction: (<Button xsmall primary fill="subtle" onClick={() => alert("Action Clicked")}>Action</Button>)
}, {
text: 'Bob Ross',
value: 2,
secondaryAction: (<Tooltip text='Edit'><Button iconName="edit" xsmall primary fill="subtle" onClick={() => alert("Action Clicked")}/></Tooltip>)
}, {
text: 'Jackie Robinson',
value: 3,
secondaryAction: (
<Stack spacing={1}>
<Tooltip text='Edit'>
<Button iconName="edit" xsmall primary fill="subtle" onClick={() => alert("Action Clicked")} />
</Tooltip>
<Tooltip direction='tl' text='Delete'>
<Button iconName="delete" xsmall primary fill="subtle" onClick={() => alert("Action Clicked")}/>
</Tooltip>
</Stack>
)
}
]}
trigger={{placeholder: 'Hover over an option'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
Empty State
Displayed when there are no options available, typically discovered when searching a Select.
<AnvilSelect
search
options={[]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
Trigger
Select Addons
Select Addons can be added to help describe the content. They can be used to help with formatting or show contextual indicators of the content we’re asking for.
<Form>
<Form.AnvilSelect
options={[{
text: '100',
value: 1,
}]}
label="Addon"
trigger={{
shortLabel: '$',
size: 'small'
}}
/>
<Form.AnvilSelect
options={[{
text: '25',
value: 1,
}]}
label="Addon on the right"
trigger={{
shortLabel: '%',
shortLabelPosition: 'right'
}}
/>
<Form.AnvilSelect
multiple
options={[{
text: 'This is a tag',
value: 1,
}]}
label="Addon with an Icon"
trigger={{
shortLabel: <Icon name="tag" />,
size: 'large',
}}
/>
</Form>
With Description
<State initial={[{}]}>
{([value, setValue]) => (
<Form.AnvilSelect
value={value}
onChange={() => setValue()}
options={[]}
label="Technician"
trigger={{placeholder: 'Select a Technician', shortLabel: <Icon name="person" />}}
description="Description goes here..."
/>
)}
</State>
Error State
<State initial={{value: 2, text: 'Bob Ross'}}>
{([state, setState]) => (
<Form.AnvilSelect
value={state}
onChange={setState}
options={[
{value: 1, text: 'Jane Doe'},
{value: 2, text: 'Bob Ross'},
{value: 3, text: 'Jackie Robinson'},
{value: 4, text: 'Alexandria Garcia'}
]}
label="Technician"
trigger={{
error: true,
placeholder: 'Select a Technician',
shortLabel: <Icon name="person" />
}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
)}
</State>
<State initial={[]}>
{([value, setValue]) => (
<Form.AnvilSelect
value={value}
onChange={setValue}
options={[]}
label="Technician"
trigger={{error: true, placeholder: 'Select a Technician', shortLabel: <Icon name="person" />}}
error="You must choose a technician."
/>
)}
</State>
Trigger Sizes
The Select's trigger can be configured into three sizes
<Form>
<Form.AnvilSelect
options={[]}
label="Small Select"
trigger={{ size: 'small' }}
/>
<Form.AnvilSelect
options={[]}
label="Default Select"
trigger={{}}
/>
<Form.AnvilSelect
options={[]}
label="Large Select"
trigger={{ size: 'large' }}
/>
</Form>
Tag display on selection
In multiselects, tags are populated in the trigger for each selection. This allows for a quick readout of selections, but this can quickly get out of hand. By default, the Select will display an X Selected when the selections span across more than 1 row, which can be configured for specific needs.
<State
initial={{
options: [
{value: 1, text: 'Jane Doe'},
{value: 2, text: 'Bob Ross'},
{value: 3, text: 'Jackie Robinson'},
{value: 4, text: 'Alexandria Garcia'},
{value: 5, text: 'Zack Bower'},
{value: 6, text: 'Erin Smith'},
{value: 7, text: 'Jarrod Saltalamacchia'},
{value: 8, text: 'Natalia McPhearson'}
]
}}
>
{([state, setState]) => (
<AnvilSelect
multiple
value={state.selected || state.options}
onChange={value => setState(prev => ({...prev, selected: value}))}
options={state.options}
trigger={{placeholder: 'Technicians', rows: 1}}
/>
)}
</State>
Caution. Too many Tags can break a layout, rows: 0
<State
initial={{
options: [
{value: 1, text: 'Jane Doe'},
{value: 2, text: 'Bob Ross'},
{value: 3, text: 'Jackie Robinson'},
{value: 4, text: 'Alexandria Garcia'},
{value: 5, text: 'Zack Bower'},
{value: 6, text: 'Erin Smith'},
{value: 7, text: 'Jarrod Saltalamacchia'},
{value: 8, text: 'Natalia McPhearson'}
]
}}
>
{([state, setState]) => (
<AnvilSelect
multiple
value={state.selected || state.options}
onChange={value => setState(prev => ({...prev, selected: value}))}
options={state.options}
trigger={{placeholder: 'Technicians', rows: 0}}
/>
)}
</State>
Multiselects can take advantage of tag's custom color options.
<State
initial={{
options: [
{text: 'Red', value: 1, color: tokens.colorRed},
{text: 'Blue', value: 2, color: 'rgb(0, 0, 255)'},
{text: 'Green', value: 3, color: '#00FF00'}
]
}}
>
{([state, setState]) => (
<AnvilSelect
value={state.selected || state.options}
onChange={value => setState(prev => ({...prev, selected: value}))}
multiple
options={state.options}
trigger={{placeholder: 'Colors'}}
/>
)}
</State>
Label
Label Help
Labels can have a help icon with a tooltip to provide additional context to a label.
<Form>
<Form.AnvilSelect
options={[]}
label="Select"
labelProps={{
help: "This is help text"
}}
/>
</Form>
Required and Optional
A visual indicator can be applied on a Select's label.
<Form>
<Form.AnvilSelect
label="Required Label"
options={[]}
labelProps={{
required: true
}}
/>
<Form.AnvilSelect
label="Optional Label"
options={[]}
labelProps={{
optional: true
}}
/>
</Form>
Custom Content
The content of the trigger can be customized.
<State initial={[{text: 'Bob Ross', value: 2},{text: 'Jackie Robinson', value: 3}]}>
{([value, setValue]) => (
<Form>
<Form.AnvilSelect
multiple
value={value}
onChange={setValue}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4}
]}
trigger={{
content: (value) => `${value.length} technician${value.length !== 1 ? 's' : ''} selected`,
}}
/>
<Form.AnvilSelect
multiple
value={value}
onChange={setValue}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4}
]}
trigger={{
content: (value) => (value.map((e, i) => i < value.length - 1 ? `${e.text}, ` : `${e.text}`)),
}}
/>
</Form>
)}
</State>
Clearable
<Form>
<Form.AnvilSelect
options={[{
text: '100',
value: 1,
}]}
label="Clearable by default"
value={{
text: '100',
value: 1,
}}
trigger={{
shortLabel: '$',
size: 'small'
}}
/>
<Form.AnvilSelect
options={[{
text: '100',
value: 1,
}]}
label="Disable Clearable"
value={{
text: '100',
value: 1,
}}
trigger={{
shortLabel: '$',
size: 'small',
clearable: false
}}
/>
</Form>
Search
Options within a select can be filtered through a search functionality. Search is useful with 10+ options available. The more options there are, the more desirable search functionality becomes. Search is also useful when the user knows what they are looking for (such as a specific person).
<AnvilSelect
search={{
placeholder: "Search for a Technician"
}}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
Select All
In multiselects, all options can be selected at once. There is also an option to select all items from within a single group of content.
<AnvilSelect
multiple={{selectAll: true}}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
Apply Selections
The Select allows for an option to delay applying selections until an explicit apply action is given by a user. This can be useful when selections update content on a page, where refreshing the page after each selection is unnecessary.
<AnvilSelect
multiple
footer={{
actionName: 'Apply',
onActionClick: (() => alert("Apply button clicked"))
}}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
The Select also can accept a custom footer. This can be useful when you need more control over footer look and behavior.
<AnvilSelect
multiple
footer={
<div style={{ flexBasis: '100%' }}>
<div style={{ margin: '-4px -12px' }}>
<Button
full
iconName="search"
fill="subtle"
primary
onClick={() => {
alert('Browse Technicians Action');
}}
>
Browse Technicians
</Button>
</div>
</div>
}
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
Body Options
Like the Popover, the Select's body content can be scrolled through. By default, it begins scrolling after it has reached 90% screen height, but can be customized to a specific pixel value.
<State
initial={{
options: [
{value: 1, text: 'Jane Doe'},
{value: 2, text: 'Bob Ross'},
{value: 3, text: 'Jackie Robinson'},
{value: 4, text: 'Alexandria Garcia'},
{value: 16, text: 'John Doe'},
{value: 5, text: 'Erica K'},
{value: 6, text: 'Moses Brooks'},
{value: 7, text: 'Pam Colin'},
{value: 8, text: 'Jackie Johnson'},
{value: 19, text: 'Jane Doe'},
{value: 10, text: 'Bob Ross'},
{value: 11, text: 'Jackie Robinson'},
{value: 12, text: 'Alexandria Garcia'},
{value: 13, text: 'Erica K'},
{value: 14, text: 'Moses Brooks'},
{value: 15, text: 'Pam Colin'}
],
selected: {value: 2, text: 'Bob Ross'}
}}
>
{([state, setState]) => (
<Form.AnvilSelect
value={state.selected}
onChange={value => setState(prev => ({...prev, selected: value}))}
options={state.options}
label="Technician"
trigger={{placeholder: 'Select a Technician', shortLabel: <Icon name="person" />}}
open
closeOnClickOutside={false}
scrollHeight={'250px'}
autoFlipVertically={false}
/>
)}
</State>
Popover Sizing
By default, the Select's trigger and its popover are the same width. The popover part of the Select can also be a custom width, which can be useful when there is little real estate for the trigger.
<AnvilSelect
multiple
options={[
{text: 'Jane Doe', value: 1},
{text: 'Bob Ross', value: 2},
{text: 'Jackie Robinson', value: 3},
{text: 'Alexandria Garcia', value: 4},
{text: 'Zack Bower', value: 5},
{text: 'Erin Smith', value: 6},
{text: 'Jarrod Saltalamacchia', value: 7},
{text: 'Natalia McPhearson', value: 8}
]}
trigger={{placeholder: 'Technicians', rows: 0}}
popoverWidth="m"
/>
Drill-in State
A select has a secondary, drilled-in state that can be accessed through a Select. Like the Popover component, this content can be customized into any format.
<State initial={true}>
{([opened, setOpened]) => {
const toggleDrillIn = () => { setOpened(!opened) };
const DrillInButton = <Button xsmall primary onClick={toggleDrillIn}>Options</Button>;
return (
<AnvilSelect
search
options={[
{text: 'Jane Doe', value: 1, secondaryAction: DrillInButton},
{text: 'Derrick Packard', value: 2, secondaryAction: DrillInButton}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
drillIn={{
open: opened,
content: 'A blank canvas for any type of content',
title: 'This is a title',
footerActionName: 'Apply',
footerOnActionClick: toggleDrillIn,
onBack: toggleDrillIn
}}
/>
)
}}
</State>
<State initial={{drillOpened: true, toggleValue: 1}}>
{([state, setState]) => {
const toggleDrillIn = () => setState(prev => ({...prev, drillOpened: !state.drillOpened}));
const DrillInButton = <Button xsmall primary onClick={toggleDrillIn}>Options</Button>;
return (
<AnvilSelect
search
options={[
{text: 'Jane Doe', value: 1, secondaryAction: DrillInButton},
{text: 'Derrick Packard', value: 2, secondaryAction: DrillInButton}
]}
trigger={{placeholder: 'Select a Technician'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
drillIn={{
open: state.drillOpened,
content: (
<Form>
<Form.AnvilSelect
label="State"
options={[
{text: 'California', value: 1},
{text: 'Arizona', value: 2},
{text: 'New Mexico', value: 3},
{text: 'Nevada', value: 4},
{text: 'Utah', value: 5}
]}
trigger={{placeholder: 'Select a Technician'}}
/>
<Form.ButtonToggle
value={state.toggleValue}
small
label="Sent by"
onChange={value => setState(prev => ({...prev, toggleValue: value}))}
options={[
{
text: 'Email',
value: 1
},
{
text: 'Phone',
value: 2
}
]}
/>
</Form>
),
title: 'This is a title',
footerActionName: 'Apply',
footerOnActionClick: toggleDrillIn,
onBack: toggleDrillIn
}}
/>
)
}}
</State>
Tree View
The Select builds off the Option List's tree view. This is useful for displaying hierarchical information.
const dummyData = [
{
text: 'Plumbing',
value: 1,
collapsed: false,
options: [
{
text: 'Install Materials',
value: 11,
options: [
{
text: 'Disposer Waste',
value: 111,
},
{
text: 'Frostproof Hydrant',
value: 112,
}
],
},
{
text: 'Service Materials',
value: 12,
options: [
{
text: 'Fluidmaster Fill Valve',
value: 121,
},
{
text: 'Tailpiece Slip Joint',
value: 122,
}
],
},
],
}
];
const Example18 = () => {
const [value, setValue] = React.useState([]);
const [collapseValue, setCollapseValue] = useOnExpand([1, 11, 12]);
const onExpand = (data) => setCollapseValue(data);
const onChange = (data, checked, children, self) => {
const getValue = Array.isArray(self) ? [...self] : [self];
const getChildrenValues = (options) => {
options.map((option) => {
if (option.options) getChildrenValues(option.options);
if (option.disabled) return null;
return getValue.push(option);
});
};
if (children.length > 0) getChildrenValues(children);
if (checked) {
setValue((prevState) => {
const values = getValue.map(({ value }) => value);
return [
...prevState.filter(({ value }) => !values.includes(value)),
...getValue,
];
});
}
else {
setValue((prevState) => {
return [...prevState].filter((item) => !getValue.includes(item));
});
}
};
return (
<AnvilSelect
value={value}
onChange={onChange}
options={dummyData}
trigger={{ placeholder: 'Select materials' }}
open
multiple
tree={{ onExpand, collapseValue }}
closeOnClickOutside={false}
autoFlipVertically={false}
/>
);
}
render (Example18);
const dummyData = [
{
text: 'Plumbing',
value: 1,
collapsed: false,
options: [
{
text: 'Install Materials',
value: 11,
options: [
{
text: 'Disposer Waste',
value: 111,
},
{
text: 'Frostproof Hydrant',
value: 112,
}
],
},
{
text: 'Service Materials',
value: 12,
options: [
{
text: 'Fluidmaster Fill Valve',
value: 121,
},
{
text: 'Tailpiece Slip Joint',
value: 122,
}
],
},
],
}
];
const Example18 = () => {
const [value, setValue] = React.useState([]);
const [collapseValue, setCollapseValue] = useOnExpand([1, 11, 12]);
const onExpand = (data) => setCollapseValue(data);
const onChange = (data) => setValue(data);
return (
<AnvilSelect
value={value}
onChange={onChange}
options={dummyData}
trigger={{ placeholder: 'Select materials' }}
open
tree={{ onExpand, collapseValue }}
closeOnClickOutside={false}
autoFlipVertically={false}
/>
);
}
render (Example18);
Selection Logic
A Select's tree selection process can be customized to suit different needs. This logic is best shown on the Option List's tree selection logic section. Any option there can be applied inside of a Select.
Use Cases
Sorting Options
The Select does not give an opinion on how choices are sorted. Common types of sorting include alphabetical, time-based, and categorical. Groups can also help in sorting information. The sort type should match up to what a user expects
<Form style={{height: '220px'}}>
<Form.Group widths="equal">
<Form.AnvilSelect
options={[
{text: 'January', value: 1},
{text: 'February', value: 2},
{text: 'March', value: 3},
{text: 'April', value: 4}
]}
trigger={{placeholder: 'Month'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
<Form.AnvilSelect
options={[
{text: 'less than', value: 1},
{text: 'greater than', value: 2},
{text: 'equal to', value: 3}
]}
trigger={{placeholder: 'is...'}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
<Form.AnvilSelect
options={[
{text: 'Small', value: 1},
{text: 'Medium', value: 2},
{text: 'Large', value: 3},
{text: 'Extra Large', value: 4}
]}
trigger={{placeholder: 'Size',}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
<Form.AnvilSelect
options={[
{text: 'Ariel Somebody', value: 1},
{text: 'Bob Someone', value: 2},
{text: 'Caroline Someperson', value: 3},
{text: 'Derrek Something', value: 4}
]}
trigger={{placeholder: 'Technician',}}
open
closeOnClickOutside={false}
autoFlipVertically={false}
/>
</Form.Group>
</Form>
When to use a Select
-
There are at least 5 choices.
-
There is a default, recommended option.
-
Space is at a premium in the interface.
-
When search functionality is useful to narrow options.
When not to use a Select
Caution in using a Select
It is easy to overuse the Select, as it can be used to hide complexity on the page. Discoverability of choices can be obscured when hidden via a Select.
-
For less than 5 options, and screen space is not constrained. Checkboxes for multi-select or Radios for single select may be better.
-
For more than 30 options. Search is highly encouraged for many options. There is not a definitive component for hundreds of choices: a table (data list, Kendo), a Modal, a complex Popover, or a Drawer.
-
When a user benefits from scanning the options, visible Checkbox or Radios on the page allows for more emphasis.
Related Content
Components
-
To build a full form, use the Form component.
-
For less than 5 options, a Radio or Checkbox list surfaces options faster.
Patterns
- Form design pattern for how related controls are ordered.