engineering
Ticket Writing
Write clear, well-scoped engineering tickets with acceptance criteria, technical context, effort estimates, and dependency mapping.
ticketsjiralinearproject-management
Works well with agents
ticket-writing/
search-autocomplete.md
Markdown| 1 | # Implement typeahead autocomplete for product search bar |
| 2 | |
| 3 | **Ticket ID**: SEARCH-289 |
| 4 | **Type**: Feature |
| 5 | **Epic**: SEARCH-200 — Search Experience Improvements |
| 6 | **Priority**: High |
| 7 | **Labels**: frontend, search, ux |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Context |
| 12 | |
| 13 | Product search currently requires users to type a full query and press Enter before seeing results. Analytics show 38% of searches are abandoned after the first attempt — users misspell product names or don't know exact terms. Competitor benchmarking (Algolia, Typesense demos) shows autocomplete reduces search abandonment by 20-30%. |
| 14 | |
| 15 | This ticket adds typeahead autocomplete to the existing search bar, showing suggestions as the user types. The search API already supports prefix matching via the `?prefix=true` parameter (added in SEARCH-245), so this is primarily a frontend change with minor API work for a dedicated suggestions endpoint. |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | ## Acceptance Criteria |
| 20 | |
| 21 | - [ ] Given a user focuses the search bar and types at least 2 characters, when 150ms have elapsed since the last keystroke, then up to 8 autocomplete suggestions appear in a dropdown below the search bar |
| 22 | - [ ] Given suggestions are visible, when the user clicks a suggestion or presses Enter on a highlighted suggestion, then the app navigates to the search results page with that term pre-filled |
| 23 | - [ ] Given suggestions are visible, when the user presses the up/down arrow keys, then the highlight moves between suggestions and the highlighted text is readable at WCAG AA contrast |
| 24 | - [ ] Given the API returns zero suggestions, when the dropdown would render, then no dropdown appears (no empty state or "no results" message at this stage) |
| 25 | - [ ] Given the user clears the search bar or presses Escape, when suggestions are visible, then the dropdown closes |
| 26 | - [ ] Given a slow network (>500ms API response), when the user is typing, then a subtle loading indicator appears in the search bar without blocking input |
| 27 | - [ ] Given the user types a query that was fetched in the last 60 seconds, when suggestions are requested, then the cached result is used without a network call |
| 28 | - [ ] The suggestions endpoint responds in under 100ms at p95 for the production dataset (~120K products) |
| 29 | |
| 30 | --- |
| 31 | |
| 32 | ## Technical Notes |
| 33 | |
| 34 | **Relevant files:** |
| 35 | - `src/components/SearchBar.tsx` — current search bar component |
| 36 | - `src/hooks/useDebounce.ts` — existing debounce hook (150ms default) |
| 37 | - `src/api/routes/search.ts` — search route handler with prefix support |
| 38 | - `src/services/search/index.ts` — Typesense client wrapper |
| 39 | |
| 40 | **Considerations:** |
| 41 | - Use the existing `useDebounce` hook for keystroke debouncing — do not add a new dependency |
| 42 | - The suggestions endpoint should return `{ suggestions: [{ text: string, category: string, resultCount: number }] }` to support future category grouping |
| 43 | - Typesense supports search-as-you-type natively via the `query_by` parameter; avoid building a custom prefix index |
| 44 | - Use `aria-combobox` pattern for the dropdown to meet accessibility requirements (see WAI-ARIA combobox spec) |
| 45 | - Cache suggestions in a `Map<string, Result>` in the hook; no need for Redux or global state |
| 46 | |
| 47 | --- |
| 48 | |
| 49 | ## Effort Estimate |
| 50 | |
| 51 | **Size**: M (3 days) |
| 52 | **Reasoning**: The search bar component exists, the debounce hook exists, and Typesense supports prefix search. The work is: create the `/api/search/suggestions` endpoint (~0.5 day), build the dropdown UI with keyboard navigation and accessibility (~1.5 days), add client-side caching (~0.5 day), and write tests (~0.5 day). No database migration or backend architecture changes. |
| 53 | |
| 54 | --- |
| 55 | |
| 56 | ## Dependencies |
| 57 | |
| 58 | - **Blocked by**: None (SEARCH-245 prefix search support already merged) |
| 59 | - **Blocks**: SEARCH-301 (recent searches in dropdown — needs the dropdown component from this ticket) |
| 60 | - **Related**: SEARCH-290 (search analytics tracking — will add impression events to autocomplete later) |
| 61 | |
| 62 | --- |
| 63 | |
| 64 | ## Out of Scope |
| 65 | |
| 66 | - Recent searches or trending queries in the dropdown (covered by SEARCH-301) |
| 67 | - Search result previews or rich suggestion cards (future enhancement, not in this sprint) |
| 68 | - Personalized suggestions based on user history (requires ML pipeline, separate epic) |
| 69 |