Hone logo
Hone
Problems

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:

  1. Component Structure: Create a functional React component named Dropdown.
  2. Props: The component should accept the following props:
    • options: An array of objects, where each object represents a dropdown option and has at least value (unique identifier, string or number) and label (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 the value of 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.
  3. Behavior:
    • When the dropdown is clicked, it should display a list of options.
    • Clicking an option in the list should update the selectedValue (via onSelect) and close the dropdown list.
    • The placeholder should be displayed when selectedValue is not set or doesn't match any option's value.
    • The label should 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.
  4. 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.
  5. TypeScript: All component props and internal state should be typed using TypeScript.

Edge Cases to Consider:

  • Empty Options: What happens if the options array is empty?
  • No Initial Selection: How is the placeholder handled when selectedValue is initially null or undefined?
  • 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 Dropdown component should be a functional component.
  • All state management for the dropdown's open/closed state should be handled within the Dropdown component.
  • The selectedValue and onSelect props 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 useState for 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 document might be useful.
  • The selectedValue prop should determine which option is visually highlighted or displayed as the current selection.
  • The onSelect callback is the mechanism for the parent component to know which option the user has chosen.
Loading editor...
typescript