style(labs): format the filter rail files

This commit is contained in:
Marc-André Bombeck committed 2026-11-11 00:22:26 +01:00
1 parent c59f4d7e65
commit ba3ee968c7
11 files changed
+74 -45

No files matched your search

@@ -72,9 +72,9 @@ describe("<LabFilterRail> layout", () => {
.closest('[data-slot="filter-bar"]')
?.querySelector("svg.lucide-list-filter, svg.lucide-funnel");
expect(icon).toBeTruthy();
expect(Math.abs(centerY(icon!) - centerY(dateRange.element()))).toBeLessThan(
2,
);
expect(
Math.abs(centerY(icon!) - centerY(dateRange.element())),
).toBeLessThan(2);
expect(document.documentElement.scrollWidth).toBeLessThanOrEqual(390);
});
@@ -199,7 +199,9 @@ describe("<LabFilterRail> keyboard", () => {
const search = screen.getByRole("searchbox", { name: "Search biomarkers" });
(
screen.getByRole("button", { name: "Open dialog" }).element() as HTMLElement
screen
.getByRole("button", { name: "Open dialog" })
.element() as HTMLElement
).focus();
await userEvent.keyboard("/");
await expect.element(search).toHaveFocus();
@@ -83,14 +83,12 @@ describe("<LabFilterRail>", () => {
);
expect(html).toContain('aria-label="Clear search"');
expect(html).not.toContain('<kbd');
expect(html).not.toContain("<kbd");
});
it("pluralizes marker count with tCount across locales", () => {
const extractCountText = (html: string) => {
const match = html.match(
/<span[^>]*tabular-nums[^>]*>([^<]+)<\/span>/,
);
const match = html.match(/<span[^>]*tabular-nums[^>]*>([^<]+)<\/span>/);
return match ? match[1].trim() : null;
};
@@ -106,7 +104,7 @@ describe("<LabFilterRail>", () => {
onCategoryChange={vi.fn()}
onFromDayChange={vi.fn()}
onToDayChange={vi.fn()}
onResetFilters={vi.fn()}
onResetFilters={vi.fn()}
isFiltered={false}
total={total}
/>
@@ -178,9 +178,7 @@ describe("filterMarkerRows", () => {
accented,
]);
expect(filterMarkerRows([accented], { q: "hämo" })).toEqual([accented]);
expect(filterMarkerRows([accented], { q: "blutbild" })).toEqual([
accented,
]);
expect(filterMarkerRows([accented], { q: "blutbild" })).toEqual([accented]);
expect(filterMarkerRows([accented], { q: "ferritin" })).toEqual([]);
});
@@ -269,12 +267,7 @@ describe("filterMarkerRows", () => {
),
).toEqual([]);
expect(
filterMarkerRows(
[lateRow],
{ toDay: "2026-08-31" },
undefined,
dayKeyNy,
),
filterMarkerRows([lateRow], { toDay: "2026-08-31" }, undefined, dayKeyNy),
).toEqual([lateRow]);
});
});
@@ -319,8 +312,12 @@ describe("labReadingDayKey", () => {
it("keeps noon UTC anchors on their stated date across all timezones", () => {
const noonInstant = "2026-09-01T12:00:00.000Z";
expect(labReadingDayKey(noonInstant, "UTC")).toBe("2026-09-01");
expect(labReadingDayKey(noonInstant, "America/New_York")).toBe("2026-09-01");
expect(labReadingDayKey(noonInstant, "Pacific/Kiritimati")).toBe("2026-09-01");
expect(labReadingDayKey(noonInstant, "America/New_York")).toBe(
"2026-09-01",
);
expect(labReadingDayKey(noonInstant, "Pacific/Kiritimati")).toBe(
"2026-09-01",
);
});
it("documents exact-noon date-anchor ambiguity vs true instant timestamps", () => {
@@ -329,12 +326,16 @@ describe("labReadingDayKey", () => {
// `useLabDate` / `date-only.ts`. In Pacific/Kiritimati (UTC+14), 12:00 UTC would be
// 02:00 on the NEXT calendar day if treated as an instant; but as an anchor it stays 2026-09-01.
const noonAnchor = "2026-09-01T12:00:00.000Z";
expect(labReadingDayKey(noonAnchor, "Pacific/Kiritimati")).toBe("2026-09-01");
expect(labReadingDayKey(noonAnchor, "Pacific/Kiritimati")).toBe(
"2026-09-01",
);
// Any non-noon instant (even 1 second off noon) is recognized as a true instant with time
// and correctly shifts to the local date in the user's timezone:
const nonNoonInstant = "2026-09-01T12:00:01.000Z";
expect(labReadingDayKey(nonNoonInstant, "Pacific/Kiritimati")).toBe("2026-09-02");
expect(labReadingDayKey(nonNoonInstant, "Pacific/Kiritimati")).toBe(
"2026-09-02",
);
expect(labReadingDayKey(nonNoonInstant, "UTC")).toBe("2026-09-01");
});
@@ -3,7 +3,10 @@ import type { QueryClient } from "@tanstack/react-query";
import { describe, expect, it } from "vitest";
import { queryKeys } from "@/lib/query-keys";
import { renderWithApp, testQueryClient } from "../../../../tests/browser/render";
import {
renderWithApp,
testQueryClient,
} from "../../../../tests/browser/render";
import {
currentSearch,
recordHistory,
@@ -210,7 +213,9 @@ describe("LabList stale category and group header handling", () => {
// Group header button shows the filtered count ("1 of 1 outside range") and chip "1"
const groupButton = screen.getByRole("button", { name: /Iron Panel/i });
await expect.element(groupButton).toBeVisible();
await expect.element(groupButton.getByText("1 of 1 outside range")).toBeVisible();
await expect
.element(groupButton.getByText("1 of 1 outside range"))
.toBeVisible();
await expect.element(groupButton.getByText("1")).toBeVisible();
});
@@ -52,7 +52,8 @@ const mockModulePrefs: ModuleListPrefs = {
};
vi.mock("@/lib/module-list-prefs", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/module-list-prefs")>();
const actual =
await importOriginal<typeof import("@/lib/module-list-prefs")>();
return {
...actual,
useModuleListPrefs: () => ({
@@ -35,7 +35,8 @@ vi.mock("@/lib/i18n/context", async (importOriginal) => {
});
vi.mock("@/lib/module-list-prefs", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/module-list-prefs")>();
const actual =
await importOriginal<typeof import("@/lib/module-list-prefs")>();
return {
...actual,
useModuleListPrefs: () => ({
+1 -1
View File
@@ -106,7 +106,7 @@ export function LabFilterRail({
searchInputRef.current?.focus();
}}
aria-label={t("labs.filter.clearSearch")}
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 absolute top-1/2 right-0 inline-flex size-11 -translate-y-1/2 sm:size-10 items-center justify-center rounded-full outline-none focus-visible:ring-[3px]"
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 absolute top-1/2 right-0 inline-flex size-11 -translate-y-1/2 items-center justify-center rounded-full outline-none focus-visible:ring-[3px] sm:size-10"
>
<X className="size-4" aria-hidden="true" />
</button>
+4 -4
View File
@@ -174,7 +174,9 @@ export interface LabGroupSummary {
}
/** Summarise counts and overall status for a list of rows. */
export function summariseGroupRows(rows: readonly MarkerRow[]): LabGroupSummary {
export function summariseGroupRows(
rows: readonly MarkerRow[],
): LabGroupSummary {
const counts = { inRange: 0, below: 0, above: 0, unknown: 0 };
for (const row of rows) {
const s = row.latest.rangeStatus;
@@ -278,9 +280,7 @@ export function LabGroupHeader({
<span className="text-muted-foreground min-w-0 truncate text-xs font-medium tracking-wide uppercase">
{category.label}
</span>
<TagChip className="shrink-0 tabular-nums">
{rows.length}
</TagChip>
<TagChip className="shrink-0 tabular-nums">{rows.length}</TagChip>
<span className="ml-auto shrink-0">
<GroupStatus counts={counts} status={status} />
</span>
+1 -4
View File
@@ -42,10 +42,7 @@ import {
toMarkerRows,
type MarkerRow,
} from "./lab-list-rows";
import type {
LabOverviewCategoryDto,
LabOverviewResponse,
} from "./types";
import type { LabOverviewCategoryDto, LabOverviewResponse } from "./types";
type GroupsOpen = ReturnType<typeof useGroupsOpen>;
@@ -112,10 +112,7 @@ function FilterRail({ syncToUrl = true }: { syncToUrl?: boolean }) {
<button
type="button"
onClick={() =>
patchFilters(
{ type: "PULSE" },
{ mode: "replace", afterPaint: true },
)
patchFilters({ type: "PULSE" }, { mode: "replace", afterPaint: true })
}
>
Defer replace type
@@ -208,7 +205,9 @@ describe("useUrlFilterSync with FilterBar", () => {
const { screen, history } = await mount("?type=WEIGHT");
const entries = window.history.length;
(
screen.getByRole("button", { name: "Defer replace type" }).element() as HTMLButtonElement
screen
.getByRole("button", { name: "Defer replace type" })
.element() as HTMLButtonElement
).click();
// Immediately after trigger, the write is deferred until after paint:
+30 -5
View File
@@ -68,19 +68,44 @@ describe("formatReferenceRange", () => {
describe("priorityReferenceRangeStatus", () => {
it("implements canonical priority: above > below > in-range > unknown with counts", () => {
expect(
priorityReferenceRangeStatus({ above: 1, below: 1, inRange: 1, unknown: 1 }),
priorityReferenceRangeStatus({
above: 1,
below: 1,
inRange: 1,
unknown: 1,
}),
).toBe("above");
expect(
priorityReferenceRangeStatus({ above: 0, below: 2, inRange: 5, unknown: 1 }),
priorityReferenceRangeStatus({
above: 0,
below: 2,
inRange: 5,
unknown: 1,
}),
).toBe("below");
expect(
priorityReferenceRangeStatus({ above: 0, below: 0, inRange: 3, unknown: 2 }),
priorityReferenceRangeStatus({
above: 0,
below: 0,
inRange: 3,
unknown: 2,
}),
).toBe("in-range");
expect(
priorityReferenceRangeStatus({ above: 0, below: 0, inRange: 0, unknown: 4 }),
priorityReferenceRangeStatus({
above: 0,
below: 0,
inRange: 0,
unknown: 4,
}),
).toBe("unknown");
expect(
priorityReferenceRangeStatus({ above: 0, below: 0, inRange: 0, unknown: 0 }),
priorityReferenceRangeStatus({
above: 0,
below: 0,
inRange: 0,
unknown: 0,
}),
).toBe("unknown");
});
});