providers.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { api } from '@/vdb/graphql/api.js';
  2. import {
  3. getSettingsStoreValueDocument,
  4. setSettingsStoreValueDocument,
  5. } from '@/vdb/graphql/settings-store-operations.js';
  6. import { useAuth } from '@/vdb/hooks/use-auth.js';
  7. import { AuthProvider } from '@/vdb/providers/auth.js';
  8. import { ChannelProvider } from '@/vdb/providers/channel-provider.js';
  9. import { I18nProvider } from '@/vdb/providers/i18n-provider.js';
  10. import { ThemeProvider } from '@/vdb/providers/theme-provider.js';
  11. import { UserSettingsProvider } from '@/vdb/providers/user-settings.js';
  12. import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
  13. import {
  14. AnyRoute,
  15. createMemoryHistory,
  16. createRootRoute,
  17. createRoute,
  18. createRouter,
  19. RouterProvider,
  20. } from '@tanstack/react-router';
  21. import { PropsWithChildren, useEffect } from 'react';
  22. // Initialize API mocks for Storybook
  23. mockUserSettingsApi();
  24. const queryClient = new QueryClient({
  25. defaultOptions: {
  26. queries: {
  27. staleTime: 1000 * 60 * 5,
  28. networkMode: 'offlineFirst',
  29. },
  30. },
  31. });
  32. /**
  33. * These providers are required for all stories and are set in the preview.tsx file
  34. * @param children
  35. * @constructor
  36. */
  37. export function CommonProviders({ children }: { children: React.ReactNode }) {
  38. return (
  39. <QueryClientProvider client={queryClient}>
  40. <UserSettingsProvider queryClient={queryClient}>
  41. <AuthProvider>
  42. <DemoAuthProvider>
  43. <I18nProvider>
  44. <ThemeProvider defaultTheme="light">
  45. <ChannelProvider>{children}</ChannelProvider>
  46. </ThemeProvider>
  47. </I18nProvider>
  48. </DemoAuthProvider>
  49. </AuthProvider>
  50. </UserSettingsProvider>
  51. </QueryClientProvider>
  52. );
  53. }
  54. /**
  55. * Required by some stories that need a Tanstack Router Route object
  56. */
  57. export function createDemoRoute(path?: string, initialPath?: string) {
  58. const rootRoute = createRootRoute();
  59. const route = createRoute({
  60. getParentRoute: () => rootRoute,
  61. path: path ?? 'test',
  62. component: () => <div>Test Route</div>,
  63. loader: () => ({ breadcrumb: 'Test' }),
  64. });
  65. const router = createRouter({
  66. routeTree: rootRoute.addChildren([route]),
  67. history: createMemoryHistory({
  68. initialEntries: [initialPath ?? '/test'],
  69. }),
  70. });
  71. return { route, router };
  72. }
  73. /**
  74. * A wrapper around components that need a Tanstack Router context
  75. */
  76. export function DemoRouterProvider(props: {
  77. path?: string;
  78. initialPath?: string;
  79. component: (route: AnyRoute) => React.ReactNode;
  80. }) {
  81. const rootRoute = createRootRoute();
  82. const route = createRoute({
  83. getParentRoute: () => rootRoute,
  84. path: props.path ?? 'test',
  85. component: () => props.component(route),
  86. loader: () => ({ breadcrumb: 'Test' }),
  87. });
  88. const router = createRouter({
  89. routeTree: rootRoute.addChildren([route]),
  90. history: createMemoryHistory({
  91. initialEntries: [props.initialPath ?? '/test'],
  92. }),
  93. });
  94. return <RouterProvider router={router} />;
  95. }
  96. /**
  97. * Provides a logged in superadmin user
  98. */
  99. export function DemoAuthProvider({ children }: PropsWithChildren) {
  100. const { login, isAuthenticated } = useAuth();
  101. const username = import.meta.env.VITE_ADMIN_USERNAME ?? 'admin';
  102. const password = import.meta.env.VITE_ADMIN_PASSWORD ?? 'admin';
  103. useEffect(() => {
  104. if (!isAuthenticated) {
  105. login(username, password);
  106. }
  107. }, [isAuthenticated]);
  108. return children;
  109. }
  110. /**
  111. * Mocks the user settings API calls to prevent unnecessary network requests in Storybook.
  112. *
  113. * This function intercepts GraphQL queries and mutations for user settings by monkey-patching
  114. * the `api.query` and `api.mutate` functions. It provides instant mock responses for:
  115. *
  116. * 1. **GetSettingsStoreValue query**: Returns mock user settings data
  117. * 2. **SetSettingsStoreValue mutation**: Returns a mock success response
  118. *
  119. * All other GraphQL operations are passed through to the original implementation unchanged.
  120. *
  121. * @remarks
  122. * This is necessary because the UserSettingsProvider uses `staleTime: 0` in its useQuery,
  123. * which would cause the query to refetch on every story render. By mocking at the API level,
  124. * we avoid these unnecessary network calls while still allowing the provider to function
  125. * normally for demonstration purposes.
  126. */
  127. function mockUserSettingsApi() {
  128. // Mock user settings data
  129. const mockUserSettings = {
  130. displayLanguage: 'en',
  131. contentLanguage: 'en',
  132. theme: 'system',
  133. displayUiExtensionPoints: false,
  134. mainNavExpanded: true,
  135. activeChannelId: '1',
  136. devMode: false,
  137. hasSeenOnboarding: false,
  138. tableSettings: {},
  139. };
  140. // Mock the query function to intercept GetSettingsStoreValue
  141. const originalQuery = api.query.bind(api);
  142. api.query = ((document: any, variables?: any) => {
  143. // Intercept the user settings query and return mock data immediately
  144. if (
  145. document === getSettingsStoreValueDocument &&
  146. variables?.key === 'vendure.dashboard.userSettings'
  147. ) {
  148. return Promise.resolve({ getSettingsStoreValue: mockUserSettings });
  149. }
  150. // Pass through all other queries to the original implementation
  151. return originalQuery(document, variables);
  152. }) as typeof api.query;
  153. // Mock the mutate function to intercept SetSettingsStoreValue
  154. const originalMutate = api.mutate.bind(api);
  155. api.mutate = ((document: any, variables?: any) => {
  156. // Intercept the user settings mutation and return mock success response
  157. if (
  158. document === setSettingsStoreValueDocument &&
  159. variables?.input?.key === 'vendure.dashboard.userSettings'
  160. ) {
  161. return Promise.resolve({
  162. setSettingsStoreValue: {
  163. key: 'vendure.dashboard.userSettings',
  164. result: true,
  165. error: null,
  166. },
  167. });
  168. }
  169. // Pass through all other mutations to the original implementation
  170. return originalMutate(document, variables);
  171. }) as typeof api.mutate;
  172. }