Reflow tablet portrait layouts: Plan week 4 + 3, Settings one column
CI / ci (push) Successful in 8m7s
CI / ci (pull_request) Successful in 8m8s

At 768-1199 px held upright the shell marks itself portrait (new
useOrientation hook over the existing tablet breakpoint) and trims its side
padding; the Plan week lays out four days over three instead of scrolling
sideways, and Settings (mock 2e) stacks its two columns into one. Vitest
covers every primary route at 820x1180 and 1180x820.

Task: 529c743e-4464-479b-b750-7ec41a0c9772

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 23:10:16 -04:00
co-authored by Claude Opus 5.5
parent 25edb6fb26
commit b2a4e66675
9 changed files with 297 additions and 13 deletions
+14 -7
View File
@@ -3,16 +3,22 @@ import { act } from '@testing-library/react'
type Listener = (event: MediaQueryListEvent) => void
// Replaces window.matchMedia with one that answers min-width / max-width
// queries for a pretend viewport width, and prefers-reduced-motion: reduce
// with reducedMotion (jsdom has no matchMedia and no layout). Returns a
// function that resizes the pretend viewport and notifies every query whose
// answer changed.
export function mockViewport(initialWidth: number, { reducedMotion = false } = {}): (width: number) => void {
// queries for a pretend viewport width, orientation: portrait for its height
// (a viewport given no height is landscape), and prefers-reduced-motion:
// reduce with reducedMotion (jsdom has no matchMedia and no layout). Returns
// a function that resizes the pretend viewport and notifies every query
// whose answer changed.
export function mockViewport(
initialWidth: number,
{ reducedMotion = false, height: initialHeight = 0 } = {},
): (width: number, height?: number) => void {
let width = initialWidth
let height = initialHeight
const lists: { list: MediaQueryList; listeners: Set<Listener> }[] = []
function evaluate(query: string): boolean {
if (query === '(prefers-reduced-motion: reduce)') return reducedMotion
if (query === '(orientation: portrait)') return height >= width
const min = /\(min-width:\s*(\d+)px\)/.exec(query)
const max = /\(max-width:\s*(\d+)px\)/.exec(query)
if (!min && !max) throw new Error(`mockViewport cannot evaluate ${JSON.stringify(query)}`)
@@ -37,9 +43,10 @@ export function mockViewport(initialWidth: number, { reducedMotion = false } = {
return list
}
return (next: number) => {
return (nextWidth: number, nextHeight = 0) => {
const before = lists.map(({ list }) => list.matches)
width = next
width = nextWidth
height = nextHeight
act(() => {
lists.forEach(({ list, listeners }, i) => {
if (list.matches === before[i]) return