Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4ecaf75
remove data broker from dashboard
codemist Nov 26, 2025
7f18d44
big sweep
codemist Nov 26, 2025
709ef55
fix build errors
codemist Nov 26, 2025
320ee16
fix filterexposures component
codemist Nov 26, 2025
ada3de5
remove data broker from exposurecarddataclass
codemist Nov 26, 2025
4600fff
fix dashboard top banner
codemist Nov 26, 2025
03e4986
fix resolution container
codemist Nov 26, 2025
f539857
fix guided resolution flow
codemist Nov 26, 2025
8a29722
include missed story
codemist Nov 26, 2025
41f1a46
fix build error in high riks data breaches
codemist Nov 26, 2025
afe4e1a
remove email address plus const
codemist Nov 27, 2025
2c6f25e
remove emails
codemist Nov 28, 2025
5539019
big sweep
codemist Nov 28, 2025
5692b54
remove isEligibleForPremium guard
codemist Nov 28, 2025
1bd8306
remove dashboard tests
codemist Dec 1, 2025
022a131
fix bad rebase
codemist Dec 2, 2025
8ad23bd
Remove plus settings stories
codemist Dec 2, 2025
8c50d6b
landing page remaining deprecation
codemist Dec 2, 2025
1b67fd9
remove data broker exposure card tests and onerep functions
codemist Dec 2, 2025
3d78536
onerep funcs cleanup
codemist Dec 2, 2025
921899a
remove removalprocess
codemist Dec 2, 2025
5b4deb1
remove cancel flow
codemist Dec 2, 2025
2cd5be2
more code removal
codemist Dec 4, 2025
05848b2
remove location data from build command
codemist Dec 4, 2025
d302587
remove broken imports
codemist Dec 4, 2025
54a1858
fix unit tests
codemist Dec 4, 2025
2091b5e
fix breaking build
codemist Dec 4, 2025
956fcdf
add invalid test case
codemist Dec 4, 2025
9d5f86e
fix tests
codemist Dec 4, 2025
8a1d253
remove accounts metric flow and search param logic
codemist Dec 4, 2025
843046d
fix unit tests
codemist Dec 4, 2025
f9de655
add useviewtelemetry test
codemist Dec 5, 2025
60a0e4a
fix all tests
codemist Dec 5, 2025
be97d80
remove more subscription code
codemist Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const FraudAlertModal = () => {
isDismissable={true}
>
<Dialog
onDismiss={() => overlayTriggerState.close()}
Copy link
Collaborator Author

@codemist codemist Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tracked down why onDismiss wasn’t being passed by default, it was only missing in this dialog component, which seemed inconsistent with our patterns. The dismiss button really should be a non-optional part of the dialog component, so I made it unconditional and added it directly to this component. It looked fine in Storybook, so I also added a test for coverage.

title={l10n.getString("ssn-modal-title")}
illustration={<Image src={FraudAlertDialogIllustration} alt="" />}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { it, expect } from "@jest/globals";
import { render, screen } from "@testing-library/react";
import { act, render, screen } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { composeStory } from "@storybook/react";
import { axe } from "jest-axe";
import { setupJestCanvasMock } from "jest-canvas-mock";
Expand Down Expand Up @@ -130,3 +131,49 @@ it("shows the high-risk celebration view, next step is passwords, no next step",
});
expect(buttonLink).toHaveAttribute("href", "/user/dashboard");
});

it("opens the fraud alert modal when Open modal button is clicked", async () => {
const user = userEvent.setup();
const ComposedComponent = composeStory(SsnStory, Meta);
// Suppress navigation errors from jsdom
jest.spyOn(console, "error").mockImplementation(() => undefined);
render(<ComposedComponent />);

// The FraudAlertModal is only rendered for SSN breach type
// The button has aria-label="Open modal" and aria-describedby="ssnModalTitle"
const learnMoreButton = screen.getByLabelText("Open modal");
await act(async () => {
await user.click(learnMoreButton);
});

const dialog = screen.getByRole("dialog");
expect(dialog).toBeInTheDocument();
// Verify the dialog contains a title element
expect(dialog).toHaveAttribute("aria-labelledby");
});

it("closes the fraud alert modal when close button is clicked", async () => {
const user = userEvent.setup();
const ComposedComponent = composeStory(SsnStory, Meta);
// Suppress navigation errors from jsdom
jest.spyOn(console, "error").mockImplementation(() => undefined);
render(<ComposedComponent />);

// Open the modal first
const learnMoreButton = screen.getByLabelText("Open modal");
await act(async () => {
await user.click(learnMoreButton);
});

expect(screen.getByRole("dialog")).toBeInTheDocument();

// Close the modal
const closeButton = screen.getByRole("button", {
name: "Close modal",
});
await act(async () => {
await user.click(closeButton);
});

expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@
expect(affectedRadioButton).toHaveAttribute("aria-checked", "true");
});

it("refreshes the session token after changing email alert preferences, to ensure the latest pref is available in it", async () => {

Check warning on line 751 in src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/settings/SettingsPage.test.tsx

View workflow job for this annotation

GitHub Actions / npm-lint

Test has no assertions

Check warning on line 751 in src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/settings/SettingsPage.test.tsx

View workflow job for this annotation

GitHub Actions / npm-lint

Test has no assertions
global.fetch = jest.fn().mockResolvedValueOnce({ ok: true });
const user = userEvent.setup();
const ComposedStory = composeStory(
Expand All @@ -762,8 +762,23 @@
await act(async () => {
await user.click(affectedRadioButton);
});
});
});

describe("activeTab prop fallback", () => {
it("uses first tab (edit-info) as default when activeTab is undefined", () => {
const ComposedStory = composeStory(
SettingsEditNotifications,
SettingsMeta,
);
// Pass undefined to test the fallback logic from SettingsContent.tsx line 58
render(<ComposedStory activeTab={undefined} />);

expect(mockedSessionUpdate).toHaveBeenCalledTimes(1);
// The first tab "Edit info" should be selected by default (tabsData[0].key = "edit-info")
const editInfoPanel = screen.getByRole("heading", {
name: "Update scan info",
});
expect(editInfoPanel).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import {
EmailAddressRow,
OnerepProfileRow,
SubscriberRow,
} from "knex/types/tables";
import { EmailAddressRow, SubscriberRow } from "knex/types/tables";
import { fn } from "storybook/test";
import { SettingsView, TabType } from "../View";
import { Shell } from "../../../../../Shell/Shell";
Expand Down Expand Up @@ -34,9 +30,7 @@ export type SettingsWrapperProps = {
isMonthlySubscriber: boolean;
subscriber: SubscriberRow;
emailAddresses?: EmailAddressRow[];
profileData?: OnerepProfileRow;
data?: SubscriberEmailPreferencesOutput;
hasPlus?: boolean;
};

export const mockedActions = {
Expand All @@ -57,7 +51,7 @@ export const SettingsWrapper = (props: SettingsWrapperProps) => {
metricsEnabled: false,
avatar: "https://profile.stage.mozaws.net/v1/avatar/e",
avatarDefault: true,
subscriptions: props.hasPlus ? ["monitor"] : [],
subscriptions: [],
},
subscriber: {
id: 42,
Expand Down
39 changes: 19 additions & 20 deletions src/app/components/client/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useL10n } from "../../../hooks/l10n";

export type Props = {
children: ReactNode;
onDismiss?: () => void;
onDismiss: () => void;
title?: ReactNode;
illustration?: ReactNode;
fitContent?: boolean;
Expand Down Expand Up @@ -40,25 +40,24 @@ export const Dialog = ({
dialogTitleRef.current?.focus();
}, []);

const dismissButton =
typeof onDismiss === "function" ? (
<button
{...dismissButtonProps}
ref={dismissButtonRef}
className={styles.dismissButton}
/* c8 ignore start */
onClick={() => {
onDismiss();
}}
/* c8 ignore stop */
>
<CloseBtn
alt={l10n.getString("close-modal-alt")}
width="14"
height="14"
/>
</button>
) : null;
const dismissButton = (
<button
{...dismissButtonProps}
ref={dismissButtonRef}
className={styles.dismissButton}
/* c8 ignore start */
onClick={() => {
onDismiss();
}}
/* c8 ignore stop */
>
<CloseBtn
alt={l10n.getString("close-modal-alt")}
width="14"
height="14"
/>
</button>
);

return (
<div
Expand Down
20 changes: 0 additions & 20 deletions src/app/functions/universal/attributions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,6 @@ export function modifyAttributionsForUrl(
return `${originalUrl.href.split("?")[0]}?${searchParams.toString()}`;
}

export function modifyAttributionsForUrlSearchParams(
searchParams: URLSearchParams,
replaceValues: Record<string, string>,
defaultValues: Record<string, string>,
) {
// overwrite the three params below
for (const k in replaceValues) {
searchParams.set(k, replaceValues[k]);
}

// placeholder utms if acquisition source is unknown
for (const k in defaultValues) {
if (!searchParams.has(k)) {
searchParams.append(k, defaultValues[k]);
}
}

return searchParams;
}

export const containsExpectedSearchParams = (
expectedSearchParams: Record<string, string>,
searchParams: URLSearchParams,
Expand Down
13 changes: 0 additions & 13 deletions src/app/functions/universal/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { Session } from "next-auth";
import { ISO8601DateString } from "../../../utils/parse";
import { SubscriberRow } from "knex/types/tables";

// TODO: Keep this until after Dec 31, 2024, then remove Plus entirely.
Expand All @@ -20,15 +19,3 @@ export function hasPremium(user?: Session["user"] | SubscriberRow): boolean {
/* c8 ignore next */
return subscriptions?.includes("monitor") ?? false;
}

// Users need to be at least 13 years or older.
const USER_MIN_AGE = 13;
export function meetsAgeRequirement(dateOfBirth: ISO8601DateString): boolean {
const dateNow = new Date();
const dateBirth = new Date(dateOfBirth);
const dateDelta = new Date(dateNow.valueOf() - dateBirth.valueOf());
const unixStartDate = new Date(0);
const age = dateDelta.getUTCFullYear() - unixStartDate.getUTCFullYear();

return age >= USER_MIN_AGE;
}
Loading