# FilterPicker

> **NOTICE:** This component was previously named `Combobox`. Use `FilterPicker` for
> filtering existing content. For searchable field selection, use the new Base
> UI-backed [Combobox](../Combobox/Combobox.md).

FilterPicker lets users narrow an existing list, table, report, or page of
records. Use it when selected options act as filters for nearby content.

If the user is choosing a value to save, submit, assign, or display in a form
field, use [Combobox](../Combobox/Combobox.md) instead.

## Design & usage guidelines

FilterPicker's primary function is to apply filter criteria to existing content.

The FilterPicker also has the flexibility to allow for custom actions, such as
adding a new item to the list of options or managing a selection.

## Content guidelines

FilterPicker is designed to handle lists of selectable options, so the component
is text based.

You may customize the activator and action `label`. Refer to the
[Product Vocabulary](../product-vocabulary/product-vocabulary.md) for consistency in consistency
in terminology and naming conventions when setting these values.

You may also set an optional `subjectNoun` for the FilterPicker content. This
will be used in the
[empty state](/storybook/web/?path=/story/components-selections-filterpicker--empty-state)
of the FilterPicker, as well as the noun within the search placeholder.

You may include a prefix element to any `FilterPicker.Option` by setting its
`prefix` prop. This could be a [StatusIndicator](../StatusIndicator/StatusIndicator.md) or
[Icon](../Icon/Icon.md) to provide additional context to the option.

### Empty states

In cases where the list of options is empty, the FilterPicker content will
display a message indicating the absence of options. The default will read, *"No
options yet"* or *"You don't have any \[subjectNoun] yet"* if you've set the
`subjectNoun` prop.

If options are available, but the search query does not match any of them, the
FilterPicker Content will display a message indicating that no options match the
search query.

## Accessibility

| Shortcut Keys   | Action                                                                                     |
| --------------- | ------------------------------------------------------------------------------------------ |
| Space or Return | Open/close the FilterPicker                                                                |
| Escape          | Close the FilterPicker and return focus to the activator                                   |
| Tab             | When closed: Move to activatorWhen open: Move from search input to clear button to actions |
| Up/Down Arrow   | Navigate through options list                                                              |
| Space or Return | Select focused option                                                                      |

### Assistive technology

* The activator element is given a `role="combobox"` to inform the user of the
  type of content they'll be interacting with
* Once the user opens the FilterPicker, the search input is focused and
  announced
* The title and "selected/unselected" status of each item is announced as the
  user navigates the options
* The actions (if present) are announced and operable as buttons


## Configuration

FilterPicker is composed of three main parts: Activator, Content, and Action.

### Activator

The activator is the user-facing interface to open and close the FilterPicker
and initiate option selection.

#### Default activator

To use the default activator simply omit any kind of `FilterPicker.Activator`
subcomponent in your instance.

Examples of using a default activator can be found in the
[single-select](/storybook/web/?path=/story/components-selections-filterpicker--single-select)
and
[multi-select](/storybook/web/?path=/story/components-selections-filterpicker--multi-select)
stories. The label of the default activator can be customized by adding the
optional `label` prop to FilterPicker, to better reflect the type of data being
selected. Otherwise, the default activator label will read "Select".

The optional `label` prop on FilterPicker is only effective when using the
default activator.

#### Custom activator

The custom activator should be used in any instance where the default activator
is not suitable.

**Child React Component:** To use a customized `Chip` or `Button` your custom
activator should be a child of `<FilterPicker.Activator />`. An example of using
a custom activator can be found in the
[custom activator](/storybook/web/?path=/story/components-selections-filterpicker--custom-activator)
story.

**Child Render Function:** If neither a Chip nor a Button is sufficient for your
needs, you may instead provide a function as the child to
`FilterPicker.Activator`.

As arguments, the function will receive a method to open the FilterPicker
(closing is handled internally), and accessibility attributes required for a
FilterPicker. **You must implement these yourself**. See the
[render function example](/storybook/web/?path=/story/components-selections-filterpicker--custom-activator)
for reference.

The render function must return a single, valid JSX Element (it can of course
have children, it simply must have a single wrapping tag). Ensure that tabindex,
focus, and keyboard interactivity are considered and handled appropriately if a
non traditionally interactive element such as a div is used.

### Content

The content of the FilterPicker is responsible for displaying the list of
selectable options, handling user interactions, and managing search
functionality.

`<FilterPicker.Option />` represents an individual option within the list. Each
option has `id`, `label`, and `prefix` props which are used to identify and
display the option.

#### Custom content

You can use the `prefix` option to render content on the left side of the
provided `label`. An example of this behaviour can be found in the
[Prefix Options](/storybook/web/?path=/story/components-selections-filterpicker--prefix-options)
story.

If you need more advanced control of the children inside each option, you can
use the `customRender` prop to supply your own content. Check out the
[Custom Render Options](/storybook/web/?path=/story/components-selections-filterpicker--custom-render-options)
story for an example.

### Action

Actions are an optional way to allow users to perform custom tasks such as
adding a new item to the list of options.

By default, when an action is clicked, the FilterPicker will close. If you want
the FilterPicker to remain open after an action is clicked, you can use the
`keepOpenOnClick` prop. An example of this behaviour can be found in the
[Keep Open on Click](/storybook/web/?path=/story/components-selections-filterpicker--keep-open-on-click)
story.

### Selection behavior

#### Single-select

Single-select is FilterPicker's default selection mode. When a selection is
made, the FilterPicker will close.

#### Multi-select

Add the `multiSelect` prop to allow multiple options to be selected and/or
un-selected. In multi-select mode, the FilterPicker will only close when a user
takes an explicit action to close it such as clicking outside the FilterPicker
or pressing the ESC key.

With multiSelect activated, a header appears beneath the search input, providing
information about the number of selected items. Additionally, a button allows
users to either select or deselect all options in the list.

In the case of selecting all options in a filtered state, only the currently
visible filtered options will be selected. When deselecting all options, every
item will be deselected, irrespective of the filter state.

### Selection callback

As selections are made, the data will be returned via an argument to the
function provided to the `onSelect` prop.

Please note that regardless of if it is a single-select or a multi-select, the
`onSelect` will be called immediately as options are selected (or un-selected in
the case of multi-select). Multi-select will receive an array of all selections,
not only the most recent.

### onClose

If it is desirable to wait until the FilterPicker has been closed, likely
indicating that all selections have been made, it is recommended to use the
optional `onClose` callback which will fire when the FilterPicker is closed by
any means. This callback provides no arguments.

While the selections will still be updated in real time, this callback will
allow you to call any potentially expensive operations only once the
FilterPicker has been closed. As such, it will only have a difference in timing
for multi-select since a single-select selection will immediately close the
FilterPicker.

### Selection display

When using the default activator, the selections will be displayed inside the
activator. The data will still be returned via callback.

When using a custom activator, the selections will only be returned as data
keeping the choice of how to display the data flexible.

### Custom onSearch

The `onSearch` prop allows you to hook into the FilterPicker's search input and
receive what the user has typed. This callback is debounced by 300ms by default
but can be configured with the `onSearchDebounce` prop.

When using this callback, we no longer filter the options as a user types.
Everything is left up to the `onSearch` callback and accompanying code to
implement.

An example use case here would be using the search term to fire off a request to
fetch the appropriate data for that term. It is recommended to use the `loading`
prop while fetching & filtering the new data. The loading state will display a
static amount of `Glimmer` components. Then updating `loading` once the new data
has been fetched and the `FilterPicker.Option`s have been replaced with the new
ones.

Note that the component does not cache the initial options when using
`onSearch`. If new data was fetched and replaced the initial options on search
then a user clears the search, either with the clear button or deleting the
term, the initial options would need to be restored manually.

## Testing

When using Jest to test FilterPicker implementations, you will need to include
this mock in your test setup:

```js
const scrollIntoViewMock = jest.fn();
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
```

## Developer notes

* The FilterPicker confines tabbing to within itself while it's open using
  [useFocusTrap](/hooks/useFocusTrap)
* Pressing `esc` will close the FilterPicker and return focus to the activator
  using [useRefocusOnActivator](/hooks/useRefocusOnActivator)


## Props

### Web

#### FilterPicker

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `onSelect` | `(selection: FilterPickerOptionProps[]) => void` | Yes | — | Callback function invoked upon the selection of an option. Provides the selected option(s) as an argument. |
| `selected` | `FilterPickerOptionProps[]` | Yes | — | The selected options of the FilterPicker. |
| `defaultActivatorRef` | `Ref<HTMLButtonElement>` | No | — | A ref to the default activator button element. |
| `label` | `string` | No | — | The Chip heading for the trigger |
| `loading` | `boolean` | No | — | Should the FilterPicker display the loading state. |
| `multiSelect` | `boolean` | No | `false` | When `true`, `FilterPicker` will allow for multiple selections |
| `onClear` | `() => void` | No | — | Callback function invoked upon the clearing of all options. This is only available when `multiSelect` is `true`. |
| `onClose` | `() => void` | No | — | Callback function invoked upon the FilterPicker menu closing. |
| `onLoadMore` | `() => void` | No | — | Callback to load more options, this is called when the user scrolls to the bottom of the list. |
| `onSearch` | `(searchValue: string) => void` | No | — | Debounced callback function invoked on FilterPicker search input change. Receives the current search value as an argu... |
| `onSearchDebounce` | `number` | No | — | The amount of time in ms to debounce the onSearch callback. Defaults to 300ms. |
| `onSelectAll` | `(selection: FilterPickerOptionProps[]) => void` | No | — | Callback function invoked upon the selection of all options. Provides the selected option(s) as an argument. This is ... |
| `subjectNoun` | `string` | No | — | The encapsulating noun for the content of the combobox. Used in the empty state, and search placeholder. Should be pl... |

#### FilterPicker.Action

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `label` | `string | ((options: FilterPickerActionCallbackOptions) => string)` | Yes | — | The label text of the action. |
| `onClick` | `(event: MouseEvent<HTMLButtonElement, MouseEvent>, options: FilterPickerActionCallbackOptions) => void` | Yes | — | The function to call when the action is clicked. |
| `keepOpenOnClick` | `boolean` | No | `false` | Keeps the FilterPicker open when the action is clicked. |
| `visible` | `boolean | ((options: FilterPickerActionCallbackOptions) => boolean)` | No | — | Determine if the action is visible for a given item. |

#### FilterPicker.Option

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `id` | `number | string` | Yes | — | A unique identifier for the option. |
| `label` | `string` | Yes | — | The value to be visually displayed in the FilterPicker options list. |
| `customRender` | `(option: Pick<FilterPickerOptionProps, "prefix" | "label" | "id"> & { isSelected: boolean; defaultContent: ReactElement<unknown, string | JSXElementConstructor<any>>; }) => ReactNode` | No | — | Advanced: A custom render prop to completely control how this option is rendered. The function receives the option's ... |
| `onClick` | `(option: FilterPickerOptionProps) => void` | No | — | Callback function invoked when the option is clicked. |
| `prefix` | `ReactNode` | No | — | An optional component to be displayed before the label. |
