diff --git a/src/components/timeline/__tests__/timeline-labs.browser.test.tsx b/src/components/timeline/__tests__/timeline-labs.browser.test.tsx index 1860f3470..25e2cf1b4 100644 --- a/src/components/timeline/__tests__/timeline-labs.browser.test.tsx +++ b/src/components/timeline/__tests__/timeline-labs.browser.test.tsx @@ -232,6 +232,54 @@ describe("lab lines on the timeline page", () => { await expect.poll(() => lastTimelineValues(net)).toEqual(["WEIGHT"]); }); + it("keeps a chosen marker that was hidden since, until it is unticked", async () => { + await page.viewport(1280, 900); + window.localStorage.setItem( + VALUES_KEY, + JSON.stringify(["lab:bm-ferritin"]), + ); + setSearch("?zoom=all"); + // Hidden: the server no longer offers it, and still draws the chosen line. + const net = stubFetch() + .on("GET", "/api/timeline", (request) => { + const body = timeline(valuesOf(request.path)); + return { + data: { + ...body, + availableLabs: body.availableLabs.filter( + (m) => m.key !== "lab:bm-ferritin", + ), + }, + }; + }) + .on("GET", "/api/timeline/readiness", { + data: { verdict: "carries", since: "2024-01-01", lanes: [] }, + }) + .on("GET", "/api/life-events", { data: { events: [] } }); + const { screen } = await renderWithApp(, { + user: { + timezone: "Europe/Berlin", + modules: { timeline: true, labs: true }, + }, + }); + await screen.getByRole("button", { name: /Values \(1\)/ }).click(); + const ferritin = screen.getByRole("menuitemcheckbox", { name: "Ferritin" }); + await expect.element(ferritin).toHaveAttribute("aria-checked", "true"); + + await ferritin.click(); + await expect.poll(() => lastTimelineValues(net)).toEqual([]); + // Unticked, it is offered no more. + await expect + .poll( + () => + document.querySelectorAll( + '[data-slot="timeline-values-option"][data-key="lab:bm-ferritin"]', + ).length, + ) + .toBe(0); + expect(JSON.parse(window.localStorage.getItem(VALUES_KEY)!)).toEqual([]); + }); + it("neither offers nor asks for a marker with the labs module off", async () => { await page.viewport(1280, 900); const { net, screen } = await mount(["WEIGHT", "lab:bm-ferritin"], { diff --git a/src/lib/timeline/lab-series.ts b/src/lib/timeline/lab-series.ts index d48838a13..b13d3a17e 100644 --- a/src/lib/timeline/lab-series.ts +++ b/src/lib/timeline/lab-series.ts @@ -280,7 +280,11 @@ export async function loadLabSeries(args: { /** * Every marker the record has a numeric reading for, whatever the window, * by name. A marker with only qualitative readings ("negative") has nothing - * to draw and is not offered. + * to draw and is not offered, and neither is a hidden one: hiding a marker + * takes it out of the labs catalogue and the entry pickers, and this picker + * follows. A hidden marker already chosen keeps its line (`loadLabSeries` + * does not filter), and the menu names it from that line, so it can still + * be unticked, as the entry form keeps a hidden marker already selected. */ export async function loadAvailableLabMarkers(args: { recordId: string; @@ -302,7 +306,7 @@ export async function loadAvailableLabMarkers(args: { .filter((id): id is string => id !== null); if (ids.length === 0) return []; const markers = await prisma.biomarker.findMany({ - where: { userId: recordId, id: { in: ids } }, + where: { userId: recordId, id: { in: ids }, hidden: false }, select: { id: true, name: true, panel: true, unit: true }, orderBy: [{ name: "asc" }, { id: "asc" }], }); diff --git a/tests/integration/timeline-lab-series.test.ts b/tests/integration/timeline-lab-series.test.ts index 5fc0767c6..48a71e6ca 100644 --- a/tests/integration/timeline-lab-series.test.ts +++ b/tests/integration/timeline-lab-series.test.ts @@ -287,6 +287,30 @@ describe("lab lines on the timeline", () => { expect(empty.labSeries[0]).toMatchObject({ points: [], excluded: 0 }); }); + it("does not offer a hidden marker, and keeps drawing one already chosen", async () => { + const owner = await makeUser("owner"); + const { marker } = await seedFerritin(owner.id); + await getPrismaClient().biomarker.update({ + where: { id: marker.id }, + data: { hidden: true }, + }); + await signIn(owner.id); + + // Hidden from the picker, as from the labs catalogue and entry pickers. + const offered = await data(await getTimeline(`zoom=all&values=WEIGHT`)); + expect(offered.availableLabs).toEqual([]); + // A line chosen before it was hidden still draws, and still names + // itself, so the menu can untick it. + const chosen = await data( + await getTimeline(`zoom=all&values=lab:${marker.id}`), + ); + expect(chosen.availableLabs).toEqual([]); + expect(chosen.labSeries.map((line) => [line.key, line.name])).toEqual([ + [`lab:${marker.id}`, "Ferritin"], + ]); + expect(chosen.labSeries[0].points).toHaveLength(3); + }); + it("finds nothing under another record's marker id, and does not refuse", async () => { const owner = await makeUser("owner"); const other = await makeUser("other");