Design system

Filter dropdown

The compact workflow filter on the Recommendations page: the chosen option on a small button, and a list with a count beside each option.

import { FilterDropdown } from "@/pages/recommendations/index";

FilterDropdown narrows a list to one option, such as one workflow, and shows how many items each option has. The button shows only the chosen option, with no label in front of it, so it fits a narrow header. The list is the product dropdown menu.

Usage

It sits at the right of the Decision queue header on the Recommendations page. The counts are the open recommendations for each workflow, after the channel filter is applied.

<FilterDropdown
  ariaLabel="Filter by workflow"
  size="sm"
  value={scope}
  onChange={pickScope}
  options={chips.map((c) => ({
    value: c.id,
    label: c.id === "all" ? "All workflows" : c.name,
    count: c.count,
  }))}
/>

Sizes

size is passed to the Button. sm is the default and the only size the product uses. Both sizes have 12px text, because FilterDropdown overrides the small button's 10px.

<FilterDropdown ariaLabel="Filter by workflow" size="sm" align="left" value={scope} onChange={setScope} options={options} />
<FilterDropdown ariaLabel="Filter by workflow" size="md" align="left" value={scope} onChange={setScope} options={options} />

Long names

The button is at most 180px wide and cuts a longer name short with an ellipsis. The list is at least 240px wide and shows the whole name, and both carry it as a title, so hovering shows it in the browser's own tooltip. The list scrolls after 320px.

<FilterDropdown ariaLabel="Filter by workflow" value="delivery-leaks" onChange={setScope} options={options} align="left" />

Anatomy

PartStyle
ButtonButton variant="secondary": white, --color-primary-500 border and text. 12px text, CaretDown after the label, at most 180px wide.
ListThe dropdown menu panel, at least 240px wide and at most 320px tall. It lines up with the button's right edge by default.
OptionThe optional icon, then the name (14px, weight 500 when chosen), then the count in 12px --text-muted with tabular figures.
SelectedA 16px bold Check in --color-primary-500 after the count.

Props

PropTypeDefaultNotes
valuestringThe value of the chosen option. If none matches, the button shows the first option.
optionsOption[]See below.
onChange(value) => voidCalled with the option's value on every pick, including the one already chosen.
ariaLabelstringBecomes the button's aria-label. See Accessibility.
size"sm" | "md""sm"The Button size.
align"right" | "left""right"Which edge of the button the list lines up with. Any value other than "right" lines it up on the left.
Option fieldTypeNotes
valuestringCompared with value.
labelstringShown on the button when chosen, and in the list.
countnumberOptional. Shown on the right of the row, including 0.
iconReact elementOptional, placed before the name. Pass an element (<Flask size={16} />), not a component. No caller uses it today.

Accessibility

  • The menu is Base UI's: arrow keys, typing to jump, Enter to pick, and Escape to close and return focus to the button.
  • ariaLabel replaces the button's text as its accessible name. A screen reader hears "Filter by workflow" and not the chosen workflow, and someone using voice control cannot say the words they see on the button. Until this changes, include the value: ariaLabel={"Filter by workflow: " + selected.label}.
  • The chosen option is shown only by its weight and the tick. It has no aria-checked or aria-current.
  • The full name of a cut-short option is only in a title, which does not show on keyboard focus.

When to use it

Use it only where it is used today. For a new filter, use a Composer menu or the filter in Menus and filters: they name what they filter on the button, use the quieter secondaryGhost button, and are reached by keyboard and screen reader in the same way.

FilterDropdownComposer menu
ButtonBlue secondary, the value onlyGrey secondaryGhost or ghost, label and value
CountsRight-aligned in their own columnWritten into the label
Second lineNoOptional description
Long namesCut at 180pxNot cut

Do and don't

  • Do make the first option the unfiltered one, such as "All workflows", and give it the total.
  • Do keep counts as the number of items the person still has to act on, so they add up.
  • Don't use it for actions or navigation. Every option sets the filter.
  • Don't put two FilterDropdowns side by side. Without a label on the button, people cannot tell them apart.

Where it is used

FileUse
src/pages/recommendations/index.jsxThe workflow filter in the Decision queue header. The choice is kept in the page's scope state, which starts from the ?workflow= URL parameter.

The function is defined in that file and exported from it for these docs.

On this page