Techumber
Home Blog Work

Custom React Dropdown component

Published on January 23, 2022

As simple dropdown menu input.

Closed State

Open State

import * as React from 'react';
import './styles.css';
import CloseIcon from './CloseIcon';
import ArrowIcon from './ArrowIcon';

interface Option {
  id: number;
  text: string;
}

export interface Props {
  options: Option[];
  onSelect?: (opt: Option) => void;
  onClear?: () => void;
}

const Dropdown: React.FC<Props> = React.memo(
  ({ options, onSelect, onClear }) => {
    const [selectedOption, setSelectedOption] = React.useState(null);
    const [hideDropdown, setHideDropdown] = React.useState(false);
    const [filteredOptions, setFilteredOptions] = React.useState([...options]);

    React.useEffect(() => {
      setFilteredOptions(options);
    }, options);

    const handleItemClick = (opt: Option) => {
      setSelectedOption(opt);
      onSelect(opt);
    };

    const handleClear = () => {
      setSelectedOption({ text: '' });
      onClear();
    };

    const handleInputChange = (e) => {
      const val = e.target.value;
      setFilteredOptions(
        options.filter((opt) =>
          opt.text.toLowerCase().includes(val.toLowerCase())
        )
      );
    };

    return (
      <div className="dropdown">
        <div className="input">
          <input
            value={selectedOption?.text}
            type="text"
            onChange={handleInputChange}
            onFocus={() => setHideDropdown(true)}
            onBlur={() => setTimeout(() => setHideDropdown(false), 200)}
          />
          {selectedOption?.text ? (
            <button className="clear-btn" onClick={handleClear}>
              <CloseIcon />
            </button>
          ) : null}

          <div className={!hideDropdown ? 'arrow' : 'arrow-up'}>
            <ArrowIcon />
          </div>
        </div>
        {hideDropdown && (
          <ul className="dropdown-menu">
            {filteredOptions.map((opt) => (
              <li
                className={`menu-item ${
                  selectedOption?.id === opt.id ? 'slected' : ''
                }`}
                key={opt.id}
                onClick={() => handleItemClick(opt)}
              >
                {opt.text}
              </li>
            ))}
          </ul>
        )}
      </div>
    );
  }
);

export default Dropdown;
  1. Render input box and list items.
  2. Filter by searching for a string.
  3. Click on clear to clear previously selected value.

styles.css

@import url(https://fonts.googleapis.com/css?family=Poppins);

body {
  font-family: 'Poppins', serif;
}

:root {
  --color-primary-blue: #00abfa;
  --color-border-grey: #cacbce;
  --color-item-hover-background: #f2f2f3;
  --size-input-border-radius: 12px;
  --size-input-input-height: 52px;
  --size-input-padding-x: 16px;
}

/*This is will work on in webkit browsers*/
::-webkit-scrollbar {
  width: 0px;
}

.dropdown {
  width: 100%;
  max-width: 500px;
  height: 100px;
}

.input {
  width: 100%;
  border: 1px solid var(--color-border-grey);
  border-radius: var(--size-input-border-radius);
  overflow: hidden;
  padding: 0 var(--size-input-padding-x);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  height: var(--size-input-input-height);
}

.input:focus-within {
  border: 1px solid var(--color-primary-blue);
}

.input input {
  border: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  font-size: 18px;
}

.input input:focus {
  border: 0;
  outline: none;
}

.dropdown-menu {
  box-shadow: 0px 0px 4px rgba(24, 25, 27, 0.3),
    0px 1px 6px rgba(24, 25, 27, 0.15);
  border-radius: var(--size-input-border-radius);
  height: 200px;
  overflow-y: scroll;
}

.menu-item {
  font-size: 18px;
  padding: 15px 10px;
}
.menu-item.slected {
  color: var(--color-primary-blue);
}

.menu-item:hover {
  cursor: pointer;
  background-color: var(--color-item-hover-background);
}

.clear-btn {
  cursor: pointer;
  border: 0;
  background: none;
  padding: 0;
  line-height: 0px;
}

.arrow-up {
  rotate: 180deg;
  margin-top: -5px;
}

.hidden {
  display: none;
}

How to use it?

  <Dropdown options={[{id: 'id', text: 'text'}]} onSelect={handleSelect} onClear={handleClear}>