React Select collapsible group

A simple hack to implement collapsible group in react-select

React Select

If you are working on ReactJS application and you ever need to render select / dropdown component then react-select is one of the best library / component which you can use.

One of the reason I like react-select is that it is highly extensible. It comes with lots of options / props which you can use to alter the default behavior.

How to use

Add and install react-select dependency in your react application.

npm install react-select --save

Import and use Select component from react-select

import React from "react";
import Select from 'react-select';

const options = [
  { value: "blue", label: "Blue" },
  { value: "yellow", label: "Yellow" }
];

export default () => (
  <div className="container">
    <Select
      options={options}
      isMulti
      menuIsOpen
    />
  </div>
);

Grouped Options

Now to render options as a group you just have to change the structure of options array.

const colourOptions = [
  { value: "blue", label: "Blue", color: "#0052CC" },
  { value: "yellow", label: "Yellow", color: "#FFC400" }
];

const flavourOptions = [
  { value: "vanilla", label: "Vanilla", rating: "safe" },
  { value: "chocolate", label: "Chocolate", rating: "good" }
];

const groupedOptions = [
  {
    label: "Colours",
    options: colourOptions
  },
  {
    label: "Flavours",
    options: flavourOptions
  }
];

Now you can pass groupedOptions as <Select options={groupedOptions}/> after this change the options should look like this.

grouped options

Collapsible grouped options

Now I had a requirement in one of my project where I have to collapse options under a group when it’s header is clicked.

I looked for props in react-select using which we can achieve it, but as of now there is not such props or options to do it. So I used below hack to make grouped options collapsible.

react-select internally use GroupHeading component to render option group header and it provides a way using which you can replace this internal component with your own component

read more about it here.

import React from "react";
import Select, { components } from "react-select";

// handle options group header click event
// hide and show the options under clicked group
const handleHeaderClick = id => {
  const node = document.querySelector(`#${id}`).parentElement
    .nextElementSibling;
  const classes = node.classList;
  if (classes.contains("collapsed")) {
    node.classList.remove("collapsed");
  } else {
    node.classList.add("collapsed");
  }
};

// Create custom GroupHeading component, which will wrap
// react-select GroupHeading component inside a div and
// register onClick event on that div
const CustomGroupHeading = props => {
  return (
    <div
      className="group-heading-wrapper"
      onClick={() => handleHeaderClick(props.id)}
    >
      <components.GroupHeading {...props} />
    </div>
  );
};

// You can tell react-select to use your custom component by passing
// `components` props
export default () => (
  <div className="container">
    <Select
      options={groupedOptions}
      isMulti
      menuIsOpen
      components={{ GroupHeading: CustomGroupHeading }}
    />
  </div>
);

This blog is open-source on Github.