Dropdown
Dropdown is a pull-down list of items that appear when the component is selected; this list remains open until a user selects an item or dismisses the list.
Playground
Size
The "mini" size will make the dropdown shorter than normal.
<Box backgroundColor="light-grey" p="4"><Dropdown sizeVariant="mini"><DropdownOption value="A">Option A</DropdownOption><DropdownOption value="B">Option B</DropdownOption><DropdownOption value="C">Option C</DropdownOption></Dropdown></Box>
<select class="stylized-select stylized-select-mini">etc</select>
Rounded
A rounded Dropdown has rounded corners. As of fall 2020, this is now the Visage default, but it might not always be in the future (or when using some themes).
Borderless
A borderless Dropdown has no visible border around it.
<Box backgroundColor="light-grey" p="4"><Dropdown borderless><DropdownOption value="A">Option A</DropdownOption><DropdownOption value="B">Option B</DropdownOption><DropdownOption value="C">Option C</DropdownOption></Dropdown></Box>
<select class="stylized-select stylized-select-borderless">etc</select>
Skins
There is an "error" skin to indicate there's an error with the current selection. However, in React, you will not need to set this explicitly if you use the FormField
component with a FormError
; that component will set the error skin for you when an error message is present. See below.
Label, Error Message, and Helper Text
Dropdown labels and error text should use the standard Form component.
Vanilla API
See Form for more.
React API
In React, you should make use of the FormField
components to add a label, error message, or helper text. These components will automatically add the proper aria attributes to the Dropdown
so that you don't need to handle them yourself.
Note that the FormField
components don't add much styling on their own, and that you'll probably want to nest the FormField
inside of a StandardForm
in order to get the desired look.
If you inspect the DOM, you'll see that the FormLabel
automatically has its for
attribute set to the id of the Dropdown
and that the Dropdown
's aria-describedby
prop is set to the id of the FormHelper
.
<StandardForm><FormField><FormLabel>Favorite animal <FormLabelOptional>(Optional)</FormLabelOptional></FormLabel><FormInputGroup><Dropdown><DropdownOption>Panda</DropdownOption><DropdownOption>Otter</DropdownOption><DropdownOption>Kitten</DropdownOption></Dropdown><FormHelper>There is no wrong answer</FormHelper></FormInputGroup></FormField></StandardForm>
Here's an example with an error. Notice that we don't have to add any special props to the Dropdown
! The presence of a FormError
is enough for the Dropdown
to know that it should assume an error-state look. If you inspect the DOM, you'll also find that the aria-errormessage
and aria-describedby
props on the Dropdown
are automatically set.
<StandardForm><FormField><FormLabel>Favorite animal</FormLabel><FormInputGroup><Dropdown><DropdownOption hidden disabled>Select an animal</DropdownOption><DropdownOption>Panda</DropdownOption><DropdownOption>Otter</DropdownOption><DropdownOption>Kitten</DropdownOption></Dropdown><FormError>You must make a selection</FormError></FormInputGroup></FormField></StandardForm>
Managing State in React
As is the case with most form elements, folks often want to "control" this component. Controlling a component means that you take ownership over its state rather than letting the browser manage the state for you. When you control a component, you own the source of truth for the UI. This means that you don't need to query the DOM in order to determine the current value of the element. So, when it comes time to use the value of the element somewhere else (e.g. submitting the selection to a back-end service) you can trust your stored value.
If you'd like to control a Dropdown
, you'll need store the currently-selected value somewhere (e.g. React state or a Redux store), and you'll need to provide a handler to update that value when the user interacts with the component.
e.g.
const MyDropdown = props => {const [selectedValue, setSelectedValue] = React.useState('A')return (<Dropdownvalue={selectedValue}onChange={event => setSelectedValue(event.target.value)}><DropdownOption value="A">A</DropdownOption><DropdownOption value="B">B</DropdownOption><DropdownOption value="C">C</DropdownOption></Dropdown>)}
If you don't care about the state of the Dropdown
, you can simply omit the value
and onChange
props and let the browser handle the state for you. In this case, you may want to provide the defaultValue
prop so that the browser knows which value to select as the initial state.
Components
Dropdown
Prop | Type | Default | Description |
---|---|---|---|
skin | "line" | "standard" | "error" | The visual variant Note that | |
sizeVariant | "standard" | "mini" | "standard" | size (height) of the dropdown |
fullWidth | boolean | false | Whether or not the Dropdown should expand to fill the entire width of its container |
rounded | boolean | false | Whether or not the Dropdown should have rounded corners |
borderless | boolean | false | Whether or not the Dropdown should have a border around it |
All other props are forwarded to the element specified in thecomponent
prop(default: <select/>
)
DropdownOption
DropdownOption
has no props of its own
All props are forwarded to the element specified in thecomponent
prop(default: <option/>
)
Implementation Notes
Our dropdown uses (and extends) the native <select>
element, rather than try to reproduce its functionality with HTML. This does impose some limits on the styling we can perform on the options inside the dropdown. There are many reasons for using a native dropdown:
Since 2019, about half of our sessions are on tablets and phones. Using a native
<select>
means that phone users get their device's own select experience, which is mobile-optimized. (For instance, iPhone users get the fullscreen "scroll wheel" display.) The browser manufacturers have invested a lot of UX research into determining that this is a good user experience for touch-screen users.Replicating this experience across browsers is complex, and thus costly to maintain. Apple, Google, and Microsoft have invested considerable development and QA time into their versions, which we get to use for free. Building it ourselves would be technically challenging, would require considerable cross-browser and cross-device testing, and we would have a continuous maintenance cost on this going forward.
Consider a small screen where the dropdown, when opened, drops down off the bottom edge of the screen. If the user touches the screen to scroll downward to see more choices, that touch will be registered as a click on the dropdown, thus making a selection and closing the dropdown! This is not an easy usability problem to surmount, which is exactly why phone browsers have built their own unique dropdown experiences such as the iOS scroll wheel.
Recreating the browser's own built-in functionality always makes for poor performance, both in terms of bandwidth (the browser must download the custom code) and rendering speed (a JavaScript-based dropdown will never react as quickly as the browser's built-in version.) In addition, the custom version won't react to user input as quickly as a native dropdown, making it feel sluggish.
The native control automatically has accessibility support across devices, including assistive technologies. Replicating the accessibility features would require even more JavaScript (e.g. supporting keyboard access), creating an even greater negative impact on performance.
Any given implementation will create an unfamiliar user experience for some users. For instance, the iPhone Safari dropdown doesn't look or act like the Android Chrome dropdown, so if we made our dropdown look like one of these, users of the other one would have a strange-looking dropdown. The dropdowns on Chrome for Mac don't even look like the ones on Chrome for Windows! The "correct" visual experience on one browser/OS combination will look strange on any other combination, so by using the browser's built-in dropdown, the user always gets a comfortable and familiar user experience.
Guidelines
Designer Guidelines
For usability, the dropdown uses generous padding and a large font. This means that the control does have a certain minimum width (this number depends on the widest option within the dropdown). For this reason, you should be sure to place it in a part of the page that gives it sufficient room to render, including the possibility that translated text might be longer than English text. If it is placed in an area that is too narrow, it may not display properly.
We have limited styling choices for the content of the dropdown's menu items, because we use the browser's native dropdown menu (see "Implementation Notes") to provide an optimal experience on mobile devices and assistive technologies. The browsers don't give you as much styling control as you might want; for instance, we can set the font color, size, and weight, but only for the entire dropdown, not for just one part of it. We cannot use strikethrough text or set any layout inside those dropdown choices.
Developer Guidelines
For the vanilla JS API, if your dropdown is created after the DOMready event, you will need initialize it by triggering an
initializeControl
event on it.We recommend that you not place a dropdown inside an element that is a table cell or has CSS
display: table-cell
on it, as this can cause the dropdown to be rendered more narrowly than its true minimum width.
Accessibility Guidelines
Dropdowns, like any form input field, need an accessible label, preferably a
<label>
tag, with afor
attribute whose value is the id of the dropdown. (If a<label>
tag is not possible, the<select>
tag either needs aaria-labelledby
attribute, or anaria-label
attribute with a localized value.)When a dropdown has an error, the error message should not be a
<label>
tag pointing to the dropdown. Instead, the<select>
tag should havearia-describedby
andaria-errormessage
attributes whose value is the id of the error message. In addition, while the dropdown's value is invalid, the<select>
should havearia-invalid="true"
on it.If an dropdown is a required field before the user can submit the page, add the attribute
aria-required="true"
to the<select>
tag.