Katkat

Design a Debounced Product Search Box

AmazonFrontend Engineer · Onsite

Last updated

Design and implement an accessible, resilient product search-as-you-type component featuring debounced requests, race-condition handling via request cancellation, client-side caching, and keyboard navigation.

Problem Statement

Design and implement a product search dropdown component (such as an Amazon-style auto-suggest search bar). The component must fetch suggestions from a mock backend API as the user types, render up to 10 ranked results, and maintain high responsiveness under variable network latency.

Requirements

1. Functional Requirements

  • Debounced Fetch: Trigger network requests only after the user stops typing for 300ms.
  • Stale Request Handling: Guard against race conditions where older requests resolve after newer ones. Cancel pending in-flight requests or ignore stale responses.
  • Client-Side Cache: Cache recent query results in memory to avoid duplicate network calls when users backspace or repeat queries.
  • Keyboard Navigation: Support ArrowDown and ArrowUp to navigate suggestions, Enter to select, and Escape to close the dropdown.
  • Accessibility (a11y): Implement ARIA attributes adhering to the WAI-ARIA combobox pattern (role="combobox", aria-autocomplete="list", aria-expanded, and aria-activedescendant).
  • States: Gracefully handle loading, empty results, and network error states.

2. Component Interface

You may implement this in React (hook or component) or modern TypeScript/vanilla JavaScript.

interface Suggestion {
  id: string;
  title: string;
  category: string;
}

interface SearchBoxProps {
  fetchSuggestions: (query: string, signal: AbortSignal) => Promise;
  onSelect: (item: Suggestion) => void;
  debounceDelayMs?: number; // default: 300
  maxResults?: number;      // default: 10
}

Example Workflow

  1. User types "shoe":
    • A timer starts for 300ms.
    • If the user types "s" then "shoes" within 200ms, the previous timer clears.
    • At 300ms after "shoes", an HTTP request is dispatched with an AbortSignal.
    • Dropdown renders up to 10 suggestions with an active loading indicator during fetch.
  2. User presses ArrowDown:
    • Focus visually highlights the first suggestion (index = 0).
    • aria-activedescendant is updated to point to the active item's DOM id.
  3. User presses Enter:
    • onSelect(item) is triggered, the dropdown closes, and the input reflects the selection.

Edge Cases

  • Empty or whitespace-only inputs should clear suggestions and not trigger network requests.
  • Rapid query alterations (e.g., typing "apple", backspacing to "app", re-typing "apple") should retrieve the result from the local cache immediately without making an API call.
  • Handle network failures gracefully without clearing the user's current input text.

Hints

Hint 1

Use an AbortController instance stored across renders to cancel previous pending fetch requests whenever a new request is triggered.

Hint 2

Implement an in-memory Map or LRU cache where the normalized search query string acts as the key to store past results.

Hint 3

Use aria-activedescendant on the input pointing to the id of the currently highlighted list item rather than shifting DOM focus away from the input.

Follow-up questions

  • How would you handle analytics logging for search impressions vs. debounced keystrokes to avoid overwhelming telemetry services?
  • How would you support bolding or highlighting matching sub-strings inside the suggestion titles without introducing XSS vulnerabilities?
  • If the dropdown needs to support categorized groups (e.g., 'Departments', 'Brands', 'Products') with different layouts, how would you refactor your component's state and rendering logic?