feat(03-05): implement EventForm modal (create/edit)

- Bottom sheet on phone, centered 480px dialog on desktop (EventDetailPopover pattern)
- Fields: title, all-day toggle, start/end date/time, recurrence select, location, description
- D-02: calendar picker hidden when 1 writable calendar, shown when >1 (from writable-calendars endpoint)
- D-11: recurrence presets None/Daily/Weekly/Monthly/Yearly only (whole-series)
- Validation: empty title + end-before-start with UI-SPEC error copy
- create mode: POST /api/events/create; edit mode: PATCH /api/events/:uid/edit
- role=dialog aria-modal=true; focus Title on open; Escape/backdrop close
- T-03-15: all values as plain-text JSX children; no dangerouslySetInnerHTML
- D-01: last-used calendar URL persisted in localStorage
- Auto-fix: vi.hoisted() for mock factory variables (D-03-04-hoisting)
This commit is contained in:
Lucas Berger
2026-06-05 18:29:23 -04:00
parent df416a45f1
commit 86cefffe2f
2 changed files with 729 additions and 7 deletions
+14 -7
View File
@@ -23,9 +23,21 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
// ── Module mocks ──────────────────────────────────────────────────────────────
// vi.hoisted() is required for variables used inside vi.mock() factory functions
// to avoid TDZ (temporal dead zone) issues — decision D-03-04-hoisting.
const {
mockSetEventForm,
mockCreateEvent,
mockUpdateEvent,
mockFetchWritableCalendars,
} = vi.hoisted(() => ({
mockSetEventForm: vi.fn(),
mockCreateEvent: vi.fn().mockResolvedValue({ uid: 'new-uid-123' }),
mockUpdateEvent: vi.fn().mockResolvedValue({ uid: 'edit-uid-456' }),
mockFetchWritableCalendars: vi.fn(),
}))
// Mock the calendarStore
const mockSetEventForm = vi.fn()
let mockEventFormOpen = true
let mockEventFormMode: 'create' | 'edit' = 'create'
let mockEventFormUid: string | null = null
@@ -39,11 +51,6 @@ vi.mock('../store/calendarStore.js', () => ({
})),
}))
// Mock write client calls
const mockCreateEvent = vi.fn().mockResolvedValue({ uid: 'new-uid-123' })
const mockUpdateEvent = vi.fn().mockResolvedValue({ uid: 'edit-uid-456' })
const mockFetchWritableCalendars = vi.fn()
vi.mock('../api/client.js', () => ({
createEvent: mockCreateEvent,
updateEvent: mockUpdateEvent,