Dropdown

A form input for making a single selection from a list of items.

  • Install
    npm install @pluralsight/ps-design-system-dropdown
  • Import
    import Dropdown from '@pluralsight/ps-design-system-dropdown'

Examples

Controlling with value

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
import Button from '@pluralsight/ps-design-system-button'
function Example() {
const options = [
{ value: 'beg', label: 'Beginner' },
{ value: 'int', label: 'Intermediate' },
{ value: 'adv', label: 'Advanced' }
]
const [value, setValue] = React.useState(options[1].value)
return (
<div className="example-flex-column">
<Dropdown
label="Level"
placeholder="Select"
onChange={(evt, value, label) => setValue(value)}
menu={options.map(opt => (
<Dropdown.Item key={opt.value} value={opt.value}>
{opt.label}
</Dropdown.Item>
))}
value={value}
/>
<div>Selected: {value}</div>
<div>
<Button
appearance="secondary"
size="xSmall"
onClick={() => setValue('beg')}
>
Set Beginner
</Button>
</div>
</div>
)
}
export default Example

Label

Primary identification of a dropdown comes through the label. Usage hints are given in the placeholder. Supporting text and error messaging is set in the subLabel.

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Comp = () => (
<div className="example-flex-column">
<Dropdown
label="Level"
placeholder="Select level"
subLabel="Which audience is this course aimed at?"
menu={
<>
<Dropdown.Item>Beginner</Dropdown.Item>
<Dropdown.Item>Intermediate</Dropdown.Item>
<Dropdown.Item>Advanced</Dropdown.Item>
</>
}
/>
</div>
)
export default Comp

Appearance

When using the dark theme, a subtle appearance is available. (Switch theme to dark)

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Comp = () => (
<Dropdown
appearance={Dropdown.appearances.subtle}
label="Level"
placeholder="Select level"
menu={
<React.Fragment>
<Dropdown.Item>Beginner</Dropdown.Item>
<Dropdown.Item>Intermediate</Dropdown.Item>
<Dropdown.Item>Advanced</Dropdown.Item>
</React.Fragment>
}
/>
)
export default Comp

Sizes

For table rows, step down to the small size.

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Comp = () => (
<div className="example-flex-column">
<Dropdown
placeholder="Medium dropdown"
menu={
<>
<Dropdown.Item>Beginner</Dropdown.Item>
<Dropdown.Item>Intermediate</Dropdown.Item>
<Dropdown.Item>Advanced</Dropdown.Item>
</>
}
/>
<Dropdown
size={Dropdown.sizes.small}
placeholder="Small dropdown"
menu={
<>
<Dropdown.Item>Beginner</Dropdown.Item>
<Dropdown.Item>Intermediate</Dropdown.Item>
<Dropdown.Item>Advanced</Dropdown.Item>
</>
}
/>
</div>
)
export default Comp

Disabled

Disabled dropdowns are unmodifiable, not interactive, and diminished visually.

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Comp = () => (
<Dropdown disabled label="Can't touch this" placeholder="Just try it" />
)
export default Comp

Error

Error states are engaged with the error flag. Error-related messaging is sent to the subLabel prop.

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Comp = () => (
<Dropdown
error
label="Level"
placeholder="Select level"
menu={
<>
<Dropdown.Item>Beginner</Dropdown.Item>
<Dropdown.Item>Intermediate</Dropdown.Item>
<Dropdown.Item>Advanced</Dropdown.Item>
</>
}
/>
)
export default Comp

Customizing with icon

import React from 'react'
import Dropdown, { useDropdown } from '@pluralsight/ps-design-system-dropdown'
import { CalendarIcon } from '@pluralsight/ps-design-system-icon'
interface DropdownWithIconProps extends HTMLAttributes<HTMLButtonElement> {
icon: React.ReactNode
menu: React.ReactNode
}
const DropdownWithIcon = React.forwardRef<
HTMLButtonElement,
DropdownWithIconProps
>(({ icon, ...props }, forwardedRef) => {
const allProps = useDropdown(props, forwardedRef)
return (
<Dropdown.Layout
{...allProps.layout}
label={<Dropdown.Label {...allProps.label} />}
menu={
<DropdownContext.Provider {...allProps.value}>
<Dropdown.Menu {...allProps.menu} />
</DropdownContext.Provider>
}
subLabel={<Dropdown.SubLabel {...allProps.subLabel} />}
button={
<Dropdown.Button {...allProps.button}>
{icon}
<div style={{ height: '100%', position: 'relative', flex: 1 }}>
<Dropdown.Selected {...allProps.selected} />
</div>
</Dropdown.Button>
}
/>
)
})
function Example() {
return (
<DropdownWithIcon
icon={<CalendarIcon style={{ marginRight: 8 }} />}
menu={
<>
<Dropdown.Item>Trailing 14 Days</Dropdown.Item>
<Dropdown.Item>Last Month</Dropdown.Item>
<Dropdown.Item>Trailing 30 Days</Dropdown.Item>
<Dropdown.Item>Last Quater</Dropdown.Item>
<Dropdown.Item>Trailing 90 Days</Dropdown.Item>
<Dropdown.Item>Custom</Dropdown.Item>
</>
}
/>
)
}
export default Example

Customizing with dynamic icon

import React, { HTMLAttributes } from 'react'
import Dropdown, { useDropdown } from '@pluralsight/ps-design-system-dropdown'
import { CalendarIcon } from '@pluralsight/ps-design-system-icon'
interface DropdownWithIconProps
extends Omit<HTMLAttributes<HTMLButtonElement>, 'onChange'> {
icon: React.ReactNode
onChange?: (e: React.MouseEvent, value: React.ReactText) => void
menu: React.ReactNode
}
const DropdownWithIcon = React.forwardRef<
HTMLButtonElement,
DropdownWithIconProps
>(({ icon, ...props }, forwardedRef) => {
const allProps = useDropdown(props, forwardedRef)
return (
<Dropdown.Layout
{...allProps.layout}
label={<Dropdown.Label {...allProps.label} />}
menu={
<DropdownContext.Provider {...allProps.value}>
<Dropdown.Menu {...allProps.menu} />
</DropdownContext.Provider>
}
subLabel={<Dropdown.SubLabel {...allProps.subLabel} />}
button={
<Dropdown.Button {...allProps.button}>
{icon}
<div style={{ height: '100%', position: 'relative', flex: 1 }}>
<Dropdown.Selected {...allProps.selected} />
</div>
</Dropdown.Button>
}
/>
)
})
function Example() {
const [selected, setSelected] = React.useState<null | string>()
const values = {
channel: {
value: 'channel',
icon: <ChannelIcon style={{ marginRight: 8 }} />,
label: 'Channel'
},
analytics: {
value: 'analytics',
icon: <AnalyticsIcon style={{ marginRight: 8 }} />,
label: 'Analytics'
},
authorKit: {
value: 'authorKit',
icon: <AuthorKitIcon style={{ marginRight: 8 }} />,
label: 'Author Kit'
},
labs: {
value: 'labs',
icon: <LabsIcon style={{ marginRight: 8 }} />,
label: 'Labs'
}
}
const handleChange = (e: React.MouseEvent, value: React.ReactText) => {
setSelected(value)
}
const icon = values[selected] ? (
values[selected].icon
) : (
<div style={{ width: 24, height: 24, marginRight: 8 }} />
)
return (
<DropdownWithIcon
icon={icon}
onChange={handleChange}
menu={Object.values(values).map(({ value, icon, label }) => (
<Dropdown.Item value={value} key={value} icon={icon}>
{label}
</Dropdown.Item>
))}
/>
)
}
export default Example

The dropdown component menu has a max height of 400px and will scroll for content needing more vertical space.

import React from 'react'
import Dropdown from '@pluralsight/ps-design-system-dropdown'
const Example = () => (
<Dropdown
label="Max height example"
menu={
<>
<Dropdown.Item>One item</Dropdown.Item>
<Dropdown.Item>Two item</Dropdown.Item>
<Dropdown.Item>Three item</Dropdown.Item>
<Dropdown.Item>Four item</Dropdown.Item>
<Dropdown.Item>Five item</Dropdown.Item>
<Dropdown.Item>Six item</Dropdown.Item>
<Dropdown.Item>Seven item</Dropdown.Item>
<Dropdown.Item>Eight item</Dropdown.Item>
<Dropdown.Item>Nine item</Dropdown.Item>
<Dropdown.Item>Ten item</Dropdown.Item>
<Dropdown.Item>Eleven item</Dropdown.Item>
<Dropdown.Item>Twelve item</Dropdown.Item>
<Dropdown.Item>Thirteen item</Dropdown.Item>
<Dropdown.Item>Fourteen item</Dropdown.Item>
<Dropdown.Item>Fifteen item</Dropdown.Item>
</>
}
/>
)
export default Example

Accessibility

WCAG 2.1 AA Compliance

100% axe-core tests
No manual audit

WAI-ARIA Patterns: Listbox

Props

Name
Type
Description
Default
appearance
default | subtle
visual style (from Dropdown.appearances)default
disabledbooleanstandard input disabled flag
errorbooleanerror state flag
labelstringidentifying string for dropdown
menuDropdown.Item(s)menu items for dropdown<span></span>
onChange(Event, value, label) => voidtriggered when an item selected
placeholderstringin-field usage hint
size
medium | small
sets dropdown size (from Dropdown.sizes)medium
subLabelstringsupporting text or error messaging
uniqueId(prefix: string) => stringoverride internal id generation for test stability
Name
Type
Description
Default
disabledbooleanvisually disabled, non-interactive
icon*IconAn Icon component
onClick(Event, value) => voidoverride default onClick behavior passed to Dropdown.props.onChange
valuestring | numbervalue sent to ActionMenu#onChange