From fbb288a822deff3ec4efbf3924bb980c0c9621ef Mon Sep 17 00:00:00 2001 From: Alon Tuval Date: Fri, 12 Jun 2026 07:57:26 +0300 Subject: [PATCH] fix(nav): keep holidays/Hebrew-date overlays when changing year MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changeYear/changeRange preserved the `calendars` URL param verbatim, so a year change that fired before the toggle's URL update had committed would drop the overlays (they only stuck on the second toggle, once the URL caught up). Rebuild `calendars` from component state (visibleCalendarIds + the two toggle flags) in both, matching the toggle handlers — the URL is no longer trusted as the source of truth. Adds a regression test. Co-Authored-By: Claude Opus 4.8 --- src/components/LinearCalendar.tsx | 8 +++++++- src/lib/calendar-params.test.ts | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/LinearCalendar.tsx b/src/components/LinearCalendar.tsx index feb7ebe..a83aa7b 100644 --- a/src/components/LinearCalendar.tsx +++ b/src/components/LinearCalendar.tsx @@ -491,6 +491,11 @@ export default function LinearCalendar({ events: initialEventsProp, startDate, e params.delete('start'); params.delete('end'); params.set('year', String(year)); + // Rebuild `calendars` from component state (the source of truth) rather + // than trusting the URL, which may not have committed the most recent + // toggle yet — otherwise the holidays/Hebrew-date overlays get dropped + // on the first year change after enabling them. + applyCalendarsParam(params, { regularIds: visibleCalendarIds, showJewishCalendar, showHebrewDate }); router.push(`/?${params.toString()}`); }; @@ -515,7 +520,8 @@ export default function LinearCalendar({ events: initialEventsProp, startDate, e params.delete('year'); params.set('start', format(newStart, 'yyyy-MM')); params.set('end', format(newEnd, 'yyyy-MM')); - + // Preserve the overlays/visible calendars from state (see changeYear). + applyCalendarsParam(params, { regularIds: visibleCalendarIds, showJewishCalendar, showHebrewDate }); router.push(`/?${params.toString()}`); }; diff --git a/src/lib/calendar-params.test.ts b/src/lib/calendar-params.test.ts index 7161e45..469762a 100644 --- a/src/lib/calendar-params.test.ts +++ b/src/lib/calendar-params.test.ts @@ -91,4 +91,21 @@ describe('applyCalendarsParam', () => { expect(ids).toContain('work'); expect(ids).toContain(JEWISH_CALENDAR_ID); }); + + // Regression: changing the year must keep the overlays even when the URL + // hasn't committed the most recent toggle yet — by rebuilding `calendars` + // from component state, not from the (stale) URL. + it('year navigation rebuilds overlays from state over a stale URL', () => { + const params = new URLSearchParams('calendars=primary&year=2026'); // URL missing overlays + params.set('year', '2027'); + applyCalendarsParam(params, { + regularIds: ['primary'], + showJewishCalendar: true, // state says they ARE on + showHebrewDate: true, + }); + const ids = params.get('calendars')!.split(','); + expect(ids).toContain(JEWISH_CALENDAR_ID); + expect(ids).toContain(HEBREW_DATE_ID); + expect(params.get('year')).toBe('2027'); + }); });