Separate account deletion from the routine profile options so it reads as a destructive action.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d66cfb5ad3
commit
72c51adbb3
2 files changed
+170
-11
No files matched your search
@@ -237,17 +237,8 @@ export function ProfilePageView({
|
|||||||
leadingIcon: "mail",
|
leadingIcon: "mail",
|
||||||
showDescription: false,
|
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 =
|
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)]";
|
"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>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -502,7 +518,7 @@ export function ProfilePageView({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="medium"
|
size="medium"
|
||||||
buttonType="filled"
|
buttonType="danger"
|
||||||
palette="default"
|
palette="default"
|
||||||
onClick={onConfirmDeleteAccount}
|
onClick={onConfirmDeleteAccount}
|
||||||
disabled={accountDeleteBusy}
|
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