From bf7a1c3e067bd3dbbc4735eb98b28617dcc862bc Mon Sep 17 00:00:00 2001 From: Kaitlyn Andres Date: Fri, 29 Nov 2024 11:43:31 -0500 Subject: [PATCH] MNTOR-3396 - Organize storybook dashboard states (#5308) * Migrate utils/hibp.js to TypeScript This removes the `Breach` type in functions/universal/breaches, which was created when first introducing TypeScript and the flow of data was still unclear, but by now had overlap with other types and no clear provenance. Instead, there are now three breach-related types, that represent where the data came from: - HibpGetBreachesResponse: this is an array of breach elements as returned from the HIBP API, unprocessed. Properties are in PascalCase, so are a breach's data classes. - BreachRow: this is a breach's data as stored in our database, along with some data we added to it, such as a favicon URL. Properties are snake_case, and data classes are lowercased and kebab-cased by the formatDataClassesArray function. - HibpLikeDbBreach: this is a breach's data fetched from the database, but stored in an object meant to look like the ones in HibpGetBreachesResponse. In other words, it contains the same data as BreachRow (including lowercased, kebab-cased data classes), but on PascalCase properties. The latter is somewhat of a historical artefact, because we used to try to load breaches from our database, then if our database didn't contain any breaches yet, fetch them live from the HIBP API and continue working with that. We no longer do that: now, even after fetching them from the HIBP API, we do a new query to get them from the database and process them into HibpLikeDbBreach, so that we can assume a consisent data structure everywhere we work with breaches. * MNTOR-3435 - breaches js to ts * remove old typedef * explicitly type breach as any * fix rebase error * update breaches path * organize storybook files * add mobile and public shell stories * organize dashboard states * rollback unwanted changes * rollback unwanted changes * rollback unwanted changes * consolidate brokeroptiona and breachoptions * consolidate dashboardwrapper props * top banner spacing * fix lint error * rmv checkbox and radio stories --------- Co-authored-by: Vincent --- .../dashboard/Dashboard.stories.tsx | 6 +- .../(dashboard)/dashboard/Dashboard.test.tsx | 24 +- .../dashboard/DashboardNonUSUsers.stories.tsx | 275 ++++++++++++ .../dashboard/DashboardPlusUsers.stories.tsx | 421 ++++++++++++++++++ .../dashboard/DashboardUSUsers.stories.tsx | 412 +++++++++++++++++ .../(public)/MobileShell.stories.ts | 45 ++ .../(public)/PublicShell.stories.ts | 22 + .../breaches/BreachIndexView.stories.tsx | 10 +- .../exposure_card/ExposureCard.stories.tsx | 2 +- .../client/stories/Button.stories.ts | 2 +- .../client/stories/Chart.stories.ts | 2 +- .../client/stories/ComboBox.stories.tsx | 2 +- .../client/stories/CsatSurvey.stories.ts | 2 +- .../client/stories/ExposuresFilter.stories.ts | 2 +- .../client/stories/InputField.stories.ts | 2 +- .../client/stories/PetitionBanner.stories.ts | 2 +- .../client/stories/ProgressBar.stories.tsx | 2 +- .../client/stories/ProgressCard.stories.ts | 2 +- .../client/stories/TabList.stories.ts | 2 +- .../client/stories/UpsellCta.stories.tsx | 2 +- .../client/toolbar/AppPicker.stories.ts | 2 +- .../client/toolbar/UserMenu.stories.ts | 2 +- .../server/stories/StatusPill.stories.ts | 2 +- 23 files changed, 1210 insertions(+), 35 deletions(-) create mode 100644 src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardNonUSUsers.stories.tsx create mode 100644 src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardPlusUsers.stories.tsx create mode 100644 src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardUSUsers.stories.tsx create mode 100644 src/app/(proper_react)/(redesign)/(public)/MobileShell.stories.ts create mode 100644 src/app/(proper_react)/(redesign)/(public)/PublicShell.stories.ts diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.stories.tsx index edd1db69390..647bba17ee1 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.stories.tsx @@ -24,7 +24,7 @@ import { } from "../../../../../../../telemetry/generated/nimbus/experiments"; import { FeatureFlagName } from "../../../../../../../db/tables/featureFlags"; -const brokerOptions = { +export const brokerOptions = { "no-scan": "No scan started", empty: "No scan results", unresolved: "With unresolved scan results", @@ -32,12 +32,12 @@ const brokerOptions = { "scan-in-progress": "Scan is in progress", "manually-resolved": "Manually resolved", }; -const breachOptions = { +export const breachOptions = { empty: "No data breaches", unresolved: "With unresolved data breaches", resolved: "All data breaches resolved", }; -type DashboardWrapperProps = ( +export type DashboardWrapperProps = ( | { countryCode: "us"; brokers: keyof typeof brokerOptions; diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.test.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.test.tsx index 2bc43a344bc..6b1c5e6d699 100644 --- a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.test.tsx +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/Dashboard.test.tsx @@ -20,6 +20,11 @@ import Meta, { DashboardNonUsNoBreaches, DashboardNonUsUnresolvedBreaches, DashboardNonUsResolvedBreaches, +} from "./DashboardNonUSUsers.stories"; +import { useTelemetry } from "../../../../../../hooks/useTelemetry"; +import { deleteAllCookies } from "../../../../../../functions/client/deleteAllCookies"; +import { defaultExperimentData } from "../../../../../../../telemetry/generated/nimbus/experiments"; +import { DashboardUsNoPremiumNoScanNoBreaches, DashboardUsNoPremiumNoScanUnresolvedBreaches, DashboardUsNoPremiumNoScanResolvedBreaches, @@ -33,6 +38,11 @@ import Meta, { DashboardUsNoPremiumResolvedScanNoBreaches, DashboardUsNoPremiumResolvedScanUnresolvedBreaches, DashboardUsNoPremiumResolvedScanResolvedBreaches, + DashboardUsNoPremiumScanInProgressNoBreaches, + DashboardUsNoPremiumScanInProgressUnresolvedBreaches, + DashboardUsNoPremiumScanInProgressResolvedBreaches, +} from "./DashboardUSUsers.stories"; +import { DashboardUsPremiumEmptyScanNoBreaches, DashboardUsPremiumEmptyScanUnresolvedBreaches, DashboardUsPremiumEmptyScanResolvedBreaches, @@ -42,18 +52,12 @@ import Meta, { DashboardUsPremiumResolvedScanNoBreaches, DashboardUsPremiumResolvedScanUnresolvedBreaches, DashboardUsPremiumResolvedScanResolvedBreaches, - DashboardUsNoPremiumScanInProgressNoBreaches, - DashboardUsNoPremiumScanInProgressUnresolvedBreaches, - DashboardUsNoPremiumScanInProgressResolvedBreaches, - DashboardUsPremiumScanInProgressNoBreaches, - DashboardUsPremiumScanInProgressUnresolvedBreaches, - DashboardUsPremiumScanInProgressResolvedBreaches, DashboardInvalidPremiumUserNoScanResolvedBreaches, DashboardUsPremiumManuallyResolvedScansNoBreaches, -} from "./Dashboard.stories"; -import { useTelemetry } from "../../../../../../hooks/useTelemetry"; -import { deleteAllCookies } from "../../../../../../functions/client/deleteAllCookies"; -import { defaultExperimentData } from "../../../../../../../telemetry/generated/nimbus/experiments"; + DashboardUsPremiumScanInProgressNoBreaches, + DashboardUsPremiumScanInProgressResolvedBreaches, + DashboardUsPremiumScanInProgressUnresolvedBreaches, +} from "./DashboardPlusUsers.stories"; jest.mock("next/navigation", () => ({ useRouter: jest.fn(), diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardNonUSUsers.stories.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardNonUSUsers.stories.tsx new file mode 100644 index 00000000000..a09debfbefc --- /dev/null +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardNonUSUsers.stories.tsx @@ -0,0 +1,275 @@ +/* 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 { faker } from "@faker-js/faker"; +import { View as DashboardEl } from "./View"; +import { Shell } from "../../../../Shell"; +import { getL10n } from "../../../../../../functions/l10n/storybookAndJest"; +import { + createRandomScanResult, + createRandomBreach, + createUserWithPremiumSubscription, +} from "../../../../../../../apiMocks/mockData"; +import { SubscriberBreach } from "../../../../../../../utils/subscriberBreaches"; +import { LatestOnerepScanData } from "../../../../../../../db/tables/onerep_scans"; +import { CountryCodeProvider } from "../../../../../../../contextProviders/country-code"; +import { SessionProvider } from "../../../../../../../contextProviders/session"; +import { defaultExperimentData } from "../../../../../../../telemetry/generated/nimbus/experiments"; +import { + breachOptions, + brokerOptions, + DashboardWrapperProps, +} from "./Dashboard.stories"; + +const DashboardWrapper = (props: DashboardWrapperProps) => { + const mockedResolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: [ + "email-addresses", + "ip-addresses", + "phone-numbers", + "passwords", + "pins", + "social-security-numbers", + "partial-credit-card-data", + "security-questions-and-answers", + ], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + { "phone-numbers": 1 }, + { passwords: 1 }, + ], + isResolved: true, + }); + + const mockedUnresolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: ["email-addresses", "ip-addresses", "phone-numbers"], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + ], + isResolved: false, + }); + + let breaches: SubscriberBreach[] = []; + if (props.breaches === "resolved") { + breaches = [mockedResolvedBreach]; + } + if (props.breaches === "unresolved") { + breaches = [mockedResolvedBreach, mockedUnresolvedBreach]; + } + + const mockedScan: OnerepScanRow = { + created_at: new Date(Date.UTC(1998, 2, 31)), + updated_at: new Date(Date.UTC(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 mockedInProgressScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: false, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: false, + }), + ]; + + const mockedAllResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ status: "removed", manually_resolved: false }), + ]; + + const mockedUnresolvedScanResults: OnerepScanResultRow[] = [ + ...mockedInProgressScanResults, + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: true }), + ]; + + const mockedManuallyResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "new", manually_resolved: true }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: true, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: true, + }), + createRandomScanResult({ status: "removed", manually_resolved: true }), + ]; + + const scanData: LatestOnerepScanData = { scan: null, results: [] }; + let scanCount = 0; + + if (props.countryCode === "us") { + if (props.brokers && props.brokers !== "no-scan") { + const scanInProgress = props.brokers === "scan-in-progress"; + scanData.scan = scanInProgress ? mockedScanInProgress : mockedScan; + + if (scanInProgress) { + scanCount = 1; + } + if (props.brokers === "resolved") { + scanData.results = mockedAllResolvedScanResults; + } + if (props.brokers === "unresolved") { + scanData.results = mockedUnresolvedScanResults; + } + + if (props.brokers === "manually-resolved") { + scanData.results = mockedManuallyResolvedScanResults; + } + } + } + + const user = createUserWithPremiumSubscription(); + if ((props.countryCode !== "us" || !props.premium) && user.fxa) { + user.fxa.subscriptions = []; + } + + const mockedSession = { + expires: new Date().toISOString(), + user: user, + }; + + const mockedRemovalTimeEstimates = scanData.results + .map((scan) => ({ + d: scan.data_broker, + t: faker.number.float({ min: 0, max: 200 }), + })) + .filter(() => Math.random() < 0.1); + + return ( + + + + + + + + ); +}; + +const meta: Meta = { + title: "Pages/Logged in/Dashboard/Non US User", + component: DashboardWrapper, + argTypes: { + brokers: { + options: Object.keys(brokerOptions), + description: "Scan results", + control: { + type: "radio", + labels: brokerOptions, + }, + }, + breaches: { + options: Object.keys(breachOptions), + control: { + type: "radio", + labels: breachOptions, + }, + }, + elapsedTimeInDaysSinceInitialScan: { + name: "Days since initial scan", + control: { + type: "number", + }, + }, + hasFirstMonitoringScan: { + name: "Has first monitoring scan", + control: { + type: "boolean", + }, + }, + signInCount: { + name: "Sign-in count", + control: { + type: "number", + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const DashboardNonUsNoBreaches: Story = { + name: "Non-US user, with 0 breaches", + args: { + countryCode: "nl", + breaches: "empty", + }, +}; + +export const DashboardNonUsUnresolvedBreaches: Story = { + name: "Non-US user, with unresolved breaches", + args: { + countryCode: "nl", + breaches: "unresolved", + }, +}; + +export const DashboardNonUsResolvedBreaches: Story = { + name: "Non-US user, with all breaches resolved", + args: { + countryCode: "nl", + breaches: "resolved", + }, +}; diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardPlusUsers.stories.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardPlusUsers.stories.tsx new file mode 100644 index 00000000000..58d7322949b --- /dev/null +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardPlusUsers.stories.tsx @@ -0,0 +1,421 @@ +/* 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 { faker } from "@faker-js/faker"; +import { View as DashboardEl } from "./View"; +import { Shell } from "../../../../Shell"; +import { getL10n } from "../../../../../../functions/l10n/storybookAndJest"; +import { + createRandomScanResult, + createRandomBreach, + createUserWithPremiumSubscription, +} from "../../../../../../../apiMocks/mockData"; +import { SubscriberBreach } from "../../../../../../../utils/subscriberBreaches"; +import { LatestOnerepScanData } from "../../../../../../../db/tables/onerep_scans"; +import { CountryCodeProvider } from "../../../../../../../contextProviders/country-code"; +import { SessionProvider } from "../../../../../../../contextProviders/session"; +import { defaultExperimentData } from "../../../../../../../telemetry/generated/nimbus/experiments"; +import { + breachOptions, + brokerOptions, + DashboardWrapperProps, +} from "./Dashboard.stories"; + +const DashboardWrapper = (props: DashboardWrapperProps) => { + const mockedResolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: [ + "email-addresses", + "ip-addresses", + "phone-numbers", + "passwords", + "pins", + "social-security-numbers", + "partial-credit-card-data", + "security-questions-and-answers", + ], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + { "phone-numbers": 1 }, + { passwords: 1 }, + ], + isResolved: true, + }); + + const mockedUnresolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: ["email-addresses", "ip-addresses", "phone-numbers"], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + ], + isResolved: false, + }); + + let breaches: SubscriberBreach[] = []; + if (props.breaches === "resolved") { + breaches = [mockedResolvedBreach]; + } + if (props.breaches === "unresolved") { + breaches = [mockedResolvedBreach, mockedUnresolvedBreach]; + } + + const mockedScan: OnerepScanRow = { + created_at: new Date(Date.UTC(1998, 2, 31)), + updated_at: new Date(Date.UTC(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 mockedInProgressScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: false, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: false, + }), + ]; + + const mockedAllResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ status: "removed", manually_resolved: false }), + ]; + + const mockedUnresolvedScanResults: OnerepScanResultRow[] = [ + ...mockedInProgressScanResults, + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: true }), + ]; + + const mockedManuallyResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "new", manually_resolved: true }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: true, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: true, + }), + createRandomScanResult({ status: "removed", manually_resolved: true }), + ]; + + const scanData: LatestOnerepScanData = { scan: null, results: [] }; + let scanCount = 0; + + if (props.countryCode === "us") { + if (props.brokers && props.brokers !== "no-scan") { + const scanInProgress = props.brokers === "scan-in-progress"; + scanData.scan = scanInProgress ? mockedScanInProgress : mockedScan; + + if (scanInProgress) { + scanCount = 1; + } + if (props.brokers === "resolved") { + scanData.results = mockedAllResolvedScanResults; + } + if (props.brokers === "unresolved") { + scanData.results = mockedUnresolvedScanResults; + } + + if (props.brokers === "manually-resolved") { + scanData.results = mockedManuallyResolvedScanResults; + } + } + } + + const user = createUserWithPremiumSubscription(); + if ((props.countryCode !== "us" || !props.premium) && user.fxa) { + user.fxa.subscriptions = []; + } + + const mockedSession = { + expires: new Date().toISOString(), + user: user, + }; + + const mockedRemovalTimeEstimates = scanData.results + .map((scan) => ({ + d: scan.data_broker, + t: faker.number.float({ min: 0, max: 200 }), + })) + .filter(() => Math.random() < 0.1); + + return ( + + + + + + + + ); +}; + +const meta: Meta = { + title: "Pages/Logged in/Dashboard/US User/Plus", + component: DashboardWrapper, + argTypes: { + brokers: { + options: Object.keys(brokerOptions), + description: "Scan results", + control: { + type: "radio", + labels: brokerOptions, + }, + }, + breaches: { + options: Object.keys(breachOptions), + control: { + type: "radio", + labels: breachOptions, + }, + }, + elapsedTimeInDaysSinceInitialScan: { + name: "Days since initial scan", + control: { + type: "number", + }, + }, + hasFirstMonitoringScan: { + name: "Has first monitoring scan", + control: { + type: "boolean", + }, + }, + signInCount: { + name: "Sign-in count", + control: { + type: "number", + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const DashboardUsPremiumEmptyScanNoBreaches: Story = { + name: "US user, with Premium, with 0 scan results, with 0 breaches", + args: { + countryCode: "us", + premium: true, + breaches: "empty", + brokers: "empty", + }, +}; + +export const DashboardUsPremiumEmptyScanUnresolvedBreaches: Story = { + name: "US user, with Premium, with 0 scan results, with unresolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "unresolved", + brokers: "empty", + }, +}; + +export const DashboardUsPremiumEmptyScanResolvedBreaches: Story = { + name: "US user, with Premium, with 0 scan results, with all breaches resolved", + args: { + countryCode: "us", + premium: true, + breaches: "resolved", + brokers: "empty", + }, +}; + +export const DashboardUsPremiumUnresolvedScanNoBreaches: Story = { + name: "US user, with Premium, with unresolved scan results, with 0 breaches", + args: { + countryCode: "us", + premium: true, + breaches: "empty", + brokers: "unresolved", + }, +}; + +export const DashboardUsPremiumUnresolvedScanUnresolvedBreaches: Story = { + name: "US user, with Premium, with unresolved scan results, with unresolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "unresolved", + brokers: "unresolved", + }, +}; + +export const DashboardUsPremiumUnresolvedScanResolvedBreaches: Story = { + name: "US user, with Premium, with unresolved scan results, with all breaches resolved", + args: { + countryCode: "us", + premium: true, + breaches: "resolved", + brokers: "unresolved", + }, +}; + +export const DashboardUsPremiumResolvedScanNoBreaches: Story = { + name: "US user, with Premium, with all scan results resolved, with 0 breaches", + args: { + countryCode: "us", + premium: true, + breaches: "empty", + brokers: "resolved", + }, +}; + +export const DashboardUsPremiumResolvedScanUnresolvedBreaches: Story = { + name: "US user, with Premium, with all scan results resolved, with unresolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "unresolved", + brokers: "resolved", + }, +}; + +export const DashboardUsPremiumResolvedScanResolvedBreaches: Story = { + name: "US user, with Premium, with all scan results resolved, with all breaches resolved", + args: { + countryCode: "us", + premium: true, + breaches: "resolved", + brokers: "resolved", + }, +}; + +export const DashboardUsNoPremiumScanInProgressNoBreaches: Story = { + name: "US user, without Premium, scan in progress, with no breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsNoPremiumScanInProgressUnresolvedBreaches: Story = { + name: "US user, without Premium, scan in progress, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsNoPremiumScanInProgressResolvedBreaches: Story = { + name: "US user, without Premium, scan in progress, with resolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsPremiumScanInProgressNoBreaches: Story = { + name: "US user, with Premium, scan in progress, with no breaches", + args: { + countryCode: "us", + premium: true, + breaches: "empty", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsPremiumManuallyResolvedScansNoBreaches: Story = { + name: "US user, with Premium, scan manually resolved, with no breaches", + args: { + countryCode: "us", + premium: true, + breaches: "empty", + brokers: "manually-resolved", + }, +}; + +export const DashboardUsPremiumScanInProgressUnresolvedBreaches: Story = { + name: "US user, with Premium, scan in progress, with unresolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "unresolved", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsPremiumScanInProgressResolvedBreaches: Story = { + name: "US user, with Premium, scan in progress, with resolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "resolved", + brokers: "scan-in-progress", + }, +}; + +export const DashboardInvalidPremiumUserNoScanResolvedBreaches: Story = { + name: "Invalid state: US user, with Premium, with no scan, with resolved breaches", + args: { + countryCode: "us", + premium: true, + breaches: "resolved", + brokers: "no-scan", + }, +}; diff --git a/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardUSUsers.stories.tsx b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardUSUsers.stories.tsx new file mode 100644 index 00000000000..dc0d8b4711f --- /dev/null +++ b/src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/DashboardUSUsers.stories.tsx @@ -0,0 +1,412 @@ +/* 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 { faker } from "@faker-js/faker"; +import { View as DashboardEl } from "./View"; +import { Shell } from "../../../../Shell"; +import { getL10n } from "../../../../../../functions/l10n/storybookAndJest"; +import { + createRandomScanResult, + createRandomBreach, + createUserWithPremiumSubscription, +} from "../../../../../../../apiMocks/mockData"; +import { SubscriberBreach } from "../../../../../../../utils/subscriberBreaches"; +import { LatestOnerepScanData } from "../../../../../../../db/tables/onerep_scans"; +import { CountryCodeProvider } from "../../../../../../../contextProviders/country-code"; +import { SessionProvider } from "../../../../../../../contextProviders/session"; +import { defaultExperimentData } from "../../../../../../../telemetry/generated/nimbus/experiments"; +import { + breachOptions, + brokerOptions, + DashboardWrapperProps, +} from "./Dashboard.stories"; + +const DashboardWrapper = (props: DashboardWrapperProps) => { + const mockedResolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: [ + "email-addresses", + "ip-addresses", + "phone-numbers", + "passwords", + "pins", + "social-security-numbers", + "partial-credit-card-data", + "security-questions-and-answers", + ], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + { "phone-numbers": 1 }, + { passwords: 1 }, + ], + isResolved: true, + }); + + const mockedUnresolvedBreach: SubscriberBreach = createRandomBreach({ + dataClasses: ["email-addresses", "ip-addresses", "phone-numbers"], + addedDate: new Date("2023-06-18T14:48:00.000Z"), + dataClassesEffected: [ + { "email-addresses": ["email1@gmail.com", "email2@gmail.com"] }, + { "ip-addresses": 1 }, + ], + isResolved: false, + }); + + let breaches: SubscriberBreach[] = []; + if (props.breaches === "resolved") { + breaches = [mockedResolvedBreach]; + } + if (props.breaches === "unresolved") { + breaches = [mockedResolvedBreach, mockedUnresolvedBreach]; + } + + const mockedScan: OnerepScanRow = { + created_at: new Date(Date.UTC(1998, 2, 31)), + updated_at: new Date(Date.UTC(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 mockedInProgressScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: false, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: false, + }), + ]; + + const mockedAllResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "removed", manually_resolved: false }), + createRandomScanResult({ status: "removed", manually_resolved: false }), + ]; + + const mockedUnresolvedScanResults: OnerepScanResultRow[] = [ + ...mockedInProgressScanResults, + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: false }), + createRandomScanResult({ status: "new", manually_resolved: true }), + ]; + + const mockedManuallyResolvedScanResults: OnerepScanResultRow[] = [ + createRandomScanResult({ status: "new", manually_resolved: true }), + createRandomScanResult({ + status: "waiting_for_verification", + manually_resolved: true, + }), + createRandomScanResult({ + status: "optout_in_progress", + manually_resolved: true, + }), + createRandomScanResult({ status: "removed", manually_resolved: true }), + ]; + + const scanData: LatestOnerepScanData = { scan: null, results: [] }; + let scanCount = 0; + + if (props.countryCode === "us") { + if (props.brokers && props.brokers !== "no-scan") { + const scanInProgress = props.brokers === "scan-in-progress"; + scanData.scan = scanInProgress ? mockedScanInProgress : mockedScan; + + if (scanInProgress) { + scanCount = 1; + } + if (props.brokers === "resolved") { + scanData.results = mockedAllResolvedScanResults; + } + if (props.brokers === "unresolved") { + scanData.results = mockedUnresolvedScanResults; + } + + if (props.brokers === "manually-resolved") { + scanData.results = mockedManuallyResolvedScanResults; + } + } + } + + const user = createUserWithPremiumSubscription(); + if ((props.countryCode !== "us" || !props.premium) && user.fxa) { + user.fxa.subscriptions = []; + } + + const mockedSession = { + expires: new Date().toISOString(), + user: user, + }; + + const mockedRemovalTimeEstimates = scanData.results + .map((scan) => ({ + d: scan.data_broker, + t: faker.number.float({ min: 0, max: 200 }), + })) + .filter(() => Math.random() < 0.1); + + return ( + + + + + + + + ); +}; + +const meta: Meta = { + title: "Pages/Logged in/Dashboard/US User/No Plus", + component: DashboardWrapper, + argTypes: { + brokers: { + options: Object.keys(brokerOptions), + description: "Scan results", + control: { + type: "radio", + labels: brokerOptions, + }, + }, + breaches: { + options: Object.keys(breachOptions), + control: { + type: "radio", + labels: breachOptions, + }, + }, + elapsedTimeInDaysSinceInitialScan: { + name: "Days since initial scan", + control: { + type: "number", + }, + }, + hasFirstMonitoringScan: { + name: "Has first monitoring scan", + control: { + type: "boolean", + }, + }, + signInCount: { + name: "Sign-in count", + control: { + type: "number", + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const DashboardUsNoPremiumNoScanNoBreaches: Story = { + name: "US user, without Premium, without scan, with 0 breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "no-scan", + }, +}; + +export const DashboardUsNoPremiumNoScanUnresolvedBreaches: Story = { + name: "US user, without Premium, without scan, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "no-scan", + }, +}; + +export const DashboardUsNoPremiumNoScanResolvedBreaches: Story = { + name: "US user, without Premium, without scan, with all breaches resolved", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "no-scan", + }, +}; + +export const DashboardUsNoPremiumNoScanNoBreachesScanLimitReached: Story = { + name: "US user, without Premium, without scan, with 0 breaches, Scan limit reached", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "no-scan", + totalNumberOfPerformedScans: 280000, + }, +}; + +export const DashboardUsNoPremiumEmptyScanNoBreaches: Story = { + name: "US user, without Premium, with 0 scan results, with 0 breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "empty", + }, +}; + +export const DashboardUsNoPremiumEmptyScanUnresolvedBreaches: Story = { + name: "US user, without Premium, with 0 scan results, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "empty", + }, +}; + +export const DashboardUsNoPremiumEmptyScanResolvedBreaches: Story = { + name: "US user, without Premium, with 0 scan results, with all breaches resolved", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "empty", + }, +}; + +export const DashboardUsNoPremiumUnresolvedScanNoBreaches: Story = { + name: "US user, without Premium, with unresolved scan results, with 0 breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "unresolved", + }, +}; + +export const DashboardUsNoPremiumUnresolvedScanUnresolvedBreaches: Story = { + name: "US user, without Premium, with unresolved scan results, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "unresolved", + }, +}; + +export const DashboardUsNoPremiumUnresolvedScanResolvedBreaches: Story = { + name: "US user, without Premium, with unresolved scan results, with all breaches resolved", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "unresolved", + }, +}; + +export const DashboardUsNoPremiumResolvedScanNoBreaches: Story = { + name: "US user, without Premium, with all scan results resolved, with 0 breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "resolved", + }, +}; + +export const DashboardUsNoPremiumResolvedScanUnresolvedBreaches: Story = { + name: "US user, without Premium, with all scan results resolved, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "resolved", + }, +}; + +export const DashboardUsNoPremiumResolvedScanResolvedBreaches: Story = { + name: "US user, without Premium, with all scan results resolved, with all breaches resolved", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "resolved", + }, +}; + +export const DashboardUsNoPremiumScanInProgressNoBreaches: Story = { + name: "US user, without Premium, scan in progress, with no breaches", + args: { + countryCode: "us", + premium: false, + breaches: "empty", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsNoPremiumScanInProgressUnresolvedBreaches: Story = { + name: "US user, without Premium, scan in progress, with unresolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "unresolved", + brokers: "scan-in-progress", + }, +}; + +export const DashboardUsNoPremiumScanInProgressResolvedBreaches: Story = { + name: "US user, without Premium, scan in progress, with resolved breaches", + args: { + countryCode: "us", + premium: false, + breaches: "resolved", + brokers: "scan-in-progress", + }, +}; diff --git a/src/app/(proper_react)/(redesign)/(public)/MobileShell.stories.ts b/src/app/(proper_react)/(redesign)/(public)/MobileShell.stories.ts new file mode 100644 index 00000000000..cc0dda1dafe --- /dev/null +++ b/src/app/(proper_react)/(redesign)/(public)/MobileShell.stories.ts @@ -0,0 +1,45 @@ +/* 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 { Session } from "next-auth"; +import { SerializedSubscriber } from "../../../../next-auth"; + +import type { Meta, StoryObj } from "@storybook/react"; +import { MobileShell } from "../MobileShell"; + +function createUser(): Session["user"] { + return { + email: "example@example.com", + fxa: { + locale: "us", + twoFactorAuthentication: false, + metricsEnabled: false, + avatar: "https://profile.stage.mozaws.net/v1/avatar/e", + avatarDefault: true, + subscriptions: ["monitor"], + }, + subscriber: { + id: 42, + } as SerializedSubscriber, + }; +} + +const mockedSession = { + expires: new Date().toISOString(), + user: createUser(), +}; + +const meta: Meta = { + title: "Layout/Mobile Shell", + component: MobileShell, +}; +export default meta; +type Story = StoryObj; + +export const MobileShellStory: Story = { + args: { + countryCode: "us", + session: mockedSession, + }, +}; diff --git a/src/app/(proper_react)/(redesign)/(public)/PublicShell.stories.ts b/src/app/(proper_react)/(redesign)/(public)/PublicShell.stories.ts new file mode 100644 index 00000000000..87dd3f7ae6f --- /dev/null +++ b/src/app/(proper_react)/(redesign)/(public)/PublicShell.stories.ts @@ -0,0 +1,22 @@ +/* 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 { PublicShell } from "./PublicShell"; +import { getL10n } from "../../../functions/l10n/storybookAndJest"; + +const meta: Meta = { + title: "Layout/Public Shell", + component: PublicShell, +}; +export default meta; +type Story = StoryObj; + +export const PublicShellStory: Story = { + args: { + countryCode: "us", + l10n: getL10n("en"), + }, +}; diff --git a/src/app/(proper_react)/(redesign)/(public)/breaches/BreachIndexView.stories.tsx b/src/app/(proper_react)/(redesign)/(public)/breaches/BreachIndexView.stories.tsx index 683399bd1c3..de5b7cc67b2 100644 --- a/src/app/(proper_react)/(redesign)/(public)/breaches/BreachIndexView.stories.tsx +++ b/src/app/(proper_react)/(redesign)/(public)/breaches/BreachIndexView.stories.tsx @@ -8,7 +8,6 @@ import { BreachIndexView, Props as ViewProps } from "./BreachIndexView"; import { getL10n } from "../../../../functions/l10n/storybookAndJest"; import { PublicShell } from "../PublicShell"; import { createRandomHibpListing } from "../../../../../apiMocks/mockData"; -import { HibpLikeDbBreach } from "../../../../../utils/hibp"; const meta: Meta = { title: "Pages/Public/Breach index", @@ -25,11 +24,8 @@ type Story = StoryObj; export const BreachIndexViewStory: Story = { name: "Breach index", args: { - allBreaches: faker.helpers.multiple( - createRandomHibpListing as (v: unknown, index: number) => unknown, - { - count: { min: 7, max: 200 }, - }, - ) as unknown as HibpLikeDbBreach[], + allBreaches: faker.helpers.multiple(() => createRandomHibpListing(), { + count: { min: 7, max: 200 }, + }), }, }; diff --git a/src/app/components/client/exposure_card/ExposureCard.stories.tsx b/src/app/components/client/exposure_card/ExposureCard.stories.tsx index 12217af9483..4aac589328e 100644 --- a/src/app/components/client/exposure_card/ExposureCard.stories.tsx +++ b/src/app/components/client/exposure_card/ExposureCard.stories.tsx @@ -13,7 +13,7 @@ import { import { defaultExperimentData } from "../../../../telemetry/generated/nimbus/experiments"; const meta: Meta = { - title: "ExposureCard", + title: "Dashboard/Exposures/Exposure Card", component: ExposureCard, tags: ["autodocs"], args: { diff --git a/src/app/components/client/stories/Button.stories.ts b/src/app/components/client/stories/Button.stories.ts index c3c2e8f36d4..5f03aca8ef9 100644 --- a/src/app/components/client/stories/Button.stories.ts +++ b/src/app/components/client/stories/Button.stories.ts @@ -8,7 +8,7 @@ import { Button } from "../Button"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Button", + title: "Design Systems/Atoms/Button", component: Button, }; export default meta; diff --git a/src/app/components/client/stories/Chart.stories.ts b/src/app/components/client/stories/Chart.stories.ts index e1748c19058..b6153d0e78b 100644 --- a/src/app/components/client/stories/Chart.stories.ts +++ b/src/app/components/client/stories/Chart.stories.ts @@ -8,7 +8,7 @@ import { DoughnutChart } from "../Chart"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Charts", + title: "Dashboard/Top Banner/Charts", component: DoughnutChart, }; export default meta; diff --git a/src/app/components/client/stories/ComboBox.stories.tsx b/src/app/components/client/stories/ComboBox.stories.tsx index 2a5ed43d24c..14e33a10bf3 100644 --- a/src/app/components/client/stories/ComboBox.stories.tsx +++ b/src/app/components/client/stories/ComboBox.stories.tsx @@ -20,7 +20,7 @@ const items: Array = [ ]; const meta: Meta = { - title: "ComboBox", + title: "Design Systems/Atoms/ComboBox", component: ComboBox, }; export default meta; diff --git a/src/app/components/client/stories/CsatSurvey.stories.ts b/src/app/components/client/stories/CsatSurvey.stories.ts index 3e5453db77c..889fde8d679 100644 --- a/src/app/components/client/stories/CsatSurvey.stories.ts +++ b/src/app/components/client/stories/CsatSurvey.stories.ts @@ -9,7 +9,7 @@ import { createUserWithPremiumSubscription } from "../../../../apiMocks/mockData import { defaultExperimentData } from "../../../../telemetry/generated/nimbus/experiments"; const meta: Meta = { - title: "CsatSurvey", + title: "Misc/CsatSurvey", component: CsatSurvey, }; export default meta; diff --git a/src/app/components/client/stories/ExposuresFilter.stories.ts b/src/app/components/client/stories/ExposuresFilter.stories.ts index a7c0de228db..c64ed7a806c 100644 --- a/src/app/components/client/stories/ExposuresFilter.stories.ts +++ b/src/app/components/client/stories/ExposuresFilter.stories.ts @@ -7,7 +7,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { ExposuresFilter, FilterState } from "../ExposuresFilter"; const meta: Meta = { - title: "ExposuresFilter", + title: "Dashboard/Exposures/FilterBar", component: ExposuresFilter, }; export default meta; diff --git a/src/app/components/client/stories/InputField.stories.ts b/src/app/components/client/stories/InputField.stories.ts index 402684871c9..59e1a7666a1 100644 --- a/src/app/components/client/stories/InputField.stories.ts +++ b/src/app/components/client/stories/InputField.stories.ts @@ -7,7 +7,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { InputField } from "../InputField"; const meta: Meta = { - title: "InputField", + title: "Design Systems/Molecules/Input Field", component: InputField, }; export default meta; diff --git a/src/app/components/client/stories/PetitionBanner.stories.ts b/src/app/components/client/stories/PetitionBanner.stories.ts index 5a46986b6cb..177f13216ce 100644 --- a/src/app/components/client/stories/PetitionBanner.stories.ts +++ b/src/app/components/client/stories/PetitionBanner.stories.ts @@ -7,7 +7,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { PetitionBanner } from "../PetitionBanner"; const meta: Meta = { - title: "PetitionBanner", + title: "Misc/PetitionBanner", component: PetitionBanner, }; export default meta; diff --git a/src/app/components/client/stories/ProgressBar.stories.tsx b/src/app/components/client/stories/ProgressBar.stories.tsx index 64b1057e14f..9f065e91cf1 100644 --- a/src/app/components/client/stories/ProgressBar.stories.tsx +++ b/src/app/components/client/stories/ProgressBar.stories.tsx @@ -21,7 +21,7 @@ const ProgressBarDemo = (props: ProgressBarProps) => { // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Progress bar", + title: "Welcome Scan/ProgressBar", component: ProgressBarDemo, }; export default meta; diff --git a/src/app/components/client/stories/ProgressCard.stories.ts b/src/app/components/client/stories/ProgressCard.stories.ts index 4ca1cd80dc6..ddff6f5663d 100644 --- a/src/app/components/client/stories/ProgressCard.stories.ts +++ b/src/app/components/client/stories/ProgressCard.stories.ts @@ -8,7 +8,7 @@ import { ProgressCard } from "../ProgressCard"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "ProgressCard", + title: "Dashboard/Top Banner/Progress Infographic", component: ProgressCard, }; export default meta; diff --git a/src/app/components/client/stories/TabList.stories.ts b/src/app/components/client/stories/TabList.stories.ts index f2d18fd1907..d3d316e2b04 100644 --- a/src/app/components/client/stories/TabList.stories.ts +++ b/src/app/components/client/stories/TabList.stories.ts @@ -7,7 +7,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { TabList } from "../TabList"; const meta: Meta = { - title: "TabList", + title: "Design Systems/Molecules/TabList", component: TabList, }; export default meta; diff --git a/src/app/components/client/stories/UpsellCta.stories.tsx b/src/app/components/client/stories/UpsellCta.stories.tsx index 2cc9c70536d..801480cdf9e 100644 --- a/src/app/components/client/stories/UpsellCta.stories.tsx +++ b/src/app/components/client/stories/UpsellCta.stories.tsx @@ -58,7 +58,7 @@ const UpsellCtaWrapper = (props: UpsellCtaWrapperProps) => { }; const meta: Meta = { - title: "Upsell CTA", + title: "Design Systems/Atoms/Misc/Upsell CTA", component: UpsellCtaWrapper, }; export default meta; diff --git a/src/app/components/client/toolbar/AppPicker.stories.ts b/src/app/components/client/toolbar/AppPicker.stories.ts index 890875b7510..96b84c99ea8 100644 --- a/src/app/components/client/toolbar/AppPicker.stories.ts +++ b/src/app/components/client/toolbar/AppPicker.stories.ts @@ -6,7 +6,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { AppPicker } from "./AppPicker"; const meta: Meta = { - title: "Toolbar", + title: "Layout/Navigation/Bento App Picker", component: AppPicker, }; diff --git a/src/app/components/client/toolbar/UserMenu.stories.ts b/src/app/components/client/toolbar/UserMenu.stories.ts index 48cc9d057cb..5cee50c7349 100644 --- a/src/app/components/client/toolbar/UserMenu.stories.ts +++ b/src/app/components/client/toolbar/UserMenu.stories.ts @@ -6,7 +6,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { UserMenu } from "./UserMenu"; const meta: Meta = { - title: "Toolbar", + title: "Layout/Navigation/Toolbar", component: UserMenu, }; export default meta; diff --git a/src/app/components/server/stories/StatusPill.stories.ts b/src/app/components/server/stories/StatusPill.stories.ts index 7d78adf207f..e8a9b125f46 100644 --- a/src/app/components/server/stories/StatusPill.stories.ts +++ b/src/app/components/server/stories/StatusPill.stories.ts @@ -8,7 +8,7 @@ import { StatusPill } from "../StatusPill"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { - title: "Status Pill", + title: "Design Systems/Atoms/Pill", component: StatusPill, }; export default meta;