Merge branch 'dev' into fix/ab/sh-320-inline-sh-prefix-wo-number
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 185 KiB |
|
|
@ -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}
|
||||
<WoTableHeaderLabel colKey={col.key} label={col.label} />
|
||||
{Boolean(srLabel) && <span className="sr-only">{srLabel}</span>}
|
||||
</th>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
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 (
|
||||
<span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
|
||||
{label}
|
||||
<Tooltip title={tooltip} arrow placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={`${label} column help`}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
cursor: "help",
|
||||
color: "var(--color-text-muted)",
|
||||
padding: 0,
|
||||
border: 0,
|
||||
background: "transparent",
|
||||
}}
|
||||
>
|
||||
<CircleQuestionMark size={13} aria-hidden="true" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
17
src/domain/work-orders/constants/wo-table-header-tooltips.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Help copy for date-column headers on the work orders board table.
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -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(
|
||||
<table>
|
||||
<WoTableHead />
|
||||
</table>,
|
||||
{ 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("button", { 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("button", { 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();
|
||||
});
|
||||
});
|
||||