Custom React Dropdown component
Published on January 23, 2022
As simple dropdown menu input.
Closed State
Open State
Dropdown.tsx
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;
- Render input box and list items.
- Filter by searching for a string.
- 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}>