Add Storybook story for Scan Results step
This adds a Storybook story for the scan results step in the guided resolution flow. Note that all the other steps don't have a story yet, and this is the second step in the flow (assuming all of them apply), so I named it "2." - but this one is relevant to my upcoming work, so I've focused on just this one for now.
This commit is contained in:
Родитель
1c8746b2ea
Коммит
f59a9b39ca
|
@ -0,0 +1,67 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 styles from "../dataBrokerProfiles.module.scss";
|
||||
import { getL10n } from "../../../../../../../../functions/server/l10n";
|
||||
import { DataBrokerProfiles } from "../../../../../../../../components/client/DataBrokerProfiles";
|
||||
import { LatestOnerepScanData } from "../../../../../../../../../db/tables/onerep_scans";
|
||||
import { AboutBrokersIcon } from "./AboutBrokersIcon";
|
||||
import { Button } from "../../../../../../../../components/server/Button";
|
||||
|
||||
export type Props = {
|
||||
scanData: LatestOnerepScanData;
|
||||
};
|
||||
|
||||
export const ViewDataBrokersView = (props: Props) => {
|
||||
const l10n = getL10n();
|
||||
|
||||
const countOfDataBrokerProfiles = props.scanData.results.length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.content}>
|
||||
<h3>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-headline",
|
||||
{ data_broker_sites_results_num: countOfDataBrokerProfiles }
|
||||
)}
|
||||
</h3>
|
||||
<p>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-content"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<h4 className={styles.questionTooltipWrapper}>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-view-info-on-sites"
|
||||
)}
|
||||
<AboutBrokersIcon />
|
||||
</h4>
|
||||
<DataBrokerProfiles data={props.scanData.results} />
|
||||
</div>
|
||||
<div className={styles.buttonsWrapper}>
|
||||
<Button
|
||||
variant="primary"
|
||||
href="/redesign/user/dashboard/fix/data-broker-profiles/automatic-remove"
|
||||
>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-button-remove-for-me"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
href={
|
||||
"/redesign/user/dashboard/fix/data-broker-profiles/manual-remove"
|
||||
}
|
||||
>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-button-remove-manually"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,255 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 type { Meta, StoryObj } from "@storybook/react";
|
||||
import { OnerepScanResultRow, OnerepScanRow } from "knex/types/tables";
|
||||
import { ViewDataBrokersView } from "./View";
|
||||
import {
|
||||
createRandomScanResult,
|
||||
createUserWithPremiumSubscription,
|
||||
} from "../../../../../../../../../apiMocks/mockData";
|
||||
import { Shell } from "../../../../../../Shell";
|
||||
import { getEnL10nSync } from "../../../../../../../../functions/server/mockL10n";
|
||||
import { FixView } from "../../FixView";
|
||||
import { GuidedExperienceBreaches } from "../../../../../../../../functions/server/getUserBreaches";
|
||||
import { LatestOnerepScanData } from "../../../../../../../../../db/tables/onerep_scans";
|
||||
|
||||
const mockedBreachesEmpty: GuidedExperienceBreaches = {
|
||||
emails: [],
|
||||
highRisk: {
|
||||
bankBreaches: [],
|
||||
creditCardBreaches: [],
|
||||
pinBreaches: [],
|
||||
ssnBreaches: [],
|
||||
},
|
||||
passwordBreaches: {
|
||||
passwords: [],
|
||||
securityQuestions: [],
|
||||
},
|
||||
securityRecommendations: {
|
||||
emailAddress: [],
|
||||
IPAddress: [],
|
||||
phoneNumber: [],
|
||||
},
|
||||
};
|
||||
|
||||
const brokerOptions = {
|
||||
"no-scan": "No scan started",
|
||||
empty: "No scan results",
|
||||
"emtpy-scan-in-progress": "Scan is in progress with no results",
|
||||
"unresolved-scan-in-progress": "Scan is in progress with unresolved results",
|
||||
"unresolved-few": "With a few unresolved scan results",
|
||||
"unresolved-many": "With many unresolved scan results",
|
||||
resolved: "All scan results resolved",
|
||||
};
|
||||
type ViewWrapperProps = {
|
||||
brokers: keyof typeof brokerOptions;
|
||||
premium: boolean;
|
||||
};
|
||||
const ViewWrapper = (props: ViewWrapperProps) => {
|
||||
const mockedScan: OnerepScanRow = {
|
||||
created_at: new Date(1998, 2, 31),
|
||||
updated_at: new Date(1998, 2, 31),
|
||||
id: 0,
|
||||
onerep_profile_id: 0,
|
||||
onerep_scan_id: 0,
|
||||
onerep_scan_reason: "initial",
|
||||
onerep_scan_status: "finished",
|
||||
};
|
||||
const mockedScanInProgress: OnerepScanRow = {
|
||||
...mockedScan,
|
||||
onerep_scan_status: "in_progress",
|
||||
};
|
||||
|
||||
const mockedResolvedScanResults: OnerepScanResultRow[] = [
|
||||
createRandomScanResult({ status: "removed" }),
|
||||
createRandomScanResult({ status: "waiting_for_verification" }),
|
||||
createRandomScanResult({ status: "optout_in_progress" }),
|
||||
];
|
||||
|
||||
const mockedFewUnresolvedScanResults: OnerepScanResultRow[] = [
|
||||
...mockedResolvedScanResults,
|
||||
createRandomScanResult({ status: "new", manually_resolved: false }),
|
||||
createRandomScanResult({ status: "new", manually_resolved: true }),
|
||||
];
|
||||
|
||||
const mockedManyUnresolvedScanResults: OnerepScanResultRow[] = [
|
||||
...Array(42),
|
||||
].map(() =>
|
||||
createRandomScanResult({ status: "new", manually_resolved: false })
|
||||
);
|
||||
|
||||
const scanData: LatestOnerepScanData = { scan: null, results: [] };
|
||||
|
||||
if (props.brokers !== "no-scan") {
|
||||
scanData.scan =
|
||||
props.brokers === "emtpy-scan-in-progress" ||
|
||||
props.brokers === "unresolved-scan-in-progress"
|
||||
? mockedScanInProgress
|
||||
: mockedScan;
|
||||
|
||||
if (props.brokers === "resolved") {
|
||||
scanData.results = mockedResolvedScanResults;
|
||||
}
|
||||
if (props.brokers === "unresolved-scan-in-progress") {
|
||||
scanData.results = mockedFewUnresolvedScanResults;
|
||||
}
|
||||
if (props.brokers === "unresolved-few") {
|
||||
scanData.results = mockedFewUnresolvedScanResults;
|
||||
}
|
||||
if (props.brokers === "unresolved-many") {
|
||||
scanData.results = mockedManyUnresolvedScanResults;
|
||||
}
|
||||
}
|
||||
|
||||
const user = createUserWithPremiumSubscription();
|
||||
if (!props.premium) {
|
||||
user.fxa.subscriptions = [];
|
||||
}
|
||||
|
||||
const mockedSession = {
|
||||
expires: new Date().toISOString(),
|
||||
user: user,
|
||||
};
|
||||
|
||||
return (
|
||||
<Shell l10n={getEnL10nSync()} session={mockedSession} nonce="">
|
||||
<FixView
|
||||
breaches={mockedBreachesEmpty}
|
||||
userScannedResults={scanData.results}
|
||||
>
|
||||
<ViewDataBrokersView scanData={scanData} />
|
||||
</FixView>
|
||||
</Shell>
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ViewWrapper> = {
|
||||
title: "Pages/Guided resolution/1b. Scan results",
|
||||
component: ViewWrapper,
|
||||
argTypes: {
|
||||
brokers: {
|
||||
options: Object.keys(brokerOptions),
|
||||
description: "Scan results",
|
||||
control: {
|
||||
type: "radio",
|
||||
labels: brokerOptions,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ViewWrapper>;
|
||||
|
||||
export const NoneFree: Story = {
|
||||
name: "No scan yet (free)",
|
||||
args: {
|
||||
brokers: "no-scan",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const NonePremium: Story = {
|
||||
name: "No scan yet (Premium)",
|
||||
args: {
|
||||
brokers: "no-scan",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyFree: Story = {
|
||||
name: "No scan results (free)",
|
||||
args: {
|
||||
brokers: "empty",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyPremium: Story = {
|
||||
name: "No scan results (Premium)",
|
||||
args: {
|
||||
brokers: "empty",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyInProgressFree: Story = {
|
||||
name: "Scan in progress, no results yet (free)",
|
||||
args: {
|
||||
brokers: "emtpy-scan-in-progress",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyInProgressPremium: Story = {
|
||||
name: "Scan in progress, no results yet (Premium)",
|
||||
args: {
|
||||
brokers: "emtpy-scan-in-progress",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedInProgressFewFree: Story = {
|
||||
name: "Scan in progress, some unresolved results already (free)",
|
||||
args: {
|
||||
brokers: "unresolved-scan-in-progress",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedInProgressFewPremium: Story = {
|
||||
name: "Scan in progress, some unresolved results already (Premium)",
|
||||
args: {
|
||||
brokers: "unresolved-scan-in-progress",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const ResolvedFree: Story = {
|
||||
name: "All scan results resolved (free)",
|
||||
args: {
|
||||
brokers: "resolved",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const ResolvedPremium: Story = {
|
||||
name: "All scan results resolved (Premium)",
|
||||
args: {
|
||||
brokers: "resolved",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedFewFree: Story = {
|
||||
name: "With a few unresolved scan results (free)",
|
||||
args: {
|
||||
brokers: "unresolved-few",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedFewPremium: Story = {
|
||||
name: "With a few unresolved scan results (Premium)",
|
||||
args: {
|
||||
brokers: "unresolved-few",
|
||||
premium: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedManyFree: Story = {
|
||||
name: "With many unresolved scan results (free)",
|
||||
args: {
|
||||
brokers: "unresolved-many",
|
||||
premium: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const UnresolvedManyPremium: Story = {
|
||||
name: "With many unresolved scan results (Premium)",
|
||||
args: {
|
||||
brokers: "unresolved-many",
|
||||
premium: true,
|
||||
},
|
||||
};
|
|
@ -2,19 +2,14 @@
|
|||
* 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 styles from "../dataBrokerProfiles.module.scss";
|
||||
import { getL10n } from "../../../../../../../../functions/server/l10n";
|
||||
import { DataBrokerProfiles } from "../../../../../../../../components/client/DataBrokerProfiles";
|
||||
import { getLatestOnerepScanResults } from "../../../../../../../../../db/tables/onerep_scans";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getLatestOnerepScanResults } from "../../../../../../../../../db/tables/onerep_scans";
|
||||
import { authOptions } from "../../../../../../../../api/utils/auth";
|
||||
import { getOnerepProfileId } from "../../../../../../../../../db/tables/subscribers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { AboutBrokersIcon } from "./AboutBrokersIcon";
|
||||
import { Button } from "../../../../../../../../components/server/Button";
|
||||
import { ViewDataBrokersView } from "./View";
|
||||
|
||||
export default async function ViewDataBrokers() {
|
||||
const l10n = getL10n();
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
if (!session?.user?.subscriber?.id) {
|
||||
|
@ -25,53 +20,5 @@ export default async function ViewDataBrokers() {
|
|||
const profileId = result[0]["onerep_profile_id"] as number;
|
||||
const latestScan = await getLatestOnerepScanResults(profileId);
|
||||
|
||||
// TODO: Use api to set/query count
|
||||
const countOfDataBrokerProfiles = latestScan.results.length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.content}>
|
||||
<h3>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-headline",
|
||||
{ data_broker_sites_results_num: countOfDataBrokerProfiles }
|
||||
)}
|
||||
</h3>
|
||||
<p>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-content"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<h4 className={styles.questionTooltipWrapper}>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-view-info-on-sites"
|
||||
)}
|
||||
<AboutBrokersIcon />
|
||||
</h4>
|
||||
<DataBrokerProfiles data={latestScan.results} />
|
||||
</div>
|
||||
<div className={styles.buttonsWrapper}>
|
||||
<Button
|
||||
variant="primary"
|
||||
href="/redesign/user/dashboard/fix/data-broker-profiles/automatic-remove"
|
||||
>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-button-remove-for-me"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
href={
|
||||
"/redesign/user/dashboard/fix/data-broker-profiles/manual-remove"
|
||||
}
|
||||
>
|
||||
{l10n.getString(
|
||||
"fix-flow-data-broker-profiles-view-data-broker-profiles-button-remove-manually"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <ViewDataBrokersView scanData={latestScan} />;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче