diff --git a/packages/react/src/patterns/F0Dialog/__tests__/F0Dialog.footer-actions.test.tsx b/packages/react/src/patterns/F0Dialog/__tests__/F0Dialog.footer-actions.test.tsx new file mode 100644 index 0000000000..8496c8a566 --- /dev/null +++ b/packages/react/src/patterns/F0Dialog/__tests__/F0Dialog.footer-actions.test.tsx @@ -0,0 +1,58 @@ +import { describe, expect, test, vi } from "vitest" + +import { screen, userEvent, zeroRender as render } from "@/testing/test-utils" + +import { F0Dialog } from "../index" + +/** + * A footer action that LEAVES the dialog should be a link: "Go to post" is a + * place, and a place you cannot cmd-click is a place the reader has to come + * back from. The ones that act on what the dialog is showing stay handlers. + */ +const dialog = (props: Record) => + render( + {}} header={{ title: "A post" }} {...props}> +

the post

+
+ ) + +describe("footer actions", () => { + test("draws a primary action with a route as a real link", () => { + dialog({ primaryAction: { label: "Go to post", href: "/posts/1" } }) + + expect(screen.getByRole("link", { name: "Go to post" })).toHaveAttribute( + "href", + "/posts/1" + ) + }) + + test("draws a secondary action with a route as a real link", () => { + dialog({ + secondaryAction: { label: "Go to community", href: "/communities/2" }, + }) + + expect( + screen.getByRole("link", { name: "Go to community" }) + ).toHaveAttribute("href", "/communities/2") + }) + + test("keeps a handler action a button", async () => { + const onClick = vi.fn() + dialog({ primaryAction: { label: "Mark as read", onClick } }) + + await userEvent.click(screen.getByRole("button", { name: "Mark as read" })) + + expect(onClick).toHaveBeenCalledTimes(1) + }) + + test("reports the press on a link that asked to hear about it", async () => { + const onClick = vi.fn() + dialog({ + primaryAction: { label: "Go to post", href: "/posts/1", onClick }, + }) + + await userEvent.click(screen.getByRole("link", { name: "Go to post" })) + + expect(onClick).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/react/src/patterns/F0Dialog/components/F0DialogFooter.tsx b/packages/react/src/patterns/F0Dialog/components/F0DialogFooter.tsx index 4250b3edfe..63d99794d8 100644 --- a/packages/react/src/patterns/F0Dialog/components/F0DialogFooter.tsx +++ b/packages/react/src/patterns/F0Dialog/components/F0DialogFooter.tsx @@ -55,7 +55,10 @@ export const F0DialogFooter = ({ return ( void } + | { href?: never; onClick: () => void } + export type F0DialogPrimaryAction = { label: string icon?: IconType iconPosition?: "left" | "right" - onClick: () => void disabled?: boolean loading?: boolean -} +} & F0DialogActionTarget export type F0DialogSecondaryAction = { label: string icon?: IconType iconPosition?: "left" | "right" - onClick: () => void disabled?: boolean loading?: boolean -} +} & F0DialogActionTarget // Shared base for action items used in multi-action dropdowns. // Note: disabled/loading on items are reserved for future use —