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

Selected: int
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.

Which audience is this course aimed at?
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