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 = (