diff --git a/.storybook/main.ts b/.storybook/main.ts index 928d3d84..88c4c31d 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -20,22 +20,6 @@ export default defineConfig({ ], framework: '@storybook/nextjs-vite', staticDirs: ['../src/assets'], - // Inject base tag for manager (navigation/toolbar) when deploying to /_storybook/ - managerHead: - process.env.CHROMATIC !== 'true' - ? (head) => ` - ${head} - - ` - : undefined, - // Inject base tag for preview (iframe where components render) when deploying to /_storybook/ - previewHead: - process.env.CHROMATIC !== 'true' - ? (head) => ` - ${head} - - ` - : undefined, viteFinal: async (c, { configType }) => { // Dynamically import the plugin to avoid build issues if (!createMockResolverPlugin) { @@ -43,12 +27,12 @@ export default defineConfig({ createMockResolverPlugin = mockPlugin.createMockResolverPlugin } + const isProduction = configType === 'PRODUCTION' + const isChromatic = process.env.CHROMATIC === 'true' + return mergeConfig(c, { // Only set custom base path for production builds (not for Chromatic) - base: - configType === 'PRODUCTION' && process.env.CHROMATIC !== 'true' - ? '/_storybook/' - : c.base, + base: isProduction && !isChromatic ? '/_storybook/' : c.base, server: { allowedHosts: true, hmr: { clientPort: 443 }, diff --git a/components/AuthUI/AuthUI.stories.tsx b/components/AuthUI/AuthUI.stories.tsx new file mode 100644 index 00000000..d0731461 --- /dev/null +++ b/components/AuthUI/AuthUI.stories.tsx @@ -0,0 +1,91 @@ +import { Meta, StoryObj } from '@storybook/nextjs-vite' +import { HttpResponse, http } from 'msw' +import AuthUI from '@/components/AuthUI/AuthUI' +import { User } from '@/src/api/generated' +import { + mockFirebaseUser, + useFirebaseUser, +} from '@/src/hooks/useFirebaseUser.mock' +import { CAPI } from '@/src/mocks/apibase' +import { handlers } from '@/src/mocks/handlers' + +const meta = { + title: 'Components/AuthUI/SignIn', + component: AuthUI, + parameters: { + layout: 'fullscreen', + backgrounds: { default: 'dark' }, + msw: { + handlers: handlers, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +// Mock user data +const mockUser: User = { + id: 'user-123', + name: 'John Doe', + email: 'john.doe@example.com', + isAdmin: false, + isApproved: true, +} + +export const Default: Story = { + parameters: { + msw: { + handlers: [ + http.get(CAPI('/users'), () => + HttpResponse.json(null, { status: 401 }) + ), + ...handlers, + ], + }, + }, + async beforeEach() { + // Mock Firebase user as logged out + useFirebaseUser.mockReturnValue([null, false, undefined]) + }, +} + +export const Loading: Story = { + parameters: { + msw: { + handlers: [ + http.get(CAPI('/users'), () => + HttpResponse.json(null, { status: 401 }) + ), + ...handlers, + ], + }, + }, + async beforeEach() { + // Mock Firebase user as loading + useFirebaseUser.mockReturnValue([null, true, undefined]) + }, +} + +export const AlreadyLoggedIn: Story = { + parameters: { + msw: { + handlers: [ + http.get(CAPI('/users'), () => HttpResponse.json(mockUser)), + ...handlers, + ], + }, + }, + async beforeEach() { + // Mock Firebase user as logged in + useFirebaseUser.mockReturnValue([mockFirebaseUser, false, undefined]) + }, +} diff --git a/components/AuthUI/Logout.stories.tsx b/components/AuthUI/Logout.stories.tsx new file mode 100644 index 00000000..4eea9aa6 --- /dev/null +++ b/components/AuthUI/Logout.stories.tsx @@ -0,0 +1,74 @@ +import { Meta, StoryObj } from '@storybook/nextjs-vite' +import { HttpResponse, http } from 'msw' +import Logout from '@/components/AuthUI/Logout' +import { User } from '@/src/api/generated' +import { + mockFirebaseUser, + useFirebaseUser, +} from '@/src/hooks/useFirebaseUser.mock' +import { CAPI } from '@/src/mocks/apibase' +import { handlers } from '@/src/mocks/handlers' + +const meta = { + title: 'Components/AuthUI/Logout', + component: Logout, + parameters: { + layout: 'fullscreen', + backgrounds: { default: 'dark' }, + msw: { + handlers: handlers, + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +// Mock user data +const mockUser: User = { + id: 'user-123', + name: 'John Doe', + email: 'john.doe@example.com', + isAdmin: false, + isApproved: true, +} + +export const Default: Story = { + parameters: { + msw: { + handlers: [ + http.get(CAPI('/users'), () => HttpResponse.json(mockUser)), + ...handlers, + ], + }, + }, + async beforeEach() { + // Mock Firebase user as logged in + useFirebaseUser.mockReturnValue([mockFirebaseUser, false, undefined]) + }, +} + +export const LoggedOut: Story = { + parameters: { + msw: { + handlers: [ + http.get(CAPI('/users'), () => + HttpResponse.json(null, { status: 401 }) + ), + ...handlers, + ], + }, + }, + async beforeEach() { + // Mock Firebase user as logged out + useFirebaseUser.mockReturnValue([null, false, undefined]) + }, +} diff --git a/react-firebase-hooks/auth.mock.ts b/react-firebase-hooks/auth.mock.ts new file mode 100644 index 00000000..ff5a1a2a --- /dev/null +++ b/react-firebase-hooks/auth.mock.ts @@ -0,0 +1,20 @@ +import { vi } from 'vitest' + +// Mock the Firebase auth hooks for Storybook +export const useSignInWithGoogle = vi.fn().mockName('useSignInWithGoogle') +export const useSignInWithGithub = vi.fn().mockName('useSignInWithGithub') +export const useSignOut = vi.fn().mockName('useSignOut') +export const useAuthState = vi.fn().mockName('useAuthState') + +// Set default return values +useSignInWithGoogle.mockReturnValue([vi.fn(), undefined, false, undefined]) +useSignInWithGithub.mockReturnValue([vi.fn(), undefined, false, undefined]) +useSignOut.mockReturnValue([vi.fn(), false, undefined]) +useAuthState.mockReturnValue([null, false, undefined]) + +console.log('mocking react-firebase-hooks/auth', { + useSignInWithGoogle, + useSignInWithGithub, + useSignOut, + useAuthState, +}) diff --git a/src/hooks/useFirebaseUser.mock.ts b/src/hooks/useFirebaseUser.mock.ts index a940fcf9..74765e8f 100644 --- a/src/hooks/useFirebaseUser.mock.ts +++ b/src/hooks/useFirebaseUser.mock.ts @@ -1,4 +1,5 @@ -import { fn } from '@storybook/test' +import { User as FirebaseUser } from 'firebase/auth' +import { vi } from 'vitest' import * as actual from './useFirebaseUser' export * from './useFirebaseUser' @@ -24,8 +25,7 @@ export const mockFirebaseUser = { providerId: 'google', } satisfies FirebaseUser // Using 'as any' to avoid having to mock the entire Firebase User interface -export const useFirebaseUser = fn(actual.useFirebaseUser) +export const useFirebaseUser = vi + .fn(actual.useFirebaseUser) .mockName('useFirebaseUser') .mockReturnValue([mockFirebaseUser, false, undefined]) - -import { User as FirebaseUser } from 'firebase/auth' diff --git a/src/stories/Configure.mdx b/src/stories/Configure.mdx index f64fc581..2e3fd0d0 100644 --- a/src/stories/Configure.mdx +++ b/src/stories/Configure.mdx @@ -16,7 +16,7 @@ import Accessibility from './assets/accessibility.png' import Theming from './assets/theming.png' import AddonLibrary from './assets/addon-library.png' -export const RightArrow = () => ( +export const RightArrow = ( ( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
@@ -119,7 +119,7 @@ export const RightArrow = () => ( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
( Learn more + >Learn more{RightArrow}
@@ -208,7 +208,7 @@ export const RightArrow = () => ( Discover all addons + >Discover all addons{RightArrow}
( Star on GitHub + >Star on GitHub{RightArrow}
( Join Discord server + >Join Discord server{RightArrow}
@@ -270,7 +270,7 @@ export const RightArrow = () => ( Watch on YouTube + >Watch on YouTube{RightArrow}
@@ -287,7 +287,7 @@ export const RightArrow = () => ( Discover tutorials + >Discover tutorials{RightArrow}