diff --git a/client/scripts/size-baseline.json b/client/scripts/size-baseline.json
index 185f4cd1a..688069e45 100644
--- a/client/scripts/size-baseline.json
+++ b/client/scripts/size-baseline.json
@@ -24,7 +24,7 @@
"src/mobile/screens/admin/MAdminPluginsPanel.tsx": 1507,
"src/pages/JourneyDetailPage.test.tsx": 4030,
"src/pages/LoginPage.tsx": 1348,
- "src/pages/TripPlannerPage.tsx": 1179,
+ "src/pages/TripPlannerPage.tsx": 1168,
"src/pages/TripPlannerPage.wiring.test.tsx": 2049,
"src/pages/atlas/useAtlas.ts": 1287,
"src/pages/tripPlanner/useTripPlanner.roadtrip.test.tsx": 3531,
diff --git a/client/src/pages/TripPlannerPage.leftPanelTab.test.tsx b/client/src/pages/TripPlannerPage.leftPanelTab.test.tsx
index 0e7806e9d..b79b9505f 100644
--- a/client/src/pages/TripPlannerPage.leftPanelTab.test.tsx
+++ b/client/src/pages/TripPlannerPage.leftPanelTab.test.tsx
@@ -2,14 +2,14 @@ import { fireEvent, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { render } from '../../tests/helpers/render';
-import { LeftPanelTab } from './TripPlannerPage';
+import { PanelTab } from './TripPlannerPage';
-// FE-PAGE-PLANNER-LEFTTAB-001 to FE-PAGE-PLANNER-LEFTTAB-004
+// FE-PAGE-PLANNER-LEFTTAB-001 to FE-PAGE-PLANNER-LEFTTAB-005
-describe('LeftPanelTab', () => {
+describe('PanelTab', () => {
it('FE-PAGE-PLANNER-LEFTTAB-001: an open panel shows a flap on its edge that collapses it', () => {
const onToggle = vi.fn();
- render();
+ render();
const tab = screen.getByRole('button', { name: 'Collapse' });
expect(tab).toHaveClass('text-content-faint');
expect(tab.style.position).toBe('absolute');
@@ -20,7 +20,7 @@ describe('LeftPanelTab', () => {
});
it('FE-PAGE-PLANNER-LEFTTAB-002: a hidden panel leaves a raised accent tile', () => {
- render();
+ render();
const tab = screen.getByRole('button', { name: 'Plan' });
expect(tab).toHaveClass('bg-accent');
expect(tab.style.position).toBe('fixed');
@@ -28,21 +28,39 @@ describe('LeftPanelTab', () => {
expect(tab.style.borderRadius).toBe('10px');
});
- it('FE-PAGE-PLANNER-LEFTTAB-003: says nothing about its state and has no focus ring by default', () => {
- render();
+ it('FE-PAGE-PLANNER-LEFTTAB-003: an open tab says its panel is open and shows a focus ring', () => {
+ render();
const tab = screen.getByRole('button', { name: 'Collapse' });
- expect(tab).not.toHaveAttribute('aria-expanded');
- expect(tab).not.toHaveClass('focus-visible:outline');
+ expect(tab).toHaveAttribute('aria-expanded', 'true');
+ expect(tab).toHaveClass('focus-visible:outline');
});
- it('FE-PAGE-PLANNER-LEFTTAB-004: an announced tab states whether the panel is open and shows a focus ring', () => {
- const { rerender } = render();
+ it('FE-PAGE-PLANNER-LEFTTAB-004: the tab states whether the panel is open and shows a focus ring', () => {
+ const { rerender } = render();
const tab = screen.getByRole('button', { name: 'Collapse' });
expect(tab).toHaveAttribute('aria-expanded', 'true');
expect(tab).toHaveClass('text-content-faint', 'focus-visible:outline');
- rerender();
+ rerender();
const hiddenTab = screen.getByRole('button', { name: 'Plan' });
expect(hiddenTab).toHaveAttribute('aria-expanded', 'false');
expect(hiddenTab).toHaveClass('bg-accent', 'focus-visible:outline');
});
+
+ it('FE-PAGE-PLANNER-LEFTTAB-005: the right panel tab of the plan view is the same tab, mirrored', () => {
+ const onToggle = vi.fn();
+ const { rerender } = render();
+ const tab = screen.getByRole('button', { name: 'Collapse' });
+ expect(tab).toHaveAttribute('aria-expanded', 'true');
+ expect(tab).toHaveClass('text-content-faint', 'focus-visible:outline');
+ expect(tab.style.left).toBe('-28px');
+ expect(tab.style.borderRadius).toBe('10px 0 0 10px');
+ fireEvent.click(tab);
+ expect(onToggle).toHaveBeenCalledTimes(1);
+ rerender();
+ const hiddenTab = screen.getByRole('button', { name: 'Places' });
+ expect(hiddenTab).toHaveAttribute('aria-expanded', 'false');
+ expect(hiddenTab).toHaveClass('bg-accent', 'focus-visible:outline');
+ expect(hiddenTab.style.position).toBe('fixed');
+ expect(hiddenTab.style.right).toBe('10px');
+ });
});
diff --git a/client/src/pages/TripPlannerPage.test.tsx b/client/src/pages/TripPlannerPage.test.tsx
index 3834e7598..ef467f905 100644
--- a/client/src/pages/TripPlannerPage.test.tsx
+++ b/client/src/pages/TripPlannerPage.test.tsx
@@ -607,6 +607,30 @@ describe('TripPlannerPage', () => {
});
});
+ describe('FE-PAGE-PLANNER-016b: both plan view panel tabs say whether their panel is open', () => {
+ it('gives the left and right tab aria-expanded and a focus ring, and flips it when a panel closes', async () => {
+ vi.useFakeTimers();
+ seedTripStore({ id: 42 });
+ renderPlannerPage(42);
+ act(() => { vi.runAllTimers(); });
+ vi.useRealTimers();
+
+ await waitFor(() => {
+ expect(screen.getAllByRole('button', { name: 'Collapse' })).toHaveLength(2);
+ });
+ for (const tab of screen.getAllByRole('button', { name: 'Collapse' })) {
+ expect(tab).toHaveAttribute('aria-expanded', 'true');
+ expect(tab).toHaveClass('focus-visible:outline');
+ }
+
+ const rightTab = screen.getAllByRole('button', { name: 'Collapse' })[1];
+ fireEvent.click(rightTab);
+ await waitFor(() => expect(rightTab).toHaveAttribute('aria-expanded', 'false'));
+ expect(rightTab).toHaveAccessibleName('Places');
+ expect(rightTab).toHaveClass('focus-visible:outline');
+ });
+ });
+
describe('FE-PAGE-PLANNER-017: Trip navigation error redirects to dashboard', () => {
it('navigates to /dashboard when loadTrip rejects', async () => {
seedStore(useTripStore, {
diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx
index 141eca756..f3688095e 100644
--- a/client/src/pages/TripPlannerPage.tsx
+++ b/client/src/pages/TripPlannerPage.tsx
@@ -300,7 +300,7 @@ function TripPlannerPageDesktop(): React.ReactElement | null {
/>
-
+
- {/* The panel's tab: a flap on its edge while open, a raised accent tile once it is tucked away. */}
-
-
-
+
-
+
void
- /** Also states whether the panel is open and shows a focus ring (the tour planner's tab). */
- announced?: boolean
+ side?: 'left' | 'right'
}
-/** The left panel's tab: a flap on its edge while open, a raised accent tile once it is tucked away. */
-export function LeftPanelTab({ hidden, label, onToggle, announced = false }: LeftPanelTabProps): React.ReactElement {
+/**
+ * A side panel's tab: a flap on its edge while open, a raised accent tile once it is tucked away.
+ * One component for both panels in every view, so each states whether its panel is open and shows a focus ring.
+ */
+export function PanelTab({ hidden, label, onToggle, side = 'left' }: PanelTabProps): React.ReactElement {
const look = hidden ? 'bg-accent text-accent-text shadow-md hover:opacity-90' : 'text-content-faint hover:text-content'
+ const left = side === 'left'
+ const [Open, Close] = left ? [PanelLeftOpen, PanelLeftClose] : [PanelRightOpen, PanelRightClose]
return (
-
+
)