Question presented to candidate: "Add recent searches to our article search. When someone submits a query, remember it. When they click into the box, show their last five searches; picking one runs it again. The list should survive a page reload, should not contain duplicates, and should be usable from the keyboard."
Requirements:
- Submitting (Enter or the Search button) runs the search and records the term: trimmed, newest first, case-insensitive de-duplication (a repeat moves to the top), capped at 5.
- Persist the list in
localStorage; survive corrupt or missing data and storage that throws. - Clicking the input opens a "Recent searches" dropdown, filtered by what is typed; each row re-runs its search and has a remove (ร) button; there is a "Clear all".
- Keyboard: Down arrow moves into the list, Up / Down move between rows, Delete removes the focused row, Escape closes and returns to the input.
- Close on a press outside or when focus leaves the widget; follow changes made in another tab.
What a strong answer should cover:
- Pure helper functions (
addToHistory,parseHistory) separate from the UI, so dedupe and cap logic are testable on their own. - Defensive
localStorageaccess:try/catcharound reads and writes, validating the parsed JSON shape. - One
commit()path that updates state and storage together, instead of an effect that writes storage on every render. - Focus management: where focus goes after a row is removed, and why real focus moving between buttons is used here instead of
aria-activedescendant. - Outside-press and focus-leave closing, and the
storageevent for cross-tab sync.
Clarifying questions expected:
- "Is history per device (localStorage) or per account (server)? Should it sync across tabs?"
- "How many entries, and should 'React' and 'react' count as the same search?"
- "Should the dropdown open on focus, on click, or only when the user presses Down?"
Code / implementation expected: Yes. A working component with persisted, de-duplicated, capped history and keyboard support.