From 0450f8ecd6cf5005e9ea4ce955c3a5f683be6eb2 Mon Sep 17 00:00:00 2001 From: Tudor Date: Fri, 2 Oct 2026 16:43:41 +0100 Subject: [PATCH] fix(search): desktop Clear all keeps the search The desktop toolbar's Clear went to the bare home URL, dropping the search along with the filters, so it landed on the home page instead of the unfiltered results. It now clears every filter and keeps the search, postcode and distance, as the phone's Clear all already did. It is offered only once a filter is applied, since with only a search there is nothing for it to clear, and is labelled Clear all to match the phone. Co-Authored-By: Claude Opus 5.5 --- e2e/tests/journeys.spec.ts | 13 ++++++++ .../components/ResultsToolbar.test.tsx | 23 +++++++++++++- nextjs-app/components/FilterBar.tsx | 30 ++++--------------- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index 15744d8..ae585ee 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -309,6 +309,19 @@ test('school type and gender switch straight to another value', async ({ page }) * School type offers six groups a parent recognises, and Faith sits beside it. * Data-invariant: asserts what every returned school is, never how many. */ +// Clear all once dropped the search with the filters, which sent desktop back +// to the home page instead of to the unfiltered results. +test('Clear all removes the filters and keeps the search', async ({ page }) => { + await page.goto('/?search=school&phase=primary'); + const row = page.getByRole('group', { name: 'Filters', exact: true }); + await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 }); + await row.getByRole('button', { name: 'Clear all', exact: true }).click(); + await expect(page).not.toHaveURL(/[?&]phase=/); + await expect(page).toHaveURL(/[?&]search=school(&|$)/); + await expect(schoolLinks(page).first()).toBeVisible(); + await expect(row.getByRole('button', { name: 'Clear all', exact: true })).toHaveCount(0); +}); + test('school type groups and the faith filter narrow to what they name', async ({ page }) => { await page.goto('/?search=school'); const type = page.getByRole('combobox', { name: 'School type', exact: true }); diff --git a/nextjs-app/__tests__/components/ResultsToolbar.test.tsx b/nextjs-app/__tests__/components/ResultsToolbar.test.tsx index 105b9d7..fcd3d44 100644 --- a/nextjs-app/__tests__/components/ResultsToolbar.test.tsx +++ b/nextjs-app/__tests__/components/ResultsToolbar.test.tsx @@ -1,4 +1,4 @@ -import { act, fireEvent, render, screen } from '@testing-library/react'; +import { act, fireEvent, render, screen, within } from '@testing-library/react'; import { HomeView } from '@/components/HomeView'; import { FilterBar } from '@/components/FilterBar'; import { fetchSchools } from '@/lib/api'; @@ -175,3 +175,24 @@ describe('the results list', () => { expect(screen.queryByText('Search: southmead')).not.toBeInTheDocument(); }); }); + +describe('the desktop Clear all', () => { + const row = () => screen.getByRole('group', { name: 'Filters' }); + + it('removes the filters and keeps the search, rather than going home', () => { + params = new URLSearchParams('postcode=SW196AR&radius=3&phase=primary&school_type=council&local_authority=Wandsworth'); + render(); + fireEvent.click(within(row()).getByRole('button', { name: 'Clear all' })); + const pushed = push.mock.calls.at(-1)![0] as string; + const next = new URLSearchParams(pushed.split('?')[1]); + expect(next.get('postcode')).toBe('SW196AR'); + expect(next.get('radius')).toBe('3'); + for (const key of ['phase', 'school_type', 'local_authority']) expect(next.get(key)).toBeNull(); + }); + + it('is not offered when only a search is applied', () => { + params = new URLSearchParams('search=southmead'); + render(); + expect(within(row()).queryByRole('button', { name: /^Clear/ })).not.toBeInTheDocument(); + }); +}); diff --git a/nextjs-app/components/FilterBar.tsx b/nextjs-app/components/FilterBar.tsx index 8474d4e..3c4d30a 100644 --- a/nextjs-app/components/FilterBar.tsx +++ b/nextjs-app/components/FilterBar.tsx @@ -377,31 +377,13 @@ export function FilterBar({ updateURL({ ...cleared, [key]: value }); }; - // Every filter at once, keeping the search and its distance: what "Clear - // all" means beside the applied filters, where the search is not one of them. + // Every filter at once, keeping the search and its distance. The search is + // what the results answer, not a filter on them: clearing it too used to + // send desktop back to the home page. const handleClearFilterValues = () => { updateURL(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""]))); }; - const handleClearFilters = () => { - setOmniValue(""); - pendingQueryRef.current = ""; - startTransition(() => { - router.push(pathname); - }); - }; - - const hasActiveFilters = - currentSearch || - currentLA || - currentType || - currentFaith || - currentPhase || - currentPostcode || - currentGender || - currentAdmissionsPolicy || - currentHasSixthForm; - /* * A filter's options come from the full lists, not from the results: the * results have already been narrowed by that filter, so scoping to them @@ -844,14 +826,14 @@ export function FilterBar({ /> - {hasActiveFilters && ( + {appliedCount > 0 && ( )} -- 2.54.0