Files
familysync/.planning/milestones/v1.0-phases/03-event-write-back-pwa-install/03-12-SUMMARY.md
T
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

8.3 KiB
Raw Blame History

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
03-event-write-back-pwa-install 12 pwa/EventForm
tdd
gap-closure
accessibility
pwa
calendar
requires provides affects
03-05
03-06
WR-03-fix
WR-05-fix
WR-07-fix
IN-03-fix
PWA-01-verified
PWA-02-verified
apps/pwa/src/components/EventForm.tsx
apps/pwa/src/store/calendarStore.ts
added patterns
occurrence?.uid in reset effect deps (reactive re-population)
local-accessor-only date extraction (parseDateTime WR-05)
inline Tab/Shift+Tab focus trap on role=dialog (WR-07)
exported todayIso single source of truth (IN-03)
created modified
apps/pwa/src/components/EventForm.tsx
apps/pwa/src/components/EventForm.test.tsx
apps/pwa/src/store/calendarStore.ts
apps/pwa/vitest.config.ts
TZ=UTC pinned globally in vitest.config.ts env block (not per-file beforeAll) for deterministic date assertions across all tests
Focus trap implemented inline with dialogRef + onKeyDown — no new dependency added
occurrence?.uid (not full occurrence) in reset effect deps to avoid deep-equality churn while still reacting to occurrence arrival
vi.importActual used for IN-03 export test to bypass vi.mock() on calendarStore
duration_minutes completed_date tasks_completed files_modified
40 2026-06-06T00:42:08Z 2 4

Phase 03 Plan 12: EventForm Gap Closure — Edit Mode, Focus Trap, PWA Assets Summary

EventForm edit mode now pre-populates correctly from TanStack cache (even when occurrence arrives after form opens), preserves recurrence presets on edit, uses zone-consistent date extraction, and implements a real Tab/Shift+Tab focus trap. PWA install assets confirmed present.

Tasks Completed

Task Type Description Commit
1 RED test WR-03 blank/recurrence, WR-05 zone, IN-03 export — failing tests 02e312a
1 GREEN feat WR-03 deps fix, WR-05 parseDateTime fix, IN-03 todayIso export f0f1361
2 RED test WR-07 focus trap Tab/Shift+Tab cycle — failing tests 4244e8c
2 GREEN feat WR-07 inline focus trap on dialogRef + onKeyDown e971e16

What Was Built

WR-03: Edit form re-populates when occurrence arrives after open

The reset effect previously depended on [eventFormOpen, eventFormMode, eventFormUid] — not on occurrence. If the form opened before the ['events'] TanStack cache held the occurrence, the form stayed blank forever.

Fix: Added occurrence?.uid to the reset effect dep array. The effect re-runs when the occurrence resolves in the cache, populating title/allDay/start/end/recurrence/location/description.

Recurrence fix (WR-03): The effect previously hard-coded setRecurrence('none'). Now derives occurrence?.recurrence (cast via any since the CalendarOccurrence type doesn't expose it yet in v1). Defaults to 'none' only when absent, with a comment documenting the v1 limitation.

WR-05: Zone-consistent parseDateTime

The old implementation mixed toISOString().slice(0,10) (UTC date) with getHours() (local time) — the UTC date and local time can be in different day-boundaries at the edges.

Fix: Replaced with consistent local-accessor family: getFullYear/getMonth/getDate/getHours/getMinutes. No toISOString() call in the timed branch. The all-day ^\d{4}-\d{2}-\d{2}$ branch is unchanged.

TZ=UTC pinned in vitest.config.ts via env: { TZ: 'UTC' } so WR-05 assertions are deterministic on any CI runner. In UTC environment, a timed occurrence '2026-06-10T23:30:00-04:00' (UTC instant 2026-06-11T03:30:00Z) renders date=2026-06-11 and time=03:30 — both consistent local-accessor values under UTC.

IN-03: todayIso exported from calendarStore

getDefaultStartDate() and getDefaultEndDate() in EventForm.tsx had identical bodies duplicating the todayIso() function already in calendarStore. Exported todayIso from calendarStore (added export keyword) and imported it into EventForm, collapsing both helpers to todayIso() calls.

WR-07: Real focus trap on EventForm dialog

The docblock claimed "Focus trap while open" but the implementation only called .focus() once on open. Tab escaped the modal to background content.

Fix: Added dialogRef and handleDialogKeyDown handler on the dialog div. On Tab/Shift+Tab, queries all focusable elements inside dialogRef.current and wraps focus at the boundaries:

  • Tab on last element → first.focus() + preventDefault()
  • Shift+Tab on first element → last.focus() + preventDefault()

No external library added. Existing focus-on-open (titleRef) and Escape-to-close unchanged. Docblock updated to accurately describe the focus trap.

PWA-01/PWA-02: Install assets confirmed present (IN-04)

All three required PWA install assets exist in apps/pwa/public/:

  • icon-192.png — 192×192 manifest icon
  • icon-512.png — 512×512 manifest icon (+ maskable)
  • apple-touch-icon.png — iOS Add-to-Home-Screen icon

Referenced in index.html and vite.config.ts manifest. No code change needed; confirmed present for Gate 2.

TDD Gate Compliance

Gate Commit Status
Task 1 RED 02e312a test(03-12): failing tests added (3 failed)
Task 1 GREEN f0f1361 feat(03-12): 27 tests passing
Task 2 RED 4244e8c test(03-12): 2 failing focus trap tests
Task 2 GREEN e971e16 feat(03-12): 29 tests passing

Deviations from Plan

Auto-fixed Issues

1. [Rule 2 - Missing] Add todayIso to calendarStore vi.mock() in test file

  • Found during: Task 1 GREEN
  • Issue: EventForm now imports todayIso from calendarStore, but the vi.mock('../store/calendarStore.js') factory in EventForm.test.tsx only exported useCalendarStore. Tests crashed with "No todayIso export is defined on the mock."
  • Fix: Added todayIso: () => new Date().toISOString().slice(0, 10) to the mock factory so the mocked module matches the real module's export surface.
  • Files modified: apps/pwa/src/components/EventForm.test.tsx

2. [Rule 2 - Missing] Use vi.importActual for IN-03 test

  • Found during: Task 1 GREEN
  • Issue: The IN-03 test used await import('../store/calendarStore.js') which returns the mock (not the real module), so actualModule.todayIso was undefined.
  • Fix: Changed to await vi.importActual('../store/calendarStore.js') to bypass the mock and test the real module export.
  • Files modified: apps/pwa/src/components/EventForm.test.tsx

Verification

cd apps/pwa && npx vitest run src/components/EventForm.test.tsx

Result: 29 passed (29)

cd apps/pwa && npm run build

Result: Built successfully — 509.67 kB bundle, PWA service worker generated.

Issues Closed

ID Description Status
WR-03 Edit form blank when occurrence resolves after open CLOSED
WR-03 Editing recurring event resets recurrence to 'none' CLOSED
WR-05 parseDateTime mixes UTC date and local time CLOSED
WR-07 Focus trap claim without real trap implementation CLOSED
IN-03 Duplicate todayIso helpers CLOSED
IN-04 PWA install assets not verified CLOSED (assets confirmed present)

Self-Check: PASSED

Files exist:

  • apps/pwa/src/components/EventForm.tsx — modified
  • apps/pwa/src/components/EventForm.test.tsx — modified
  • apps/pwa/src/store/calendarStore.ts — modified (todayIso exported)
  • apps/pwa/vitest.config.ts — modified (TZ=UTC)
  • apps/pwa/public/icon-192.png
  • apps/pwa/public/icon-512.png
  • apps/pwa/public/apple-touch-icon.png

Commits exist: