Place account deletion in its own danger row #82
No files matched your search
@@ -237,17 +237,8 @@ export function ProfilePageView({
|
||||
leadingIcon: "mail",
|
||||
showDescription: false,
|
||||
},
|
||||
{
|
||||
id: "delete-account",
|
||||
title: t("deleteAccount"),
|
||||
description: "",
|
||||
onClick: onOpenDeleteAccount,
|
||||
leadingIcon: "warning",
|
||||
variant: "danger",
|
||||
showDescription: false,
|
||||
},
|
||||
];
|
||||
}, [t, onSignOut, onOpenDeleteAccount, onOpenEmailChange, onStartNewCustomRule]);
|
||||
}, [t, onSignOut, onOpenEmailChange, onStartNewCustomRule]);
|
||||
|
||||
const ruleCardShellClass =
|
||||
"w-full !max-w-full cursor-default !gap-3 !rounded-[12px] shadow-[0_0_48px_rgba(0,0,0,0.1)] lg:!rounded-[24px] lg:shadow-[0_0_24px_rgba(0,0,0,0.1)]";
|
||||
@@ -408,6 +399,31 @@ export function ProfilePageView({
|
||||
</nav>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section
|
||||
className="flex w-full flex-col items-start gap-3 rounded-[12px] border border-[var(--color-border-default-negative-primary)] bg-[var(--color-surface-default-negative-secondary)] px-4 py-4 lg:px-6 lg:py-6"
|
||||
aria-labelledby="profile-delete-heading"
|
||||
>
|
||||
<h2
|
||||
id="profile-delete-heading"
|
||||
className="text-small-label text-[var(--color-content-default-negative-primary)]"
|
||||
>
|
||||
{t("deleteAccount")}
|
||||
</h2>
|
||||
<p className="max-w-[640px] text-small-paragraph text-[var(--color-content-default-secondary)]">
|
||||
{t("deleteAccountIntro")}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
size="small"
|
||||
buttonType="danger"
|
||||
palette="default"
|
||||
className="self-start"
|
||||
onClick={onOpenDeleteAccount}
|
||||
>
|
||||
{t("deleteAccountConfirm")}
|
||||
</Button>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -502,7 +518,7 @@ export function ProfilePageView({
|
||||
<Button
|
||||
type="button"
|
||||
size="medium"
|
||||
buttonType="filled"
|
||||
buttonType="danger"
|
||||
palette="default"
|
||||
onClick={onConfirmDeleteAccount}
|
||||
disabled={accountDeleteBusy}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { within } from "@testing-library/react";
|
||||
import {
|
||||
cleanup,
|
||||
renderWithProviders as render,
|
||||
screen,
|
||||
} from "../utils/test-utils";
|
||||
import {
|
||||
ProfilePageView,
|
||||
type ProfilePageViewProps,
|
||||
} from "../../app/(app)/profile/_components/ProfilePage.view";
|
||||
import messages from "../../messages/en/index";
|
||||
|
||||
const copy = messages.pages.profile;
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
function viewProps(
|
||||
overrides: Partial<ProfilePageViewProps> = {},
|
||||
): ProfilePageViewProps {
|
||||
return {
|
||||
userEmail: "dani@example.com",
|
||||
ruleCardSize: "M",
|
||||
profileLgUp: true,
|
||||
profileListSize: "l",
|
||||
rules: [],
|
||||
rulesError: false,
|
||||
draft: null,
|
||||
showDraftCard: false,
|
||||
ruleDeleteOpen: false,
|
||||
ruleDeleteBusy: false,
|
||||
draftDeleteOpen: false,
|
||||
draftDeleteBusy: false,
|
||||
accountDeleteOpen: false,
|
||||
accountDeleteBusy: false,
|
||||
actionError: null,
|
||||
profileSuccessMessage: null,
|
||||
emailChangeOpen: false,
|
||||
emailChangeValue: "",
|
||||
onEmailChangeValueChange: vi.fn(),
|
||||
emailChangeBusy: false,
|
||||
emailChangeRequestSent: false,
|
||||
emailChangeModalError: null,
|
||||
onDismissEmailChangeModalError: vi.fn(),
|
||||
onOpenEmailChange: vi.fn(),
|
||||
onCloseEmailChange: vi.fn(),
|
||||
onSubmitEmailChange: vi.fn(),
|
||||
onSignOut: vi.fn(),
|
||||
onDeleteRule: vi.fn(),
|
||||
onCloseDeleteRule: vi.fn(),
|
||||
onConfirmDeleteRule: vi.fn(),
|
||||
onDuplicateRule: vi.fn(),
|
||||
onContinueDraft: vi.fn(),
|
||||
onDeleteDraft: vi.fn(),
|
||||
onCloseDeleteDraft: vi.fn(),
|
||||
onConfirmDeleteDraft: vi.fn(),
|
||||
onOpenDeleteAccount: vi.fn(),
|
||||
onCloseDeleteAccount: vi.fn(),
|
||||
onConfirmDeleteAccount: vi.fn(),
|
||||
onDismissProfileSuccess: vi.fn(),
|
||||
onDismissActionError: vi.fn(),
|
||||
onDismissRulesError: vi.fn(),
|
||||
onStartNewCustomRule: vi.fn(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("ProfilePageView", () => {
|
||||
test("keeps account deletion out of the routine options list", () => {
|
||||
render(<ProfilePageView {...viewProps()} />);
|
||||
|
||||
const options = screen.getByRole("navigation", {
|
||||
name: copy.yourOptionsHeading,
|
||||
});
|
||||
const createRule = within(options).getByRole("button", {
|
||||
name: copy.optionCreateCustom,
|
||||
});
|
||||
expect(createRule.closest("ul")).not.toBeNull();
|
||||
expect(
|
||||
within(options).queryByRole("button", { name: copy.deleteAccountConfirm }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
within(options).queryByRole("heading", { name: copy.deleteAccount }),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
const danger = screen.getByRole("region", { name: copy.deleteAccount });
|
||||
expect(danger).toHaveTextContent(copy.deleteAccountIntro);
|
||||
const deleteAccount = within(danger).getByRole("button", {
|
||||
name: copy.deleteAccountConfirm,
|
||||
});
|
||||
expect(deleteAccount.closest("ul")).toBeNull();
|
||||
expect(deleteAccount.className).toContain(
|
||||
"border-[var(--color-border-default-negative-primary)]",
|
||||
);
|
||||
expect(deleteAccount.className).not.toContain(
|
||||
"bg-[var(--color-surface-inverse-primary)]",
|
||||
);
|
||||
});
|
||||
|
||||
test("asks for confirmation before deleting the account", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onOpenDeleteAccount = vi.fn();
|
||||
const onConfirmDeleteAccount = vi.fn();
|
||||
const { rerender } = render(
|
||||
<ProfilePageView {...viewProps({ onOpenDeleteAccount })} />,
|
||||
);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: copy.deleteAccountConfirm }),
|
||||
);
|
||||
expect(onOpenDeleteAccount).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender(
|
||||
<ProfilePageView
|
||||
{...viewProps({
|
||||
onOpenDeleteAccount,
|
||||
onConfirmDeleteAccount,
|
||||
accountDeleteOpen: true,
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
const dialog = screen.getByRole("dialog", {
|
||||
name: copy.deleteAccountModalTitle,
|
||||
});
|
||||
expect(dialog).toHaveTextContent(copy.deleteAccountModalBody);
|
||||
const confirm = within(dialog).getByRole("button", {
|
||||
name: copy.deleteAccountConfirm,
|
||||
});
|
||||
expect(confirm.className).toContain(
|
||||
"border-[var(--color-border-default-negative-primary)]",
|
||||
);
|
||||
expect(confirm.className).not.toContain(
|
||||
"bg-[var(--color-surface-inverse-primary)]",
|
||||
);
|
||||
|
||||
await user.click(confirm);
|
||||
expect(onConfirmDeleteAccount).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user