From ca442924c3f6e7a97c3ce08e108044077dc24401 Mon Sep 17 00:00:00 2001 From: janicelichtman Date: Fri, 1 May 2026 13:58:02 -0400 Subject: [PATCH 1/3] added storybook stories for phone verification modal and verify account section --- components/shared/PhoneVerificationModal.tsx | 10 +++- .../PhoneVerificationModal.stories.tsx | 57 +++++++++++++++++++ .../VerifyAccountSection.stories.tsx | 43 ++++++++++++++ 3 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 stories/organisms/PhoneVerificationModal.stories.tsx create mode 100644 stories/organisms/VerifyAccountSection.stories.tsx diff --git a/components/shared/PhoneVerificationModal.tsx b/components/shared/PhoneVerificationModal.tsx index e966d2e3c..9c49e8f75 100644 --- a/components/shared/PhoneVerificationModal.tsx +++ b/components/shared/PhoneVerificationModal.tsx @@ -31,13 +31,17 @@ const AUTH_ERROR_CODE_TO_KEY: Record = { export function PhoneVerificationModal({ show, onHide, - onVerified -}: Pick & { onVerified?: () => void }) { + onVerified, + initialStep = "phone" +}: Pick & { + onVerified?: () => void + initialStep?: "phone" | "code" | "success" +}) { const { t } = useTranslation("auth") const { user } = useAuth() const completePhoneVerification = useCompletePhoneVerification() - const [step, setStep] = useState<"phone" | "code" | "success">("phone") + const [step, setStep] = useState<"phone" | "code" | "success">(initialStep) const [phone, setPhone] = useState("") const [code, setCode] = useState("") const [error, setError] = useState(null) diff --git a/stories/organisms/PhoneVerificationModal.stories.tsx b/stories/organisms/PhoneVerificationModal.stories.tsx new file mode 100644 index 000000000..b22fc21b3 --- /dev/null +++ b/stories/organisms/PhoneVerificationModal.stories.tsx @@ -0,0 +1,57 @@ +import { Meta, StoryObj } from "@storybook/react" +import { PhoneVerificationModal } from "components/shared/PhoneVerificationModal" +import { Providers } from "components/providers" +import { wrapper } from "components/store" +import { Provider as Redux } from "react-redux" + +const meta: Meta = { + title: "Organisms/PhoneVerificationModal", + component: PhoneVerificationModal, + decorators: [ + (Story, ...rest) => { + const { store } = wrapper.useWrappedStore(...rest) + + return ( + + + + + + ) + } + ] +} + +export default meta + +type Story = StoryObj + +export const PhoneStep: Story = { + args: { + show: true, + onHide: () => {}, + onVerified: () => {}, + initialStep: "phone" + }, + name: "Phone Step" +} + +export const CodeStep: Story = { + args: { + show: true, + onHide: () => {}, + onVerified: () => {}, + initialStep: "code" + }, + name: "Code Step" +} + +export const SuccessStep: Story = { + args: { + show: true, + onHide: () => {}, + onVerified: () => {}, + initialStep: "success" + }, + name: "Success Step" +} diff --git a/stories/organisms/VerifyAccountSection.stories.tsx b/stories/organisms/VerifyAccountSection.stories.tsx new file mode 100644 index 000000000..f2d7d3bb5 --- /dev/null +++ b/stories/organisms/VerifyAccountSection.stories.tsx @@ -0,0 +1,43 @@ +import { Meta, StoryObj } from "@storybook/react" +import { User } from "firebase/auth" +import { VerifyAccountSection } from "components/shared/VerifyAccountSection" +import { Providers } from "components/providers" +import { wrapper } from "components/store" +import { Provider as Redux } from "react-redux" + +const meta: Meta = { + title: "Organisms/VerifyAccountSection", + component: VerifyAccountSection, + decorators: [ + (Story, ...rest) => { + const { store, props } = wrapper.useWrappedStore(...rest) + + return ( + + + + + + ) + } + ] +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = { + args: { + user: { emailVerified: false } as User, + profile: { + topicName: "Your Topic Name", + role: "user", + fullName: "Matt Doe", + email: "anotherMatt@example.com", + phoneVerified: false + }, + className: "" + }, + name: "VerifyAccountSection" +} From 0ddc2749cfd0e63f093b11ea3efa0090e38204a6 Mon Sep 17 00:00:00 2001 From: janicelichtman Date: Tue, 5 May 2026 15:11:34 -0400 Subject: [PATCH 2/3] added storybook story for ProfilePage --- .storybook/main.js | 8 +++ stories/__mocks__/db/profile.ts | 85 +++++++++++++++++++++++++ stories/pages/ProfilePage.stories.tsx | 91 +++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 stories/__mocks__/db/profile.ts create mode 100644 stories/pages/ProfilePage.stories.tsx diff --git a/.storybook/main.js b/.storybook/main.js index 650f398a2..1f6a7cbf7 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -24,6 +24,14 @@ module.exports = { use: ["file-loader"] }) config.resolve.fallback = { fs: false, path: false } + const path = require("path") + const webpack = require("webpack") + config.plugins.push( + new webpack.NormalModuleReplacementPlugin( + /components\/db\/profile\/profile(\.tsx?)?$/, + path.resolve(__dirname, "../stories/__mocks__/db/profile.ts") + ) + ) return config }, diff --git a/stories/__mocks__/db/profile.ts b/stories/__mocks__/db/profile.ts new file mode 100644 index 000000000..f0b3ddb0d --- /dev/null +++ b/stories/__mocks__/db/profile.ts @@ -0,0 +1,85 @@ +import { Profile } from "components/db/profile/types" + +export const mockProfile: Profile = { + topicName: "Environment, Education", + role: "user", + fullName: "Sol R. Panels", + email: "iveseenthelight@example.com", + representative: { + id: "rep-123", + name: "Polly Tician", + district: "5th Middlesex" + }, + senator: { + id: "sen-123", + name: "Phil E. Buster", + district: "Middlesex and Suffolk" + }, + public: true, + about: "Environmental advocate based in Cambridge, MA.", + location: "Cambridge, MA", + phoneVerified: false +} + +export function usePublicProfile(_uid?: string, _verifyisorg?: boolean) { + return { + result: mockProfile, + loading: false, + error: undefined, + execute: async () => mockProfile, + currentPromise: null, + currentParams: null, + status: "success" as const + } +} + +const noop = async () => {} + +export function useProfile() { + return { + loading: false, + profile: mockProfile, + updatingRep: false, + updatingSenator: false, + updatingIsPublic: false, + updatingNotification: false, + updatingIsOrganization: false, + updatingAbout: false, + updatingFullName: false, + updatingProfileImage: false, + updatingOrgCategory: false, + updatingBillsFollowing: false, + updatingContactInfo: { publicEmail: false, publicPhone: false, website: false }, + updatingSocial: { + linkedIn: false, + twitter: false, + instagram: false, + fb: false, + blueSky: false, + mastodon: false + }, + updateSenator: noop, + updateRep: noop, + updateIsPublic: noop, + updateNotification: noop, + updateIsOrganization: noop, + updateAbout: noop, + updateFullName: noop, + updateProfileImage: noop, + updateSocial: noop, + updateContactInfo: noop, + updateOrgCategory: noop, + updateBillsFollowing: noop + } +} + +export type ProfileHook = ReturnType + +export function useUser() {} + +export const profileRef = (_uid: string) => ({} as any) +export const profileImageRef = (_uid: string) => ({} as any) +export const profileImageUrl = async (_uid: string) => "" +export const updateProfileImage = noop +export const setProfile = noop +export const getProfile = async (_uid: string) => mockProfile diff --git a/stories/pages/ProfilePage.stories.tsx b/stories/pages/ProfilePage.stories.tsx new file mode 100644 index 000000000..4a2323a6f --- /dev/null +++ b/stories/pages/ProfilePage.stories.tsx @@ -0,0 +1,91 @@ +import { Meta, StoryObj } from "@storybook/react" +import { configureStore } from "@reduxjs/toolkit" +import { User } from "firebase/auth" +import { RouterContext } from "next/dist/shared/lib/router-context.shared-runtime" +import { NextRouter } from "next/router" +import { Provider as Redux } from "react-redux" +import { reducer as auth } from "components/auth/redux" +import { api } from "components/db/api" +import { reducer as profile } from "components/db/profile/redux" +import { reducer as publish } from "components/publish/redux" +import { slice as testimonyDetail } from "components/testimony/TestimonyDetailPage" +import { ProfilePage } from "components/ProfilePage" + +const PROFILE_UID = "mock-profile-uid" + +const mockRouter = { + basePath: "", + pathname: "/profile/[id]", + route: "/profile/[id]", + query: { phoneVerificationUI: "enabled" }, + asPath: `/profile/${PROFILE_UID}`, + push: async () => true, + replace: async () => true, + reload: () => {}, + back: () => {}, + forward: () => {}, + prefetch: async () => undefined, + beforePopState: () => null, + events: { on: () => {}, off: () => {}, emit: () => {} }, + isFallback: false, + isLocaleDomain: false, + isPreview: false, + isReady: true, + locale: "en", + locales: ["en"], + defaultLocale: "en", + domainLocales: [] +} as unknown as NextRouter + +const createMockStore = (userUid: string, emailVerified: boolean) => + configureStore({ + reducer: { + [api.reducerPath]: api.reducer, + auth, + profile, + publish, + [testimonyDetail.name]: testimonyDetail.reducer + }, + preloadedState: { + auth: { + user: { uid: userUid, emailVerified } as unknown as User, + claims: { role: "user" as const }, + authenticated: true, + authFlowStep: null, + loading: false + } + }, + middleware: getDefaultMiddleware => + getDefaultMiddleware({ serializableCheck: false }).concat(api.middleware) + }) + +const meta: Meta = { + title: "Pages/ProfilePage", + component: ProfilePage +} + +export default meta + +type Story = StoryObj + +export const OwnProfile: Story = { + render: () => ( + + + + + + ), + name: "Own Profile" +} + +export const AnothersProfile: Story = { + render: () => ( + + + + + + ), + name: "Another's Profile" +} From dcfc1aeeeefa7eef755ba0f90c70545017e4bc6f Mon Sep 17 00:00:00 2001 From: janicelichtman Date: Tue, 5 May 2026 15:37:58 -0400 Subject: [PATCH 3/3] fixing formatting issue --- stories/__mocks__/db/profile.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stories/__mocks__/db/profile.ts b/stories/__mocks__/db/profile.ts index f0b3ddb0d..accb9c5be 100644 --- a/stories/__mocks__/db/profile.ts +++ b/stories/__mocks__/db/profile.ts @@ -49,7 +49,11 @@ export function useProfile() { updatingProfileImage: false, updatingOrgCategory: false, updatingBillsFollowing: false, - updatingContactInfo: { publicEmail: false, publicPhone: false, website: false }, + updatingContactInfo: { + publicEmail: false, + publicPhone: false, + website: false + }, updatingSocial: { linkedIn: false, twitter: false,