function assertEqual(actual: T, expected: T, message: string): void { if (actual !== expected) throw new Error(`${message}: expected ${String(expected)}, got ${String(actual)}`); } import { renderToStaticMarkup } from "react-dom/server"; import Button from "../src/components/Button"; import { DataGridEmptyAction, DataGridRowActions } from "../src/components/table/DataGrid"; import TableActionGroup, { runTableAction } from "../src/components/table/TableActionGroup"; function noop() {} function buttonCount(markup: string): number { return markup.match(/]*\btype="button"/g)?.length ?? 0; } const rowActions = renderToStaticMarkup(
); assertEqual(buttonCount(rowActions), 4, "row actions render the expected controls"); assertEqual(nonSubmittingButtonCount(rowActions), 4, "row actions do not submit an enclosing form"); const emptyAction = renderToStaticMarkup(
); assertEqual(buttonCount(emptyAction), 1, "empty action renders the expected control"); assertEqual(nonSubmittingButtonCount(emptyAction), 1, "empty action does not submit an enclosing form"); assertEqual((emptyAction.match(/table-action-placeholder/g) ?? []).length, 3, "empty actions reserve the remaining ordinary row positions"); const contextActions = renderToStaticMarkup( I, onClick: noop }, { id: "edit", label: "Edit", icon: E, applicable: false, onClick: noop }, { id: "remove", label: "Remove", icon: R, disabledReason: "Permission denied", onClick: noop } ]} minimumSlots={4} /> ); assertEqual(buttonCount(contextActions), 3, "row-level unavailable actions remain visible"); assertEqual(nonSubmittingButtonCount(contextActions), 3, "table action groups do not submit an enclosing form"); assertEqual((contextActions.match(/disabled=""/g) ?? []).length, 2, "unavailable actions are disabled"); assertEqual((contextActions.match(/table-action-placeholder/g) ?? []).length, 1, "minimum action slots reserve trailing positions"); assertEqual(contextActions.includes("disabled-action-tooltip"), true, "disabled table actions can explain their unavailable state"); assertEqual(contextActions.includes('aria-label="Inspect"'), true, "table actions expose an accessible label"); assertEqual(contextActions.includes('title="Inspect"'), true, "table actions expose a native tooltip"); assertEqual(contextActions.includes('aria-hidden="true"'), true, "table action icons stay decorative"); let propagationStopped = false; let actionCalled = false; runTableAction( { stopPropagation: () => { propagationStopped = true; } }, () => { actionCalled = true; } ); assertEqual(propagationStopped, true, "table actions do not trigger clickable row handlers"); assertEqual(actionCalled, true, "table actions still invoke their handler"); const noReorderActions = renderToStaticMarkup( ); assertEqual(buttonCount(noReorderActions), 2, "row actions omit reorder controls when ordering does not apply"); const unavailableReorderActions = renderToStaticMarkup( ); assertEqual(buttonCount(unavailableReorderActions), 4, "ordered rows retain unavailable boundary controls"); assertEqual((unavailableReorderActions.match(/disabled=""/g) ?? []).length, 2, "unavailable boundary controls are disabled"); const reasonedButton = renderToStaticMarkup(); assertEqual(reasonedButton.includes("disabled=\"\""), true, "a disabled reason disables the central button"); assertEqual(reasonedButton.includes("disabled-action-tooltip"), true, "a disabled reason remains keyboard discoverable"); assertEqual(reasonedButton.includes("disabledReason"), false, "the central-only disabled reason is not passed to the DOM");