fix(search): keep the map list's count and sort on one line #161

Merged
tudor merged 1 commits from fix/map-pane-header into main 2026-10-01 09:25:17 +00:00
4 changed files with 44 additions and 6 deletions

No files matched your search

+6
View File
@@ -567,6 +567,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);
@@ -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();
});
+21 -2
View File
@@ -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 {
+9 -3
View File
@@ -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}>