fix(#98): expose weekend switcher before sessions

This commit is contained in:
2026-07-29 03:52:51 -04:00
parent b884ba8885
commit d6d0558c72
4 changed files with 95 additions and 3 deletions

View File

@@ -47,7 +47,7 @@ export function WeekendSwitcher({ currentMeetingKey, currentSessionKey, onClose
} }
return ( return (
<div className="rh-switcher" data-testid="rh-switcher"> <div id="rh-weekend-switcher" className="rh-switcher" data-testid="rh-switcher">
<div className="rh-switcher-head"> <div className="rh-switcher-head">
<span className="sec-title">Switch Weekend</span> <span className="sec-title">Switch Weekend</span>
<div className="rh-switcher-years"> <div className="rh-switcher-years">

View File

@@ -102,7 +102,16 @@ export function RaceHubPage({ sessionKey }: Props) {
) )
} }
if (preSession && preSessionRef) { if (preSession && preSessionRef) {
return <RaceHubPreSession session={preSessionRef} weekend={preSessionWeekendQuery.data} now={now} /> return (
<RaceHubPreSession
session={preSessionRef}
weekend={preSessionWeekendQuery.data}
now={now}
switcherOpen={switcherOpen}
onToggleSwitcher={() => setSwitcherOpen((open) => !open)}
onCloseSwitcher={() => setSwitcherOpen(false)}
/>
)
} }
if (!selectedSessionKey) { if (!selectedSessionKey) {
return ( return (
@@ -350,7 +359,21 @@ export function RaceHubPage({ sessionKey }: Props) {
) )
} }
function RaceHubPreSession({ session, weekend, now }: { session: ContextSession; weekend?: Weekend; now: number }) { function RaceHubPreSession({
session,
weekend,
now,
switcherOpen,
onToggleSwitcher,
onCloseSwitcher,
}: {
session: ContextSession
weekend?: Weekend
now: number
switcherOpen: boolean
onToggleSwitcher: () => void
onCloseSwitcher: () => void
}) {
const meeting = session.meeting const meeting = session.meeting
const sessions = sortSessionsByStart((weekend?.sessions ?? []).map((entry) => entry.session)) const sessions = sortSessionsByStart((weekend?.sessions ?? []).map((entry) => entry.session))
const target = new Date(session.session.date_start) const target = new Date(session.session.date_start)
@@ -359,6 +382,29 @@ function RaceHubPreSession({ session, weekend, now }: { session: ContextSession;
return ( return (
<div className="rh-page rh-empty" data-testid="race-hub-pre-session" style={{ '--gp-accent': accent } as React.CSSProperties}> <div className="rh-page rh-empty" data-testid="race-hub-pre-session" style={{ '--gp-accent': accent } as React.CSSProperties}>
<div className="rh-topbar">
<span className="rh-topbar-label mono">box-box · race hub</span>
<span className="rh-topbar-spacer" />
<button
type="button"
className={`rh-switcher-toggle${switcherOpen ? ' active' : ''}`}
onClick={onToggleSwitcher}
aria-expanded={switcherOpen}
aria-controls="rh-weekend-switcher"
data-testid="rh-switch-weekend"
>
{switcherOpen ? 'Close' : 'Switch Weekend'}
</button>
</div>
{switcherOpen && (
<WeekendSwitcher
currentMeetingKey={meeting?.meeting_key}
currentSessionKey={session.session.session_key}
onClose={onCloseSwitcher}
/>
)}
<section className="rh-empty-band"> <section className="rh-empty-band">
<span className="rh-empty-eyebrow mono">box-box · race hub</span> <span className="rh-empty-eyebrow mono">box-box · race hub</span>
<h1 className="rh-empty-title">{meeting?.meeting_name ?? 'Next race weekend'}</h1> <h1 className="rh-empty-title">{meeting?.meeting_name ?? 'Next race weekend'}</h1>

View File

@@ -311,6 +311,30 @@ describe('RaceHubPage', () => {
expect(mockFetchRaceHub).not.toHaveBeenCalled() expect(mockFetchRaceHub).not.toHaveBeenCalled()
}) })
it('opens the weekend switcher from pre-session and navigates to the selected explicit session', async () => {
mockFetchWeekendContext.mockResolvedValue({
...analysisContext,
race_hub_default_session: {
session: { ...raceSession, session_key: 9473, session_name: 'Practice 1', session_type: 'Practice', date_start: '2099-05-23T13:00:00Z' },
meeting,
availability,
},
race_hub_pre_session: true,
race_hub_refresh_at: '2099-05-23T13:00:00Z',
})
renderRaceHub(0)
const switchWeekend = await screen.findByTestId('rh-switch-weekend')
expect(switchWeekend).toHaveAttribute('aria-expanded', 'false')
fireEvent.click(switchWeekend)
expect(await screen.findByTestId('rh-switcher')).toBeInTheDocument()
expect(switchWeekend).toHaveAttribute('aria-expanded', 'true')
fireEvent.click(await screen.findByTestId('rh-switcher-session-9471'))
await waitFor(() => expect(window.location.search).toBe('?session_key=9471'))
})
it('shows recovery instead of selecting an empty future session', async () => { it('shows recovery instead of selecting an empty future session', async () => {
mockFetchWeekendContext.mockResolvedValue({ mockFetchWeekendContext.mockResolvedValue({
...analysisContext, ...analysisContext,

View File

@@ -175,6 +175,28 @@ test.describe('Race Hub Weekend Workspace', () => {
expect(raceHubRequests).not.toContain(9473) expect(raceHubRequests).not.toContain(9473)
}) })
test('pre-session state opens the weekend switcher and navigates to an explicit session', async ({ page }) => {
await page.route('**/api/v1/weekend-context', (route) =>
route.fulfill({
contentType: 'application/json',
body: JSON.stringify(pendingContext('2030-01-01T00:00:16Z')),
}),
)
await page.goto('/race-hub')
await expect(page.getByTestId('race-hub-pre-session')).toBeVisible()
const switchWeekend = page.getByTestId('rh-switch-weekend')
await expect(switchWeekend).toHaveAttribute('aria-expanded', 'false')
await switchWeekend.click()
await expect(page.getByTestId('rh-switcher')).toBeVisible()
await expect(switchWeekend).toHaveAttribute('aria-expanded', 'true')
await page.getByTestId(`rh-switcher-session-${FULL_SESSION}`).click()
await expect(page).toHaveURL(new RegExp(`/race-hub\\?session_key=${FULL_SESSION}`))
await expect(page.getByTestId('race-hub')).toBeVisible()
})
test('bare /race-hub recovers when no completed local analysis exists', async ({ page }) => { test('bare /race-hub recovers when no completed local analysis exists', async ({ page }) => {
await page.route('**/api/v1/weekend-context', (route) => await page.route('**/api/v1/weekend-context', (route) =>
route.fulfill({ route.fulfill({