veza/apps/web/.storybook/decorators.tsx

59 lines
2.1 KiB
TypeScript

/**
* Global Storybook decorator: single point of entry for all app providers.
* No story should import or wrap with these providers directly; they are applied here.
* Stories that need a specific route can set parameters.router.initialEntries.
*
* This ensures useAuth, useNavigate, useSearchParams, useQueryClient, useToast
* and similar hooks never run without context (no "must be used within XProvider" crashes).
*/
import React from 'react';
import type { Decorator } from '@storybook/react';
import { cn } from '../src/lib/utils';
import { ThemeProvider } from '../src/components/theme/ThemeProvider';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { LazyToaster } from '../src/components/feedback/LazyToaster';
import { AudioProvider } from '../src/context/AudioContext';
import { AuthProvider } from '../src/providers/AuthProvider';
import { I18nextProvider } from 'react-i18next';
import i18n from '../src/lib/i18n';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: Infinity,
throwOnError: false,
},
},
});
export const StorybookDecorator: Decorator = (Story, context) => {
const isDark = context.globals?.backgrounds?.value !== '#ffffff';
const initialEntries =
(context.parameters?.router as { initialEntries?: string[] } | undefined)?.initialEntries ?? ['/'];
return (
<I18nextProvider i18n={i18n}>
<ThemeProvider defaultTheme={isDark ? 'dark' : 'light'}>
<div
className={cn(
isDark ? 'dark' : '',
'min-h-layout-story min-h-screen p-4 bg-background text-foreground',
)}
>
<QueryClientProvider client={queryClient}>
<LazyToaster position="top-right" />
<AudioProvider>
<AuthProvider>
<MemoryRouter initialEntries={initialEntries}>
<Story />
</MemoryRouter>
</AuthProvider>
</AudioProvider>
</QueryClientProvider>
</div>
</ThemeProvider>
</I18nextProvider>
);
};