Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c414680fd | ||
|
|
e211e1376d | ||
|
|
74418ca6b9 | ||
|
|
5df8c93420 | ||
|
|
ebf9c12446 |
No files matched your search
@@ -272,6 +272,17 @@ test('searching by postcode returns nearby schools', async ({ page }) => {
|
|||||||
await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 });
|
await expect(schoolLinks(page).first()).toBeVisible({ timeout: 15_000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the phase filter switches straight from secondary to primary', async ({ page }) => {
|
||||||
|
// The phase options once came from the result set, which the phase filter
|
||||||
|
// had already narrowed — so with secondary chosen, primary was not offered.
|
||||||
|
await page.goto('/?search=school&phase=secondary');
|
||||||
|
const phase = page.getByRole('combobox', { name: 'Phase' });
|
||||||
|
await expect(phase).toHaveValue('secondary', { timeout: 15_000 });
|
||||||
|
await phase.selectOption('primary');
|
||||||
|
await expect(page).toHaveURL(/[?&]phase=primary(&|$)/);
|
||||||
|
await expect(phase).toHaveValue('primary');
|
||||||
|
});
|
||||||
|
|
||||||
test('a report-card school shows a Report Card badge in search results, not its old grade', async ({ page }) => {
|
test('a report-card school shows a Report Card badge in search results, not its old grade', async ({ page }) => {
|
||||||
// List/map badges keyed off ofsted_grade (the carried-forward legacy grade)
|
// List/map badges keyed off ofsted_grade (the carried-forward legacy grade)
|
||||||
// and never reached the report-card branch, so report-card schools were
|
// and never reached the report-card branch, so report-card schools were
|
||||||
@@ -567,6 +578,12 @@ test('a desktop postcode search opens on the map with the list beside it', async
|
|||||||
await expect(card.getByRole('button', { name: /Compar/ })).toBeVisible();
|
await expect(card.getByRole('button', { name: /Compar/ })).toBeVisible();
|
||||||
await expect(page.locator('.sc-pin').first()).toBeVisible({ timeout: 15_000 });
|
await expect(page.locator('.sc-pin').first()).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
// The count and the sort share one line at the top of the list.
|
||||||
|
const heading = await pane.getByRole('heading', { level: 2 }).boundingBox();
|
||||||
|
const sort = await pane.getByRole('combobox').boundingBox();
|
||||||
|
expect(Math.abs((heading!.y + heading!.height / 2) - (sort!.y + sort!.height / 2)))
|
||||||
|
.toBeLessThan(8);
|
||||||
|
|
||||||
// The split runs to the bottom of the screen rather than stopping short.
|
// The split runs to the bottom of the screen rather than stopping short.
|
||||||
const split = await page.locator('[class*="mapViewContainer"]').boundingBox();
|
const split = await page.locator('[class*="mapViewContainer"]').boundingBox();
|
||||||
expect(split!.y + split!.height).toBeGreaterThan(800 - 40);
|
expect(split!.y + split!.height).toBeGreaterThan(800 - 40);
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { render, screen, within } from '@testing-library/react';
|
||||||
|
import { FilterBar } from '@/components/FilterBar';
|
||||||
|
|
||||||
|
let searchParams = new URLSearchParams();
|
||||||
|
jest.mock('next/navigation', () => ({
|
||||||
|
useRouter: () => ({ push: jest.fn(), replace: jest.fn(), prefetch: jest.fn() }),
|
||||||
|
usePathname: () => '/',
|
||||||
|
useSearchParams: () => searchParams,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const FILTERS = {
|
||||||
|
local_authorities: [], school_types: [], years: [],
|
||||||
|
phases: ['Primary', 'Secondary', 'All-through'],
|
||||||
|
genders: [], admissions_policies: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The phase options must not come from the result set. The backend scopes its
|
||||||
|
* result filters to the schools it returns, and it applies the phase filter
|
||||||
|
* first — so with "secondary" chosen the scoped list holds only secondary-ish
|
||||||
|
* phases, and switching to primary meant going back to "Any phase" first.
|
||||||
|
*/
|
||||||
|
describe('FilterBar phase options', () => {
|
||||||
|
it('offers every phase while a phase filter narrows the results', () => {
|
||||||
|
searchParams = new URLSearchParams('search=hampton&phase=secondary');
|
||||||
|
render(
|
||||||
|
<FilterBar
|
||||||
|
filters={FILTERS}
|
||||||
|
resultFilters={{
|
||||||
|
local_authorities: [], school_types: [],
|
||||||
|
phases: ['Secondary', 'All-through'],
|
||||||
|
genders: [], admissions_policies: [],
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
const phase = screen.getByRole('combobox', { name: 'Phase' });
|
||||||
|
expect(within(phase).getByRole('option', { name: 'Primary' })).toBeInTheDocument();
|
||||||
|
expect(phase).toHaveValue('secondary');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -94,7 +94,8 @@ it('lists a name search, which has no map', async () => {
|
|||||||
|
|
||||||
it('puts the count and the sort in the list beside the map, once', async () => {
|
it('puts the count and the sort in the list beside the map, once', async () => {
|
||||||
await renderMap();
|
await renderMap();
|
||||||
expect(screen.getAllByRole('heading', { name: /3 schools within 1.0 miles of SW196AR/ })).toHaveLength(1);
|
// Short beside the map, so it shares one line with the sort.
|
||||||
|
expect(screen.getAllByRole('heading', { name: '3 schools within 1 mile' })).toHaveLength(1);
|
||||||
expect(screen.getAllByRole('combobox')).toHaveLength(1);
|
expect(screen.getAllByRole('combobox')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,3 +173,9 @@ it('lets a keyboard pick a pin from the list, with a real button', async () => {
|
|||||||
expect(screen.getByTestId('map')).toHaveAttribute('data-selected', '2');
|
expect(screen.getByTestId('map')).toHaveAttribute('data-selected', '2');
|
||||||
expect(show).toHaveAttribute('aria-pressed', 'true');
|
expect(show).toHaveAttribute('aria-pressed', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps the postcode in the heading in list view, where there is room', async () => {
|
||||||
|
await renderMap();
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'List' }));
|
||||||
|
expect(screen.getByRole('heading', { name: '3 schools within 1.0 miles of SW196AR' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
@@ -295,7 +295,10 @@ export function FilterBar({
|
|||||||
const laOptions =
|
const laOptions =
|
||||||
resultFilters?.local_authorities ?? filters.local_authorities;
|
resultFilters?.local_authorities ?? filters.local_authorities;
|
||||||
const typeOptions = resultFilters?.school_types ?? filters.school_types;
|
const typeOptions = resultFilters?.school_types ?? filters.school_types;
|
||||||
const phaseOptions = resultFilters?.phases ?? filters.phases ?? [];
|
// Phase is the exception: always the full list. The result set has already
|
||||||
|
// been narrowed by the phase filter, so scoping to it would leave only the
|
||||||
|
// chosen phase on offer and switching phase would need "Any phase" first.
|
||||||
|
const phaseOptions = filters.phases ?? [];
|
||||||
const genderOptions = resultFilters?.genders ?? filters.genders ?? [];
|
const genderOptions = resultFilters?.genders ?? filters.genders ?? [];
|
||||||
const admissionsPolicyOptions =
|
const admissionsPolicyOptions =
|
||||||
resultFilters?.admissions_policies ?? filters.admissions_policies ?? [];
|
resultFilters?.admissions_policies ?? filters.admissions_policies ?? [];
|
||||||
|
|||||||
@@ -608,13 +608,32 @@
|
|||||||
border-right: 1px solid var(--border);
|
border-right: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* One line: the count takes what the sort leaves, and truncates rather than
|
||||||
|
wrap if a narrow pane cannot fit both. */
|
||||||
.mapListPane .resultsHeader {
|
.mapListPane .resultsHeader {
|
||||||
gap: 0.5rem;
|
flex-wrap: nowrap;
|
||||||
|
gap: 0.75rem;
|
||||||
padding: 0.875rem 1rem 0.75rem;
|
padding: 0.875rem 1rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapListPane .resultsHeader h2 {
|
.mapListPane .resultsHeader h2 {
|
||||||
font-size: var(--step-1);
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: var(--step-0);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapListPane .resultsHeaderActions {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A select is as wide as its longest option ("Highest Reading, Writing &
|
||||||
|
Maths %", 281px), more than half the pane. Cap it; a long choice truncates. */
|
||||||
|
.mapListPane .sortSelect {
|
||||||
|
max-width: 11rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapContainer {
|
.mapContainer {
|
||||||
|
|||||||
@@ -583,14 +583,20 @@ export function HomeView({ initialSchools, filters, totalSchools, howItWorks, ed
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const schoolCount = `${initialSchools.total.toLocaleString()} school${initialSchools.total !== 1 ? 's' : ''}`;
|
||||||
|
|
||||||
// The count and the sort. Above the list in list view; at the top of the
|
// The count and the sort. Above the list in list view; at the top of the
|
||||||
// list pane, beside the map, in map view.
|
// list pane, beside the map, in map view.
|
||||||
const resultsHeader = (
|
const resultsHeader = (
|
||||||
<div className={styles.resultsHeader}>
|
<div className={styles.resultsHeader}>
|
||||||
<h2 aria-live="polite" aria-atomic="true">
|
<h2 aria-live="polite" aria-atomic="true">
|
||||||
{isLocationSearch && initialSchools.location_info
|
{/* Beside the map the heading shares one line with the sort, so it
|
||||||
? `${initialSchools.total.toLocaleString()} school${initialSchools.total !== 1 ? 's' : ''} within ${(initialSchools.location_info.radius / 1.60934).toFixed(1)} miles of ${initialSchools.location_info.postcode}`
|
drops the postcode, which the search box just above already shows. */}
|
||||||
: `${initialSchools.total.toLocaleString()} school${initialSchools.total !== 1 ? 's' : ''} found`
|
{isMapView && radiusMiles != null
|
||||||
|
? `${schoolCount} within ${radiusMiles} mile${radiusMiles === 1 ? '' : 's'}`
|
||||||
|
: isLocationSearch && initialSchools.location_info
|
||||||
|
? `${schoolCount} within ${(initialSchools.location_info.radius / 1.60934).toFixed(1)} miles of ${initialSchools.location_info.postcode}`
|
||||||
|
: `${schoolCount} found`
|
||||||
}
|
}
|
||||||
</h2>
|
</h2>
|
||||||
<div className={styles.resultsHeaderActions}>
|
<div className={styles.resultsHeaderActions}>
|
||||||
|
|||||||
Reference in new issue
Block a user