npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@highlight-ui/select

v10.6.8

Published

Select component for the Highlight UI library

Downloads

8,555

Readme

npm personio.design storybook.personio.design

@highlight-ui/select

Feature-rich select control.

Collects user information by displaying a list, triggered by click action, allowing single or multiple selection.

Features

  • Supports selection of one or multiple options
  • Asynchronous flow
    • built-in search bar
    • search term highlighted results
    • optional search in sub labels
    • dynamically generated options
    • loader
  • Option groups
    • split multiple options my line separator and title
  • Option types
    • single-line
    • colored
    • multi-line
    • avatar
  • Option metadata
    • built with TS generics
  • Footer
    • apply action
    • cancel action
  • Creatable option
  • Support for i18n
  • Controlled component
  • Variants
    • inline
    • full-width
  • 🆕 🧪 Early access features
    • New search input
    • New trigger

Installation

Using npm:

npm install @highlight-ui/select

Using yarn:

yarn add @highlight-ui/select

Using pnpm:

pnpm install @highlight-ui/select

Once the package is installed, you can import the library:

import { Select } from '@highlight-ui/select';

Usage

Single

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function SingleSelectExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      options={[
        { label: 'Genesis', value: '23095' },
        { label: 'Destinee', value: '26211' },
      ]}
    />
  );
}

Multiple

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function MultiSelectExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  return (
    <Select
      multiple
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      options={[
        { label: 'Genesis', value: '23095' },
        { label: 'Destinee', value: '26211' },
      ]}
    />
  );
}

🆕 🧪 Early access features

The following sections highlight new features that can be enabled via props.

Search input

When the content is open, a search input will be visible if the searchable prop is set to true and the number of options is equal or above the number as provided through the minOptionsToDisplaySearch prop.

By default enableNewSearchInput will be false. Below is a comparison of what it renders, based on its value.

| enableNewSearchInput | Component | Comments | | :--------------------- | :------------------- | :----------------------------------------------------------------------------------------- | | false | DefaultSearchInput | Wrapper with role="search" containing a <label> which groups an <Icon> and <input> | | true | SearchInput | Uses the <TextInput> component with an <Icon> as prefix |

Trigger

By default enableFlowTriggers will be false. Below is a comparison of what it renders, based on its value.

| enableFlowTriggers | multiple | Component | Comments | | :------------------- | :--------- | :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | | false | false | DefaultTrigger or Custom renderTrigger() | <button> showing the selected option (by default) or a custom element | | false | true | DefaultSearchInput or Custom renderTrigger() | <button> showing the selected options count and clear icon (by default) or a custom element | | true | false | InputTrigger | <InputContainer> component with an <IconButton> as suffix, showing the selected option | | true | true | MultiInputTrigger | <InputContainer> component with an <IconButton> as suffix, showing the selected options as <SelectChip>s and clear icon |

Advanced usage

Asynchronous flow

This example focus on loading options asynchronously. In real-world example, in the onListVisibilityChange handler hardcoded options would be replaced by API call.

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

const initialOptions: SelectOption[] = [
  { label: 'Genesis', value: '23095' },
  { label: 'Destinee', value: '26211' },
  { label: 'Chyna', value: '86910' },
  { label: 'Alexie', value: '49249' },
  { label: 'Natalie', value: '18694' },
];

export default function AsyncExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  const [options, setOptions] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      searchable
      isLoading={isLoading}
      minOptionsToDisplaySearch={1}
      onListVisibilityChange={(visible) => {
        if (visible) {
          setIsLoading(true);
          // mocks API call. Should be replaced with network request
          setTimeout(() => {
            setOptions(initialOptions);
            setIsLoading(false);
          }, 3000);
        }
      }}
      options={options}
    />
  );
}

Option groups

Option groups provide a way to visually group multiple options. Groups are determined by specifying a group title in each option.

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function OptionGroupsExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      options={[
        { label: 'Genesis', value: '23095', group: 'One' },
        { label: 'Destinee', value: '26211', group: 'Two' },
      ]}
    />
  );
}

Option types

Four different option types are supported:

  • single-line
  • colored
  • multi-line
  • avatar

Option types are determined by specifying optionType prop. Additionally, each option type has its own props which are specified in each option.

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function OptionTypesExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      optionType="colored"
      options={[
        { label: 'Genesis', value: '23095', color: 'red' },
        { label: 'Destinee', value: '26211', color: 'green' },
      ]}
    />
  );
}

In addition to the label, value and group, these props are used to define option type:

| Prop | Type | Option type | Description | | :------------- | :------------------ | :-------------------- | :------------------------------------------------------ | | disabled | boolean | all | Should the option be disabled or enabled | | metadata | ComponentMetadata | all | Object used for testing. Contains testId and actionName | | subLabel | string | multi-line and avatar | Smaller, descriptive text used below main label | | avatar | string | avatar | URL used to fetch avatar image | | imageLoading | eager or lazy | avatar | Specifies how browser will load avatar image |

Creatable option

Select has a support for options dynamically created by user.

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function CreatableOptionExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  const [options, setOptions] = useState([
    {
      label: 'Genesis',
      value: '23095',
    },
  ]);
  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      creatableOptions={{
        createButtonProps: {
          label: 'Add',
          loading: false,
        },
        inputProps: {
          placeholder: 'Create a new one',
        },
        onCreate: (item) => {
          setOptions([...options, { label: item.value, value: item.value }]);
        },
      }}
      options={options}
    />
  );
}

⚠️ Submit footer cannot be used alongside creatable options.

CreatableOptionsProps

| Prop | Type | Required | Default | Description | | :--------------------------- | :----------------------------------- | :------- | :------ | :------------------------------------------------------ | | createButtonProps.label | string | Yes | | Text used in the submit button | | createButtonProps.loading | boolean | No | false | Defines if the submit button will be in loading state | | createButtonProps.metadata | ComponentMetadata | No | null | Object used for testing. Contains testId and actionName | | inputProps.placeholder | string | No | null | Placeholder used in search bar | | inputProps.metadata | ComponentMetadata | No | null | Object used for testing. Contains testId and actionName | | onCreate | function(item: SelectOption): void | Yes | null | Function called whenever new option is created |

Submit footer

Select has a support for submit footer.It provides distinction between selected and submitted options. This means that changes in selected options can be discarded.

import React from 'react';
import { Select, SelectOption } from '@highlight-ui/select';

export default function SubmitFooterExample() {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  const [submittedOptions, setSubmittedOptions] = useState<SelectOption[]>([]);

  return (
    <Select
      triggerLabel="Pick a name"
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      multiple
      options={[
        { label: 'Genesis', value: '23095' },
        { label: 'Destinee', value: '26211' },
      ]}
      submitOptions={{
        onCancel: () => setSelectedOptions(submittedOptions),
        onSubmit: (options) => setSubmittedOptions(options),
      }}
    />
  );
}

⚠ Creatable options cannot be used alongside submit footer.

SubmitOptions

| Prop | Type | Required | Default | Description | | :-------------------------- | :---------------------------------------- | :------- | :------- | ----------------------------------------------------- | | submitOptions.onSubmit | function(options: SelectOption[]): void | Yes | | Function called whenever the submit button is pressed | | submitOptions.onCancel | function(): void | Yes | | Function called whenever the cancel button is pressed | | submitOptions.submitLabel | string | No | 'Apply' | Label used in the submit button | | submitOptions.cancelLabel | string | No | 'Cancel' | Label used in the cancel button |

Props 📜

ℹ️ Whenever SelectOption is used, there is an option to extend this interface and provide your own metadata.

| Prop | Type | Required | Default | Description | | :-------------------------- | :------------------------------------------------------------------ | :------- | :------------------- | :--------------------------------------------------------------------------------------------------------- | | options | SelectOption[] | Yes | | List of objects | | onSelect | function(values: SelectOption[]): void | Yes | | Gets called each time an option is selected | | selectedOptions | SelectOption[] | Yes | | List of selected options | | className | string | No | null | Class name to apply to the element | | listClassName | string | No | null | Class name to apply to the List element | | closeOnSelect | boolean | No | false | Should the options list disappear by each selection? | | disabled | boolean | No | false | Should trigger be enabled or disabled | | selectAllLabel | string | No | 'Select all' | Label for Select all functionality when multiple prop is true | | metadata | ComponentMetadata | No | null | Object used for testing. Contains testId and actionName | | onSearchChange | function(search: string): void | No | null | Gets called whenever text is typed in search bar | | onListVisibilityChange | function(visible: boolean): void | No | null | Gets called whenever the visibility of the select's list has changed | | emptyStateLabel | string | No | 'Nothing to display' | Label for displaying the empty state of the Select list | | renderEmptyState | function(props: SelectEmptyStateProps): ReactNode | No | null | Function to return custom empty options list message | | renderTrigger | function(props: SelectTriggerProps & ref: RefCallback): ReactNode | No | null | Function to return a custom trigger | | optionType | single-line or colored or multi-line or avatar | No | 'single-line' | String specifying the option type that should be rendered. | | variant | inline or full-width | No | 'inline' | inline when used outside forms and full-width when used inside forms | | searchable | boolean | No | true | Determines whether or not a search input should be rendered in the select list | | searchAutofocus | boolean | No | true | Whether or not the search field should get the focus automatically each time the list opens up | | searchInitialValue | string | No | null | Initial search field value | | searchNoResultsLabel | string | No | 'No results for' | No search results message | | searchPlaceholder | string | No | 'Search' | No search results message | | minOptionsToDisplaySearch | number | No | 8 | If Select is searchable search input will render if the number of options is more or equal to this number. | | triggerLabel | string | No | null | Label being shown by default on the trigger button | | outline | default or warning or error | No | 'default' | String specifying the outline that should be rendered | | autoFocus | boolean | No | false | Automatically focus the Select's trigger on mount | | triggerId | string | No | null | HTML id attribute asssigned to trigger component | | creatableOptions | CreatableOptionsProps | No | null | Options used for creatable option feature | | isClearable | boolean | No | false | Defines wether to render the clearing x button | | highlightedText | string | No | null | Highlights this text in the options list only when the Select is not searchable. | | isLoading | boolean | No | false | Display the loading state of the list | | submitOptions | SubmitOptions | No | null | Options used for the submit button | | enableFlowTriggers | boolean | No | false | Allow toggling between original and new trigger | | enableNewSearchInput | boolean | No | false | Allow toggling between original and new search input | | limitTwoRows | boolean | No | true | Limit selected chips to only display two rows. Only applicable to multi-select. | | enableSubLabelSearch | boolean | No | false | Search in subLabel text when optionType is multi-line |

SelectEmptyStateProps

| Prop | Type | Required | Default | Description | | :------ | :------- | :------- | :------ | ------------------------ | | label | string | No | null | Label shown in the empty |

Accessibility ♿️

Select tries to mimic native HTML select a11y features as best as possible.

This is well-known to be a notoriously hard job There are some smaller deviation to native select's behaviour:

  • Keyboard navigation
    • using keyboard letters won't focus on list options

Testing

This example serves as starting point on how you can use Select component as part of your tests.

Some of the props are already defined in the SelectWrapper since they are mandatory but they can be overriden by sending props to the renderSelectWrapper function.

import React, { useState } from 'react';
import { render } from '@testing-library/react';
import { Select, SelectOption } from '@highlight-ui/select';

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
type SelectWrapperProps = Optional<
  SelectProps<SelectOption>,
  'selectedOptions' | 'onSelect' | 'options'
>;

function SelectWrapper(props: SelectWrapperProps) {
  const [selectedOptions, setSelectedOptions] = useState<SelectOption[]>([]);
  return (
    <Select
      selectedOptions={selectedOptions}
      onSelect={setSelectedOptions}
      options={[
        { label: 'Genesis', value: '23095', color: 'red' },
        { label: 'Destinee', value: '26211', color: 'green' },
      ]}
      {...props}
    />
  );
}

describe('SelectTest', () => {
  const renderSelectWrapper = (props: SelectWrapperProps) => {
    return render(<SelectWrapper {...props} />);
  };

  it('test description', () => {
    renderSelectWrapper({});
    // write your expect here
  });
});

Place in design system 💻

Select component's flexibility is used to implement a number of different components:

For picking a color from a color palette, color-picker should be used.

For picking unselectable menu item which will open URL or call custom callback, look at the dropdown-menu

Contributing 🖌️

Please visit personio.design.

If you're interested in contributing, please visit our contribution page.