feat(02-05): EventDetailPopover read-only popover, XSS-safe, wired into CalendarShell
- EventDetailPopover: reads openEventId from Zustand, resolves occurrence from TanStack Query cache - Dual-mode: standalone (Zustand-driven) + customComponents.eventModal (Schedule-X) - Plain-text JSX children for all event fields (T-02e-01 XSS guard) - Focus trap, Escape to close, backdrop-click to close, aria-label=Close (44px target) - Phone: bottom sheet layout; tablet/desktop: centered popover (max-width 360px) - Phase-3 footer action area reserved with comment - CalendarShell: passes customComponents.eventModal=EventDetailPopover to ScheduleXCalendar - test-setup.ts: import @testing-library/jest-dom for toHaveTextContent matcher - All 36 tests pass, tsc clean
This commit is contained in:
@@ -31,7 +31,9 @@ vi.mock('../store/calendarStore.js', () => ({
|
||||
|
||||
// ── Fixtures ───────────────────────────────────────────────────────────────────
|
||||
|
||||
const TIMED_OCCURRENCE = {
|
||||
import type { CalendarOccurrence } from '../api/client.js'
|
||||
|
||||
const TIMED_OCCURRENCE: CalendarOccurrence = {
|
||||
id: 'test-uid::2026-06-15T10:00:00',
|
||||
uid: 'test-uid',
|
||||
calendarId: 1,
|
||||
@@ -47,7 +49,7 @@ const TIMED_OCCURRENCE = {
|
||||
description: 'Daily team sync meeting',
|
||||
}
|
||||
|
||||
const OCCURRENCE_WITH_HTML = {
|
||||
const OCCURRENCE_WITH_HTML: CalendarOccurrence = {
|
||||
...TIMED_OCCURRENCE,
|
||||
id: 'xss-uid::2026-06-15T10:00:00',
|
||||
uid: 'xss-uid',
|
||||
@@ -56,7 +58,7 @@ const OCCURRENCE_WITH_HTML = {
|
||||
location: '<img src=x onerror=alert(1)>Room',
|
||||
}
|
||||
|
||||
const ALLDAY_OCCURRENCE = {
|
||||
const ALLDAY_OCCURRENCE: CalendarOccurrence = {
|
||||
id: 'allday-uid::2026-06-20',
|
||||
uid: 'allday-uid',
|
||||
calendarId: 2,
|
||||
@@ -81,7 +83,7 @@ import { useCalendarStore } from '../store/calendarStore.js'
|
||||
|
||||
function renderPopover(occurrence = TIMED_OCCURRENCE) {
|
||||
mockOpenEventId = occurrence.id
|
||||
;(useCalendarStore as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
||||
;(useCalendarStore as unknown as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
||||
openEventId: mockOpenEventId,
|
||||
setOpenEventId: mockSetOpenEventId,
|
||||
}))
|
||||
@@ -157,7 +159,7 @@ describe('EventDetailPopover', () => {
|
||||
|
||||
it('renders nothing when openEventId is null', () => {
|
||||
mockOpenEventId = null
|
||||
;(useCalendarStore as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
||||
;(useCalendarStore as unknown as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
||||
openEventId: null,
|
||||
setOpenEventId: mockSetOpenEventId,
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user