diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-detail.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-detail.png index 52d00495..ffa12f37 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-detail.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-detail.png differ diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-error.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-error.png index f07dbc15..907f118a 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-error.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-error.png differ diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png index 7af374c4..97a77e79 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-filters.png differ diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-list.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-list.png index bf2ebed4..d2e41781 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-list.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-list.png differ diff --git a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-new.png b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-new.png index 0c225e74..13e7ffe3 100644 Binary files a/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-new.png and b/e2e/__screenshots__/work-orders/work-orders.visual.spec.ts/wo-new.png differ diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-head.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-head.tsx index c490f17e..14a446fd 100644 --- a/src/app/(protected)/workorders/_components/list/table/wo-table-head.tsx +++ b/src/app/(protected)/workorders/_components/list/table/wo-table-head.tsx @@ -1,4 +1,5 @@ import { WO_TABLE_COLS } from "./wo-table-cols"; +import { WoTableHeaderLabel } from "./wo-table-header-label"; export function WoTableHead() { return ( @@ -25,7 +26,7 @@ export function WoTableHead() { borderBottom: "1px solid var(--border)", }} > - {col.label} + {Boolean(srLabel) && {srLabel}} ); diff --git a/src/app/(protected)/workorders/_components/list/table/wo-table-header-label.tsx b/src/app/(protected)/workorders/_components/list/table/wo-table-header-label.tsx new file mode 100644 index 00000000..0f4a5fc5 --- /dev/null +++ b/src/app/(protected)/workorders/_components/list/table/wo-table-header-label.tsx @@ -0,0 +1,33 @@ +import { Tooltip } from "@mui/material"; +import { CircleQuestionMark } from "lucide-react"; + +import { getWoTableHeaderTooltip } from "@/domain/work-orders/constants/wo-table-header-tooltips"; + +type WoTableHeaderLabelProps = { + colKey: string; + label: string; +}; + +export function WoTableHeaderLabel({ colKey, label }: WoTableHeaderLabelProps) { + const tooltip = getWoTableHeaderTooltip(colKey); + + if (!tooltip) { + return <>{label}; + } + + return ( + + {label} + + + + + + ); +} diff --git a/src/domain/work-orders/constants/wo-table-header-tooltips.ts b/src/domain/work-orders/constants/wo-table-header-tooltips.ts new file mode 100644 index 00000000..f0848cf9 --- /dev/null +++ b/src/domain/work-orders/constants/wo-table-header-tooltips.ts @@ -0,0 +1,17 @@ +/** + * Help copy for date-column headers on the work orders board table (SH-195). + * Keyed by the `WO_TABLE_COLS` column key; only columns with copy here show a + * header help icon. `appt` (Appt Time) is an intentional open point — do not + * add it without a business-rule decision. + */ +export const WO_TABLE_HEADER_TOOLTIPS = { + sched: "The date the work is planned to happen. Setting it moves the work order to Scheduled.", + due: "The deadline for the work — when it must be completed by.", + completed: "The date the vendor actually finished the work. Required to complete the work order.", +} as const; + +export function getWoTableHeaderTooltip(colKey: string): string | undefined { + return colKey in WO_TABLE_HEADER_TOOLTIPS + ? WO_TABLE_HEADER_TOOLTIPS[colKey as keyof typeof WO_TABLE_HEADER_TOOLTIPS] + : undefined; +} diff --git a/src/test/app/(protected)/workorders/wo-table-head-date-tooltips.test.tsx b/src/test/app/(protected)/workorders/wo-table-head-date-tooltips.test.tsx new file mode 100644 index 00000000..694013bb --- /dev/null +++ b/src/test/app/(protected)/workorders/wo-table-head-date-tooltips.test.tsx @@ -0,0 +1,71 @@ +import { screen, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; + +import { WoTableHead } from "@/app/(protected)/workorders/_components/list/table/wo-table-head"; +import { getWoTableHeaderTooltip } from "@/domain/work-orders/constants/wo-table-header-tooltips"; +import { renderWithProviders } from "@/test/test-utils"; + +const CASES = [ + { + columnName: "SCHEDULE ON", + tooltip: + "The date the work is planned to happen. Setting it moves the work order to Scheduled.", + }, + { + columnName: "DUE DATE", + tooltip: "The deadline for the work — when it must be completed by.", + }, + { + columnName: "COMPLETED DATE", + tooltip: "The date the vendor actually finished the work. Required to complete the work order.", + }, +]; + +function renderHead() { + return renderWithProviders( + + +
, + { withAuth: false }, + ); +} + +describe("WoTableHead date column header tooltips (SH-195)", () => { + it.each(CASES)( + "shows the $columnName help icon and tooltip copy on hover", + async ({ columnName, tooltip }) => { + const user = userEvent.setup(); + renderHead(); + + const header = screen.getByRole("columnheader", { name: new RegExp(`^${columnName}`) }); + const icon = within(header).getByRole("img", { name: `${columnName} column help` }); + + await user.hover(icon); + + const tip = await screen.findByText(tooltip); + expect(tip).toBeInTheDocument(); + }, + 15000, + ); + + it("moves focus to the help icon on tab and keeps it keyboard reachable", async () => { + const user = userEvent.setup(); + renderHead(); + + const header = screen.getByRole("columnheader", { name: /^SCHEDULE ON/ }); + const icon = within(header).getByRole("img", { name: "SCHEDULE ON column help" }); + + await user.tab(); + + expect(icon).toHaveFocus(); + }); + + it("does not add a help icon to Appt Time (open point, not implemented)", () => { + renderHead(); + + const header = screen.getByRole("columnheader", { name: "APPT TIME" }); + expect(within(header).queryByRole("img")).not.toBeInTheDocument(); + expect(getWoTableHeaderTooltip("appt")).toBeUndefined(); + }); +});