Back to Grafana

Select

packages/grafana-ui/src/components/Select/Select.mdx

13.1.16.2 KB
Original Source

import { ArgTypes } from '@storybook/addon-docs/blocks'; import { Select, AsyncSelect } from './Select';

Select

Select is the base for every component on this page. The approaches mentioned here are also applicable to AsyncSelect, MultiSelect, AsyncMultiSelect.

When to use

This component is particularly useful when you have a large number of options to choose from. Additionally, consider using Select in favor of radio button components when you have more than 4 options to choose from.

Select variants

Select is an input with the ability to search and create new values. It should be used when you have a list of options. If the data has a tree structure, consider using Cascader instead. Select has some features:

  • Search a list of values
  • Select multiple values
  • Select from async data
  • Create custom values that aren't in the list

Options format

There are four properties for each option:

  • label - Text that is visible in the menu.
  • value - Could be anything, but is usually a string. Used to identify what is actually selected.
  • description - Longer description that describes the choice. Use this sparingly.
  • imgUrl - URL to an image. Use this when an image or icon provides more context for the option.
jsx
const options = [
  { label: 'Basic option', value: 0 },
  { label: 'Option with description', value: 1, description: 'this is a description' },
  {
    label: 'Option with description and image',
    value: 2,
    description: 'This is a very elaborate description, describing all the wonders in the world.',
    imgUrl: 'https://placekitten.com/40/40',
  },
];

Creatable option

Creatable option is used when you want to be able to add a custom value to the list of options. allowCustomValue needs to be true and you must handle the value creation with onCreateOption.

jsx
import { Select } from "@grafana/ui";

const SelectComponent = () => {
  const [value, setValue] = useState<SelectableValue<number>>();

  return (
    <Select
      options={option}
      value={value}
      allowCustomValue
      onCreateOption={customValue => {
        setValue(customValue);
      }}
    />
  );
};

Resetting selected value from outside the component

If you want to reset the selected value from outside the component, e.g. if there are two Select components that should be in sync, you can set the dependent Select value to null in the onChange handler of the first Select component.

tsx
import { useState } from 'react';
import { Select } from '@grafana/ui';

const SelectComponent = () => {
  const [person, setPerson] = useState<string | undefined>('');
  const [team, setTeam] = useState<string | undefined | null>('');

  return (
    <form>
      <Select
        onChange={({ value }) => {
          setPerson(value);
          setTeam(null); // Setting the team to null will reset the selected value in the team Select
        }}
        options={[
          {
            value: 'option1',
            label: 'Option 1',
          },
          {
            value: 'option2',
            label: 'Option 2',
          },
        ]}
        value={person}
        backspaceRemovesValue
      />
      <Select
        onChange={({ value }) => setTeam(value)}
        options={[
          {
            value: 'team1',
            label: 'Team 1',
          },
          {
            value: 'team',
            label: 'Team 2',
          },
        ]}
        value={team}
      />
    </form>
  );
};

AsyncSelect

Like regular Select, but handles fetching options asynchronously. Use the loadOptions prop for the async function that loads the options. If defaultOptions is set to true, loadOptions will be called when the component is mounted.

jsx

import { AsyncSelect } from '@grafana/ui';

const basicSelectAsync = () => {
  const [value, setValue] = useState<SelectableValue<string>>();

  return (
    <AsyncSelect
      loadOptions={loadAsyncOptions}
      defaultOptions
      value={value}
      onChange={v => {
        setValue(v);
      }}
    />
  );
};

Where the async function could look like this:

tsx
const loadAsyncOptions = () => {
  return new Promise<Array<SelectableValue<string>>>((resolve) => {
    setTimeout(() => {
      resolve(options);
    }, 2000);
  });
};

MultiSelect

Possible to Select multiple values at the same time.

tsx
import { MultiSelect } from '@grafana/ui';

const multiSelect = () => {
  const [value, setValue] = useState<Array<SelectableValue<string>>>([]);

  return (
    <>
      <MultiSelect
        options={options}
        value={value}
        onChange={(v) => {
          setValue(v);
        }}
      />
    </>
  );
};

AsyncMultiSelect

Like MultiSelect but handles data asynchronously with the loadOptions prop.

Virtualized option list

The Select component can be configured to use a virtualized option list. The virtualized prop must be set to true to enable this behavior. This is useful when you have a large number of options, as it improves performance by only rendering the options that are currently visible in the dropdown.

Adding icons to Select

A custom icon can be added to Select, which will be displayed before the selected value, by using the prefix prop.

tsx
<Select options={options} value={value} onChange={setValue} prefix={<Icon name="arrow-down" />} />

Testing

Using React Testing Library, you can select the <Select /> using its matching label, such as the label assigned with the inputId prop. Use the react-select-event package to select values from the options.

tsx
import { render, screen } from '@testing-library/react';
import selectEvent from 'react-select-event';
import { Select } from '@grafana/ui';

it('should call onChange', () => {
  const onChange = jest.fn();

  render(
    <>
      <label htmlFor="my-select">My select</label>
      <Select onChange={onChange} options={options} inputId="my-select" />
    </>
  );

  const selectEl = screen.getByLabelText('My select');
  expect(selectEl).toBeInTheDocument();

  await selectEvent.select(selectEl, 'Option 2', { container: document.body });
  expect(onChange).toHaveBeenCalledWith({
    label: 'Option 2',
    value: 2,
  });
});

Props

<ArgTypes of={Select} />