diff --git a/client/src/components/PDF/TripPDF.tsx b/client/src/components/PDF/TripPDF.tsx index 83608b1ea..a4691f793 100644 --- a/client/src/components/PDF/TripPDF.tsx +++ b/client/src/components/PDF/TripPDF.tsx @@ -8,6 +8,7 @@ import type { Trip, Day, Place, Category, AssignmentsMap, DayNote, DistanceUnit, import { isDayInAccommodationRange, getDayOrder } from '../../utils/dayOrder' import { hidesOnMiddleDay, getTransportForDay, getMergedItems, getSpanPhase, getDisplayTimeForDay } from '../../utils/dayMerge' import { safeHexColor } from '../../utils/safeColor' +import { pdfSwitchOn, rememberPdfSwitch } from '../../utils/pdfSwitch' import { renderIconMarkup } from '../../utils/iconMarkup' import { formatMoney, formatMoneySum, formatClockTime, splitReservationDateTime, type MoneyEntry } from '../../utils/formatters' import { useSettingsStore } from '../../store/settingsStore' @@ -38,23 +39,6 @@ const PAGE_BREAK_KEY = 'trek_pdf_page_break_per_day' */ const TRANSPORT_NOTES_KEY = 'trek_pdf_transport_notes' -/** A preview switch that is on unless it was turned off; both default to on. */ -function pdfSwitchOn(key: string): boolean { - try { - return localStorage.getItem(key) !== '0' - } catch { - return true - } -} - -function rememberPdfSwitch(key: string, on: boolean): void { - try { - localStorage.setItem(key, on ? '1' : '0') - } catch { - // A blocked localStorage costs the preference, not the export. - } -} - /* The overlay is imperative DOM, so the switch from Settings/ToggleSwitch is rebuilt here rather than rendered — as static markup it would carry no behaviour. Same geometry and the same tokens, so it reads as the same control. */ diff --git a/client/src/utils/pdfSwitch.test.ts b/client/src/utils/pdfSwitch.test.ts new file mode 100644 index 000000000..09707c99e --- /dev/null +++ b/client/src/utils/pdfSwitch.test.ts @@ -0,0 +1,33 @@ +// FE-UTIL-PDFSWITCH-001 to FE-UTIL-PDFSWITCH-002 +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { pdfSwitchOn, rememberPdfSwitch } from './pdfSwitch'; + +const KEY = 'trek_pdf_test_switch'; + +afterEach(() => { + vi.restoreAllMocks(); + localStorage.removeItem(KEY); +}); + +describe('pdfSwitch', () => { + it('FE-UTIL-PDFSWITCH-001: a switch is on until it is turned off, and remembers either way', () => { + expect(pdfSwitchOn(KEY)).toBe(true); + rememberPdfSwitch(KEY, false); + expect(localStorage.getItem(KEY)).toBe('0'); + expect(pdfSwitchOn(KEY)).toBe(false); + rememberPdfSwitch(KEY, true); + expect(localStorage.getItem(KEY)).toBe('1'); + expect(pdfSwitchOn(KEY)).toBe(true); + }); + + it('FE-UTIL-PDFSWITCH-002: a blocked localStorage reads as on and drops the write without throwing', () => { + vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { + throw new Error('blocked'); + }); + vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { + throw new Error('blocked'); + }); + expect(pdfSwitchOn(KEY)).toBe(true); + expect(() => rememberPdfSwitch(KEY, false)).not.toThrow(); + }); +}); diff --git a/client/src/utils/pdfSwitch.ts b/client/src/utils/pdfSwitch.ts new file mode 100644 index 000000000..2954de87c --- /dev/null +++ b/client/src/utils/pdfSwitch.ts @@ -0,0 +1,21 @@ +/* + * The two switches of the trip PDF preview (a new page for every day, and the notes + * of the transports), remembered in this browser. + */ + +/** A preview switch that is on unless it was turned off; both default to on. */ +export function pdfSwitchOn(key: string): boolean { + try { + return localStorage.getItem(key) !== '0'; + } catch { + return true; + } +} + +export function rememberPdfSwitch(key: string, on: boolean): void { + try { + localStorage.setItem(key, on ? '1' : '0'); + } catch { + // A blocked localStorage costs the preference, not the export. + } +}