diff --git a/__tests__/components/mobile/landscape-guard.test.tsx b/__tests__/components/mobile/landscape-guard.test.tsx new file mode 100644 index 0000000..ca3a4d1 --- /dev/null +++ b/__tests__/components/mobile/landscape-guard.test.tsx @@ -0,0 +1,73 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { render, screen, act } from '@testing-library/react' +import { LandscapeGuard } from '@/components/mobile/landscape-guard' + +type Listener = (e: MediaQueryListEvent) => void + +function mockMatchMedia(initialPortrait: boolean) { + let matches = initialPortrait + let listener: Listener | null = null + + const mql = { + get matches() { return matches }, + media: '(orientation: portrait)', + onchange: null, + addEventListener: (_: string, l: Listener) => { listener = l }, + removeEventListener: () => { listener = null }, + addListener: () => {}, + removeListener: () => {}, + dispatchEvent: () => false, + } + + Object.defineProperty(window, 'matchMedia', { + writable: true, + configurable: true, + value: () => mql, + }) + + return { + setPortrait(p: boolean) { + matches = p + if (listener) listener({ matches: p } as MediaQueryListEvent) + }, + } +} + +describe('LandscapeGuard', () => { + beforeEach(() => {}) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('renders children always', () => { + mockMatchMedia(false) + render(
kids
) + expect(screen.getByText('kids')).toBeTruthy() + }) + + it('shows overlay in portrait', () => { + mockMatchMedia(true) + render(
kids
) + expect(screen.getByRole('alert').textContent).toContain('Draai je telefoon naar landscape') + // children blijven in DOM (geen unmount → SSE-streams blijven leven) + expect(screen.getByText('kids')).toBeTruthy() + }) + + it('hides overlay in landscape', () => { + mockMatchMedia(false) + render(
kids
) + expect(screen.queryByRole('alert')).toBeNull() + }) + + it('toggles overlay on orientation change', () => { + const ctl = mockMatchMedia(false) + render(
kids
) + expect(screen.queryByRole('alert')).toBeNull() + act(() => ctl.setPortrait(true)) + expect(screen.getByRole('alert')).toBeTruthy() + act(() => ctl.setPortrait(false)) + expect(screen.queryByRole('alert')).toBeNull() + }) +}) diff --git a/components/mobile/landscape-guard.tsx b/components/mobile/landscape-guard.tsx new file mode 100644 index 0000000..339d67d --- /dev/null +++ b/components/mobile/landscape-guard.tsx @@ -0,0 +1,32 @@ +'use client' + +import { useEffect, useState } from 'react' +import { RotateCw } from 'lucide-react' + +export function LandscapeGuard({ children }: { children: React.ReactNode }) { + const [isPortrait, setIsPortrait] = useState(false) + + useEffect(() => { + const mq = window.matchMedia('(orientation: portrait)') + const update = () => setIsPortrait(mq.matches) + update() + mq.addEventListener('change', update) + return () => mq.removeEventListener('change', update) + }, []) + + return ( + <> + {children} + {isPortrait && ( +
+ +

Draai je telefoon naar landscape

+
+ )} + + ) +}