AlertDialogAction was a plain <Button> — only AlertDialogCancel wrapped the primitive's Close — so confirming ran the action but never dismissed the dialog. Found in Trippy, where "Clear conversation" cleared the chat and left the modal sitting on screen; the same component is in every Crema app. Delete-style dialogs hide it by accident: the row they act on unmounts out from under the open dialog and takes it with it. Any confirm that leaves its target on screen (a rewrite, a regenerate, a clear) just gets stuck. Fixed at the component, so every call site gets it. The action still runs — Base UI merges its own close handler with the caller's onClick — and async work carries on behind the closed dialog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
// Confirming an AlertDialog must close it AND run the action.
|
|
//
|
|
// AlertDialogAction used to be a plain <Button>, so the dialog just sat there
|
|
// after you confirmed ("Clear conversation" cleared the chat and left the modal
|
|
// on screen). Other call sites only looked right because the row they acted on
|
|
// unmounted underneath the open dialog. Both halves are asserted here so a
|
|
// future refactor can't quietly drop one.
|
|
|
|
import { act } from "react"
|
|
import { render, screen } from "@testing-library/react"
|
|
import { afterEach, describe, expect, it, vi } from "vitest"
|
|
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "./alert-dialog"
|
|
|
|
function Harness({ onConfirm }: { onConfirm: () => void }) {
|
|
return (
|
|
<AlertDialog defaultOpen>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Clear this conversation?</AlertDialogTitle>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Keep it</AlertDialogCancel>
|
|
<AlertDialogAction onClick={onConfirm}>Clear</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
)
|
|
}
|
|
|
|
afterEach(() => vi.restoreAllMocks())
|
|
|
|
describe("AlertDialogAction", () => {
|
|
it("runs the action and closes the dialog", async () => {
|
|
const onConfirm = vi.fn()
|
|
render(<Harness onConfirm={onConfirm} />)
|
|
|
|
expect(screen.getByText("Clear this conversation?")).toBeTruthy()
|
|
|
|
await act(async () => {
|
|
screen.getByRole("button", { name: "Clear" }).click()
|
|
})
|
|
|
|
expect(onConfirm).toHaveBeenCalledTimes(1)
|
|
expect(screen.queryByText("Clear this conversation?")).toBeNull()
|
|
})
|
|
|
|
it("cancelling closes without running the action", async () => {
|
|
const onConfirm = vi.fn()
|
|
render(<Harness onConfirm={onConfirm} />)
|
|
|
|
await act(async () => {
|
|
screen.getByRole("button", { name: "Keep it" }).click()
|
|
})
|
|
|
|
expect(onConfirm).not.toHaveBeenCalled()
|
|
expect(screen.queryByText("Clear this conversation?")).toBeNull()
|
|
})
|
|
})
|