Files
F-r-ldraledigplannerare/frontend/e2e/support/calendarNav.ts
T
jacobh2 ae6db7e16f Redesign the setup flow and consolidate the CSS into one primitives layer
Onboarding asks explicitly for one or two parents and shows one card per
parent for balances; the balance form is per parent with inline
validation; the partner can be filled from the FK deduction or added
from it. Settings and Dashboard reuse the same form.

CSS: bare buttons carry no colour any more; buttons, cards, choice
cards, fields, alerts, badges, eyebrow and modal chrome are defined once
in primitives.css. Media queries live only in responsive.css. A style
guard test blocks hex, !important and stray media queries. Bilingual
fixes: calendar selects size by content, header no longer wraps. Vitest
is capped at four workers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 12:27:29 +02:00

84 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Page } from '@playwright/test'
import { expect } from '@playwright/test'
// Swedish labels from src/i18n/sv/calendar.ts — the suite pins
// `locale: 'sv-SE'` in playwright.config.ts so these are stable.
const MONTH_SELECT_LABEL = 'Välj månad'
const YEAR_SELECT_LABEL = 'Välj år'
/** Navigate the Calendar page to the month containing `date` (UTC). */
export async function goToCalendarMonth(page: Page, date: Date): Promise<void> {
await page.goto('/calendar')
await expect(page.getByTestId('calendar')).toBeVisible()
await page.getByLabel(YEAR_SELECT_LABEL).selectOption(String(date.getUTCFullYear()))
await page.getByLabel(MONTH_SELECT_LABEL).selectOption(String(date.getUTCMonth()))
}
/** The exact `data-testid="calendar-day"` cell for an ISO date, wherever it
* falls in the currently-displayed grid (including a leading/trailing day
* from an adjacent month). */
export function calendarDayCell(page: Page, isoDate: string) {
return page.locator(`[data-testid="calendar-day"][aria-label^="${isoDate}"]`)
}
/**
* Drives a real drag-select across the given ISO dates (in order), the way
* `Calendar.tsx` expects it: `CalendarDay` wires plain `onPointerDown`/
* `onPointerEnter` handlers (see `CalendarWeek.tsx`), and the page only
* treats the gesture as a multi-day drag if `pointerenter` actually fired
* for every intermediate cell before `pointerup` — see the `dates.length
* === 1` branch in `Calendar.tsx`'s `pointerup` handler, which opens the
* single-day modal instead of creating a period.
*
* A single fast `mouse.move` (or Playwright's default `hover()`/`dragTo()`)
* can collapse into one native pointer event and skip the intermediate
* cells entirely, which reads as a click on the anchor date. Moving in
* several steps per cell, with a short pause between cells, is what
* reliably fires a `pointerenter` on each one.
*
* The whole dragged range is scrolled into view ONCE, before the drag
* starts — never mid-drag. A default-viewport (1280×720) run of a 6-row
* month puts the trailing week's cells below the fold (`getBoundingClientRect`
* still reports their real, off-screen `y`); moving the mouse to those
* coordinates without scrolling first sends it past the bottom of the
* actually-rendered viewport, where there is no element to receive
* `pointerenter` at all — the browser just stops delivering the event, and
* the drag's `dragCurrent` freezes on the last on-screen cell it entered
* while still climbing toward the target. Scrolling once up front (via the
* last, typically lowest, target date) keeps every waypoint's precomputed
* center valid for the whole gesture; scrolling again after `mouse.down()`
* would invalidate the coordinates of any cell already computed and risk
* disrupting the in-progress native pointer sequence.
*/
export async function dragSelectDates(page: Page, isoDates: string[]): Promise<void> {
if (isoDates.length < 2) {
throw new Error('dragSelectDates needs at least two dates to actually drag across')
}
const lastDate = isoDates[isoDates.length - 1]
if (lastDate) {
await calendarDayCell(page, lastDate).scrollIntoViewIfNeeded()
}
const centerOf = async (isoDate: string): Promise<{ x: number; y: number }> => {
const box = await calendarDayCell(page, isoDate).boundingBox()
if (!box) throw new Error(`calendar day cell not visible for ${isoDate} — is it in the current month view?`)
return { x: box.x + box.width / 2, y: box.y + box.height / 2 }
}
const [firstDate, ...restDates] = isoDates
if (!firstDate) throw new Error('dragSelectDates needs at least two dates to actually drag across')
const start = await centerOf(firstDate)
await page.mouse.move(start.x, start.y)
await page.mouse.down()
for (const isoDate of restDates) {
const point = await centerOf(isoDate)
await page.mouse.move(point.x, point.y, { steps: 10 })
await page.waitForTimeout(50)
}
await page.mouse.up()
}