feat(search): pin the results toolbar and float the map button on phones #157

Merged
tudor merged 2 commits from feat/results-toolbar into main 2026-09-30 19:53:28 +00:00
Owner

Option B from the results-controls mockups (https://claude.ai/artifact/EDhJuBTH9L1iaEtZW7zLrv): make the List/Map switch and the filters on the results page easy to find, and keep them on screen.

What changes

Desktop and tablet

  • Search, filters and the List/Map switch share one card that stays pinned just under the header while you scroll.
  • Distance, phase and school type are now pill controls in that row. They used to be small dropdowns labelled "Within:", "All Phases" and (behind "Advanced") school type.
  • "Advanced" becomes More filters. It now opens only local authority and the secondary-only filters, and its count covers only those.
  • The List/Map switch is larger, filled teal for the view you're on, and marks it with aria-pressed.

Phones (≤640px)

  • Once a search has been made, the form folds to a one-line summary ("SW196AR · within 1 mile · Edit"). Tapping it unfolds the form.
  • The filter pills sit on one row that scrolls sideways, with More filters first.
  • A floating Map / List button sits above the tab bar and replaces the toolbar switch. It rides with the tab bar when iOS Chrome's URL bar hides (--mobile-bar-offset).
  • The selected pin's card now stacks under that button instead of covering the tab bar.

Both

  • Switching view from far down the list scrolls back to the top of the results.
  • New analytics event: results_view_changed with { view, via: toolbar | floating }, so we can see whether map use goes up.
  • The search input now has an accessible name ("School name or postcode"); before, it only had a placeholder.

Tests

  • Unit: new ResultsToolbar.test.tsx (9 tests). FilterBarSuggest now targets the search box by name, because the always-visible School type <select> is also a combobox. 495/495 pass, tsc is clean, and next build succeeds with DATABASE_URL unset.
  • E2E (in this PR):
    • New: the toolbar stays pinned with its switch at 1280px.
    • New: at 360, 390 and 430px, the floating Map button is visible without scrolling and clear of the tab bar, the summary stays pinned after scrolling, there is no horizontal overflow, and the new chrome has 44px tap targets (MOBILE.md).
    • Changed: the iOS fullscreen test at 390px now clicks "Show map", because the toolbar switch is hidden at that width.

Not verified

Nothing here has been checked visually in a browser: per CLAUDE.md there was no local server, and the new journeys only run against staging after merge. Things to look at on staging:

  • The desktop toolbar wrapping between 769 and 1100px.
  • Dark mode. Everything uses tokens, and the floating button inverts to a light pill.
  • The pinned toolbar sitting above Leaflet's controls as the map scrolls under it.

🤖 Generated with Claude Code

Option B from the results-controls mockups (https://claude.ai/artifact/EDhJuBTH9L1iaEtZW7zLrv): make the List/Map switch and the filters on the results page easy to find, and keep them on screen. ## What changes **Desktop and tablet** - Search, filters and the List/Map switch share one card that stays pinned just under the header while you scroll. - Distance, phase and school type are now pill controls in that row. They used to be small dropdowns labelled "Within:", "All Phases" and (behind "Advanced") school type. - "Advanced" becomes **More filters**. It now opens only local authority and the secondary-only filters, and its count covers only those. - The List/Map switch is larger, filled teal for the view you're on, and marks it with `aria-pressed`. **Phones (≤640px)** - Once a search has been made, the form folds to a one-line summary ("SW196AR · within 1 mile · Edit"). Tapping it unfolds the form. - The filter pills sit on one row that scrolls sideways, with **More filters** first. - A floating **Map** / **List** button sits above the tab bar and replaces the toolbar switch. It rides with the tab bar when iOS Chrome's URL bar hides (`--mobile-bar-offset`). - The selected pin's card now stacks under that button instead of covering the tab bar. **Both** - Switching view from far down the list scrolls back to the top of the results. - New analytics event: `results_view_changed` with `{ view, via: toolbar | floating }`, so we can see whether map use goes up. - The search input now has an accessible name ("School name or postcode"); before, it only had a placeholder. ## Tests - Unit: new `ResultsToolbar.test.tsx` (9 tests). `FilterBarSuggest` now targets the search box by name, because the always-visible School type `<select>` is also a combobox. 495/495 pass, `tsc` is clean, and `next build` succeeds with `DATABASE_URL` unset. - E2E (in this PR): - New: the toolbar stays pinned with its switch at 1280px. - New: at 360, 390 and 430px, the floating Map button is visible without scrolling and clear of the tab bar, the summary stays pinned after scrolling, there is no horizontal overflow, and the new chrome has 44px tap targets (MOBILE.md). - Changed: the iOS fullscreen test at 390px now clicks "Show map", because the toolbar switch is hidden at that width. ## Not verified Nothing here has been checked visually in a browser: per CLAUDE.md there was no local server, and the new journeys only run against staging after merge. Things to look at on staging: - The desktop toolbar wrapping between 769 and 1100px. - Dark mode. Everything uses tokens, and the floating button inverts to a light pill. - The pinned toolbar sitting above Leaflet's controls as the map scrolls under it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tudor added 1 commit 2026-09-30 17:35:58 +00:00
feat(search): pin the results toolbar and float the map button on phones
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 10s
PR Checks / Build Backend (no push) (pull_request) Successful in 33s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m19s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m16s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 26s
983a581555
The List/Map switch was a small grey control beside the results heading,
and the filters were plain dropdowns labelled "All Phases" and "Advanced".
Both scrolled away with the first result.

Search, filters and the List/Map switch now share one card pinned under
the header. Distance, phase and school type are pill controls in the row;
"Advanced" becomes "More filters" and counts only what it hides. The
switch is filled brand teal and says which view is on (aria-pressed).

On phones the search folds to a one-line summary once made, the filter
pills scroll sideways, and a floating Map/List button sits above the tab
bar in place of the toolbar switch. The selected pin's card now stacks
under that button instead of covering the tab bar.

Switching view from far down the list scrolls back to the top of the
results, and each switch is tracked as results_view_changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

🤖 AI Code Review (Claude Code)

Moves the results search, filters and List/Map switch into a sticky toolbar. On phones it adds a folded search summary, a sideways-scrolling filter row and a floating List/Map button, and it adds a results_view_changed analytics event. The change is front-end only, with no backend, data or deploy impact, and I found no correctness or security problems that would break production.

🟡 Minor

  • nextjs-app/components/FilterBar.tsx: searchOpen is reset only on form submit. If the query changes by another route (Near me, back/forward navigation, a quick search), the form stays unfolded on phones while a new search is shown. It should probably reset when currentQuery changes.
  • nextjs-app/components/HomeView.module.css: The sticky .resultsToolbar has no max-height on tablet and desktop. With the 'More filters' panel open plus wrapped controls it can grow taller than a short viewport and cover the results. Only the ≤640px block caps the panel, at 50dvh.
  • nextjs-app/components/FilterBar.module.css: The phone mask-image fade on .controlsRow also fades the last visible chip, and focus rings on the edge chips may be clipped by overflow-x: auto. This is cosmetic, but keyboard focus on the trailing chips may be obscured.
## 🤖 AI Code Review (Claude Code) Moves the results search, filters and List/Map switch into a sticky toolbar. On phones it adds a folded search summary, a sideways-scrolling filter row and a floating List/Map button, and it adds a `results_view_changed` analytics event. The change is front-end only, with no backend, data or deploy impact, and I found no correctness or security problems that would break production. ### 🟡 Minor - **nextjs-app/components/FilterBar.tsx**: `searchOpen` is reset only on form submit. If the query changes by another route (Near me, back/forward navigation, a quick search), the form stays unfolded on phones while a new search is shown. It should probably reset when `currentQuery` changes. - **nextjs-app/components/HomeView.module.css**: The sticky `.resultsToolbar` has no max-height on tablet and desktop. With the 'More filters' panel open plus wrapped controls it can grow taller than a short viewport and cover the results. Only the ≤640px block caps the panel, at `50dvh`. - **nextjs-app/components/FilterBar.module.css**: The phone `mask-image` fade on `.controlsRow` also fades the last visible chip, and focus rings on the edge chips may be clipped by `overflow-x: auto`. This is cosmetic, but keyboard focus on the trailing chips may be obscured.
tudor added 1 commit 2026-09-30 19:47:58 +00:00
fix(search): address review on the results toolbar
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m12s
PR Checks / Backend Smoke (pull_request) Successful in 9s
PR Checks / Build Backend (no push) (pull_request) Successful in 33s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m19s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 1m17s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 21s
367a07c15d
- Refold the phone search, and resync its text, when the query changes
  without a submit (back/forward, "Use my location"). The summary and the
  unfolded input could otherwise describe two different searches.
- Cap the "More filters" panel at every width, not only on phones, so an
  open panel scrolls inside the pinned toolbar on a short screen.
- Drop the phone filter row's right-edge fade once it is scrolled to the
  end or does not overflow (the MOBILE.md pattern), and give focus rings
  room inside the scroll clip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

🤖 AI Code Review (Claude Code)

Moves the search, filters and List/Map switch into a sticky results toolbar. On phones the search folds to a one-line summary, the filter chips scroll sideways, and a floating List/Map button replaces the toolbar switch. The mobile bottom sheet moves into a shared dock. The change is front-end only, with unit and E2E tests updated to match, and I found no correctness, security or deploy problems.

✅ No issues found.

## 🤖 AI Code Review (Claude Code) Moves the search, filters and List/Map switch into a sticky results toolbar. On phones the search folds to a one-line summary, the filter chips scroll sideways, and a floating List/Map button replaces the toolbar switch. The mobile bottom sheet moves into a shared dock. The change is front-end only, with unit and E2E tests updated to match, and I found no correctness, security or deploy problems. ✅ No issues found.
tudor merged commit 0e177ca2ec into main 2026-09-30 19:53:28 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tudor/school_compare#157