feat(search): show the list view's rows beside the map
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 17s
PR Checks / Build Frontend (no push) (pull_request) Successful in 1m20s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 20s

The list beside the map used its own cut-down card, so it showed less
than the list view (no phase, age range, denomination, trend, local
authority) and looked different. It now renders the list view's
SchoolRow / SecondarySchoolRow, through one renderRow shared by the list
view, the map pane and the phone's bottom sheet, which also gains
Remove from compare. CompactSchoolItem and its styles are gone.

The rows switched to their stacked layout on a max-width: 640px media
query, which never fires in a ~430px pane on a desktop screen. It is now
a container query on the list the row sits in (`results`, set on the
list view, the map pane and the bottom sheet). 600px of list is a 632px
screen less padding, so phones lay out as before.

Clicking a row, outside its links and buttons, still picks its pin, and
the selected row is outlined. The pane widens to 360–460px for the
taller rows.

The bottom sheet's close button was a 30px target; it is now 44px with
the same 30px circle (MOBILE.md), and the phone journey checks it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5.5 committed 2026-10-01 09:03:50 +01:00
1 parent 37bbda1da1
commit dff3e210ab
6 files changed
+118 -272

No files matched your search

@@ -104,11 +104,11 @@ it('selects the pin from the card, and the card from the pin', async () => {
fireEvent.click(within(card(1)).getByText(/pupils/));
expect(screen.getByTestId('map')).toHaveAttribute('data-selected', '1');
expect(card(1).className).toMatch(/compactItemSelected/);
expect(card(1).className).toMatch(/mapRowSelected/);
fireEvent.click(screen.getByRole('button', { name: 'pin' }));
expect(card(2).className).toMatch(/compactItemSelected/);
expect(card(1).className).not.toMatch(/compactItemSelected/);
expect(card(2).className).toMatch(/mapRowSelected/);
expect(card(1).className).not.toMatch(/mapRowSelected/);
});
it('clicking a card\'s link or button does not also select it', async () => {
@@ -121,8 +121,9 @@ it('clicking a card\'s link or button does not also select it', async () => {
it('shows the England comparison for mainstream schools only, and never a placeholder 0%', async () => {
const { container } = await renderMap();
const card = (urn: number) => container.querySelector(`[data-urn="${urn}"]`) as HTMLElement;
expect(card(2)).toHaveTextContent('52% RWM -10 pts · 269 pupils');
expect(card(3)).toHaveTextContent('62 pupils');
expect(card(2)).toHaveTextContent('52%Reading, Writing & Maths-10 pts vs national');
expect(card(2)).toHaveTextContent('269pupils');
expect(card(3)).toHaveTextContent('62pupils');
expect(card(3)).not.toHaveTextContent(/%|pts/);
});
@@ -152,3 +153,13 @@ it('builds no list cards on a phone, where the pane is hidden', async () => {
// The count stays: it is the pane's heading, shown above the map.
expect(screen.getByRole('heading', { name: /3 schools within/ })).toBeInTheDocument();
});
it('draws the list view\'s own row beside the map, with the same content', async () => {
const { container } = await renderMap();
const beside = (container.querySelector('[data-urn="2"]') as HTMLElement).textContent;
fireEvent.click(screen.getByRole('button', { name: 'List' }));
const row = screen.getByRole('link', { name: 'Southmead Primary School' }).closest('[class~="row"]')!;
expect(row.parentElement?.className).toMatch(/schoolList/);
expect(row.textContent).toBe(beside);
});