fix(ui): let ConfirmButton's dialog wear the destructive style

ConfirmButton took a `destructive` prop and dropped it on the way to its
dialog, so every delete, revoke and remove built on it confirmed with a
neutral primary button while ConfirmDialog confirmed the same kind of act
in red. The prop is forwarded now, and the call sites whose act takes
something away pass it. A guard holds every confirmation whose slot names
a destroying act to the destructive style; resets stay neutral.
This commit is contained in:
Marc-André Bombeck committed 2026-10-21 12:02:13 +02:00
1 parent dc704b5e63
commit 9a8b95cfd0
21 files changed
+181

No files matched your search

@@ -160,6 +160,59 @@ describe("ConfirmBody — the action is the only path to the callback", () => {
});
});
describe("the destructive style reaches the action from both shapes", () => {
// `ConfirmButton` accepted `destructive` through its props type and dropped
// it on the way to the dialog, so every delete confirmation built on it
// offered a neutral primary where `ConfirmDialog` offered a red one.
function actionVariant(element: ReactElement): unknown {
const body = walk(element).find((el) => nameOf(el) === "ConfirmBody")!;
const rendered = walk(
(body.type as (p: unknown) => ReactElement)(body.props),
);
const action = rendered.find((el) => nameOf(el) === "AlertDialogAction")!;
return (action.props as Record<string, unknown>).variant;
}
it("ConfirmButton forwards destructive to the dialog's action", () => {
expect(
actionVariant(
ConfirmButton({
label: "Delete",
onConfirm: vi.fn(),
destructive: true,
...COPY,
}) as ReactElement,
),
).toBe("destructive");
});
it("ConfirmButton stays neutral when the act does not destroy", () => {
expect(
actionVariant(
ConfirmButton({
label: "Reset",
onConfirm: vi.fn(),
...COPY,
}) as ReactElement,
),
).toBe("default");
});
it("ConfirmDialog forwards it the same way", () => {
expect(
actionVariant(
ConfirmDialog({
open: true,
onOpenChange: vi.fn(),
onConfirm: vi.fn(),
destructive: true,
...COPY,
}) as ReactElement,
),
).toBe("destructive");
});
});
describe("<ConfirmDialog> — the trigger-less half", () => {
const onConfirm = vi.fn();
const tree = walk(
+2
View File
@@ -114,6 +114,7 @@ export function ConfirmButton({
ariaLabel,
slot,
extra,
destructive = false,
}: ConfirmCopy & {
/** The control's own label, before the dialog opens. */
label: string;
@@ -156,6 +157,7 @@ export function ConfirmButton({
pending={pending}
slot={slot}
extra={extra}
destructive={destructive}
/>
</AlertDialog>
);