Skip to the solution
EasyMachine Coding13 min read

Build a Dropdown Menu

299 views

The problem

Question presented to candidate: "Build an Actions dropdown: a button that opens a short list of commands like Edit, Duplicate, Share and Delete. It should work with a mouse and with the keyboard alone, close when you click elsewhere or press Escape, and tell the page which item was picked."

Requirements:

  • A trigger button toggles a menu of items; choosing an item reports it to the parent and closes the menu.
  • Opening moves focus to the first item; ArrowDown and ArrowUp move between items and wrap; Home and End jump to the ends; ArrowUp on the closed trigger opens on the last item.
  • Escape closes and returns focus to the trigger; Tab closes and lets focus continue; a pointer press outside the widget closes it.
  • Correct ARIA: aria-haspopup, aria-expanded and aria-controls on the trigger, role="menu" and role="menuitem" on the list.

What a strong answer should cover:

  • A tiny state model: open plus the index of the active item, with DOM focus moved after render, not during it.
  • Roving tabindex: only the active item has tabindex="0", the rest -1, so Tab leaves the menu in one step.
  • Outside-click detection with contains() on a listener that exists only while the menu is open, and its cleanup.
  • Why focus must return to the trigger on Escape and on selection, and why Tab should not be trapped in a menu.
  • The ARIA menu-button pattern, and when a plain disclosure list is the better choice (site navigation).

Clarifying questions expected:

  • "Are the items commands (a menu) or links to pages (navigation)? That changes the roles."
  • "Can the list be long enough to need scrolling or type-ahead search?"
  • "Does it need to flip upward near the bottom of the viewport?"

Code / implementation expected: Yes. A working component with keyboard support; about 100 lines in any framework.

  • React
  • Menu
  • Accessibility

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

13 min read Β· 9 sections

React solution

Every file of the working solution. Open it in the playground to run and change it.

ReactComplete solution
import { useState } from "react";
import Dropdown from "./src/components/Dropdown";

const ITEMS = [
  { id: "edit", label: "Edit" },
  { id: "duplicate", label: "Duplicate" },
  { id: "share", label: "Share" },
  { id: "delete", label: "Delete" },
];

export default function App() {
  const [last, setLast] = useState("none");
  return (
    <main className="page">
      <h1>Dropdown menu</h1>
      <Dropdown
        label="Actions"
        items={ITEMS}
        onSelect={(item) => setLast(item.label)}
      />
      <p data-testid="status" aria-live="polite">
        Last action: {last}
      </p>
      <p data-testid="outside" className="hint">
        Click anywhere outside the menu to close it.
      </p>
      <button
        type="button"
        data-testid="reset"
        className="reset"
        onClick={() => setLast("none")}
      >
        Reset
      </button>
    </main>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 21 of 107 in Machine coding

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextEasy

Build a Digital Clock

Continue