Build a Reusable React Dropdown Component in TypeScript
This challenge focuses on creating a flexible and accessible dropdown component using React and TypeScript. A well-implemented dropdown is a fundamental UI element for selecting options from a list, improving user experience by saving space and presenting choices clearly. You will build a component that can be easily integrated into various applications, supporting customizable options and selection logic.
Problem Description
Your task is to create a reusable Dropdown component in React using TypeScript. This component should allow users to select a single item from a predefined list of options.
Key Requirements:
- Component Structure: Create a functional React component named
Dropdown. - Props: The component should accept the following props:
options: An array of objects, where each object represents a dropdown option and has at leastvalue(unique identifier,stringornumber) andlabel(string) properties.selectedValue: The currently selected value. This should be a controlled prop.onSelect: A callback function that is called when an option is selected. It should receive thevalueof the selected option as an argument.placeholder(optional): A string to display when no option is selected.label(optional): A string to be displayed as a visible label for the dropdown itself.
- Behavior:
- When the dropdown is clicked, it should display a list of options.
- Clicking an option in the list should update the
selectedValue(viaonSelect) and close the dropdown list. - The
placeholdershould be displayed whenselectedValueis not set or doesn't match any option's value. - The
labelshould be displayed above or next to the dropdown trigger. - The dropdown should be styled in a basic, functional way. Focus on accessibility for keyboard navigation.
- Accessibility: The dropdown should be keyboard navigable. Users should be able to open/close the dropdown, navigate through options, and select an option using the keyboard.
- TypeScript: All component props and internal state should be typed using TypeScript.
Edge Cases to Consider:
- Empty Options: What happens if the
optionsarray is empty? - No Initial Selection: How is the
placeholderhandled whenselectedValueis initiallynullorundefined? - Disabled Options (Advanced, but good to think about): While not explicitly required, consider how you might handle disabled options in the future.
- Close on Outside Click: The dropdown list should close if the user clicks anywhere outside of the dropdown itself.
Examples
Example 1: Basic Usage
// Parent Component
function App() {
const fruitOptions = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
];
const [selectedFruit, setSelectedFruit] = React.useState<string | null>(null);
return (
<div>
<Dropdown
label="Select a Fruit"
options={fruitOptions}
selectedValue={selectedFruit}
onSelect={setSelectedFruit}
placeholder="Choose a fruit..."
/>
{selectedFruit && <p>You selected: {selectedFruit}</p>}
</div>
);
}
// Rendered Output (Initial State)
// Label: Select a Fruit
// [ "Choose a fruit..." (Clickable button/area) ]
// Rendered Output (After selecting 'Banana')
// Label: Select a Fruit
// [ "Banana" (Clickable button/area) ]
// You selected: banana
Example 2: Handling Different Values
// Parent Component
function App() {
const numberOptions = [
{ value: 10, label: 'Ten' },
{ value: 25, label: 'Twenty-Five' },
{ value: 50, label: 'Fifty' },
];
const [selectedNumber, setSelectedNumber] = React.useState<number | null>(25);
return (
<div>
<Dropdown
options={numberOptions}
selectedValue={selectedNumber}
onSelect={setSelectedNumber}
placeholder="Select a number"
/>
{selectedNumber !== null && <p>Current number: {selectedNumber}</p>}
</div>
);
}
// Rendered Output (Initial State)
// [ "Twenty-Five" (Clickable button/area) ]
// Current number: 25
Example 3: Empty Options
// Parent Component
function App() {
const emptyOptions: Array<{ value: string; label: string }> = [];
const [selectedValue, setSelectedValue] = React.useState<string | null>(null);
return (
<div>
<Dropdown
options={emptyOptions}
selectedValue={selectedValue}
onSelect={setSelectedValue}
placeholder="No options available"
/>
</div>
);
}
// Rendered Output
// [ "No options available" (Clickable button/area, likely non-functional or displays a message) ]
Constraints
- Your solution must be implemented in React and TypeScript.
- The
Dropdowncomponent should be a functional component. - All state management for the dropdown's open/closed state should be handled within the
Dropdowncomponent. - The
selectedValueandonSelectprops must be used for controlled selection. - Avoid using external UI libraries (e.g., Material UI, Ant Design) for the core dropdown functionality. Basic CSS for styling is permitted.
- The solution should aim for good performance, especially with a moderate number of options (up to 100).
Notes
- Consider using
useStatefor managing the open/closed state of the dropdown list. - For accessibility, use appropriate ARIA attributes and semantic HTML elements.
- Think about how to handle closing the dropdown when the user clicks outside of it. Event listeners on the
documentmight be useful. - The
selectedValueprop should determine which option is visually highlighted or displayed as the current selection. - The
onSelectcallback is the mechanism for the parent component to know which option the user has chosen.