// The month grid behind the date button. Off-by-one week starts and month // boundaries are how a hand-rolled calendar goes wrong, and none of it shows // until somebody opens the month that breaks. import { load } from "./extract.mjs"; import { suite, eq, report } from "./assert.mjs"; const app = load({ names: ["ymd", "calendarGridStart"] }); const MONDAY = 1, SUNDAY = 0; const cells = (year, monthIdx, start) => { const gs = app.calendarGridStart(new Date(year, monthIdx, 1), start); return Array.from({ length: 42 }, (_, i) => { const d = new Date(gs); d.setDate(gs.getDate() + i); return d; }); }; suite("the grid starts on the locale's first weekday"); for (const [name, start] of [["Monday", MONDAY], ["Sunday", SUNDAY]]) { const everyMonth = Array.from({ length: 12 }, (_, m) => cells(2026, m, start)[0].getDay()); eq(everyMonth.every(day => day === start), true, `${name} start: all twelve months of 2026 open on a ${name}`); } suite("six rows, covering the month exactly once"); { let alwaysSix = true, coversAll = true; for (let m = 0; m < 12; m++) { const cs = cells(2026, m, MONDAY); if (cs.length !== 42) alwaysSix = false; const inMonth = cs.filter(d => d.getMonth() === m).length; if (inMonth !== new Date(2026, m + 1, 0).getDate()) coversAll = false; } eq(alwaysSix, true, "42 cells every month, so the panel never changes height"); eq(coversAll, true, "every day of every month appears exactly once"); } suite("the months that catch people out"); { // 1 Feb 2026 is a Sunday: under Monday weeks that needs six leading days from // January, which is precisely what a naive `1 - getDay()` gets wrong. const feb = cells(2026, 1, MONDAY); eq(app.ymd(feb[0]), "2026-01-26", "Feb 2026 starts Sunday: the grid opens on 26 Jan"); eq(app.ymd(feb[6]), "2026-02-01", "…putting the 1st in the last column of row one"); eq(app.ymd(cells(2026, 1, SUNDAY)[0]), "2026-02-01", "the same month under Sunday weeks needs no leading days at all"); eq(cells(2026, 7, MONDAY).filter(d => d.getMonth() === 7).length, 31, "a 31-day month starting late in the week still fits"); eq(cells(2024, 1, MONDAY).filter(d => d.getMonth() === 1).length, 29, "Feb 2024 shows 29 days"); eq(cells(2026, 1, MONDAY).filter(d => d.getMonth() === 1).length, 28, "Feb 2026 shows 28"); } suite("year boundaries"); { eq(app.ymd(cells(2026, 0, MONDAY)[0]).startsWith("2025"), true, "January's leading cells come from the previous December"); eq(app.ymd(cells(2026, 11, MONDAY)[41]).startsWith("2027"), true, "December's trailing cells run into January"); } export default report("calendar");