-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreact-router-stub.tsx
More file actions
101 lines (88 loc) · 3.48 KB
/
react-router-stub.tsx
File metadata and controls
101 lines (88 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { Command } from '@lambdacurry/forms/ui';
import type { Decorator } from '@storybook/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { NuqsAdapter } from 'nuqs/adapters/react-router/v7';
import type { ComponentType } from 'react';
import { DayPickerProvider } from 'react-day-picker';
import {
type ActionFunction,
type IndexRouteObject,
type LinksFunction,
type LoaderFunction,
type MetaFunction,
type NonIndexRouteObject,
RouterProvider,
createMemoryRouter,
} from 'react-router-dom';
export type StubRouteObject = StubIndexRouteObject | StubNonIndexRouteObject;
interface StubNonIndexRouteObject
extends Omit<NonIndexRouteObject, 'loader' | 'action' | 'element' | 'errorElement' | 'children'> {
loader?: LoaderFunction;
action?: ActionFunction;
children?: StubRouteObject[];
meta?: MetaFunction;
links?: LinksFunction;
// biome-ignore lint/suspicious/noExplicitAny: allow any here
Component?: ComponentType<any>;
}
interface StubIndexRouteObject
extends Omit<IndexRouteObject, 'loader' | 'action' | 'element' | 'errorElement' | 'children'> {
loader?: LoaderFunction;
action?: ActionFunction;
children?: StubRouteObject[];
meta?: MetaFunction;
links?: LinksFunction;
// biome-ignore lint/suspicious/noExplicitAny: allow any here
Component?: ComponentType<any>;
}
interface RemixStubOptions {
routes: StubRouteObject[];
initialPath?: string;
}
// Create a single QueryClient instance outside the decorator
const queryClient = new QueryClient();
export const withReactRouterStubDecorator = (options: RemixStubOptions): Decorator => {
const { routes, initialPath = '/' } = options;
// This outer function runs once when Storybook loads the story meta
return (Story, context) => {
// This inner function runs when the story component actually renders
const mappedRoutes = routes.map((route) => ({
...route,
Component: route.Component ?? (() => <Story {...context.args} />),
}));
// Get the base path (without existing query params from options)
const basePath = initialPath.split('?')[0];
// Get the current search string from the actual browser window, if available
// If not available, use a default search string with parameters needed for the data table
const currentWindowSearch = typeof window !== 'undefined' ? window.location.search : '?page=0&pageSize=10';
// Combine them for the initial entry
const actualInitialPath = `${basePath}${currentWindowSearch}`;
// Create a memory router, initializing it with the path derived from the window's search params
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const router = createMemoryRouter(mappedRoutes as any, {
initialEntries: [actualInitialPath], // Use the path combined with window.location.search
});
// Wrap existing providers with QueryClientProvider and DayPickerProvider
return (
<QueryClientProvider client={queryClient}>
<DayPickerProvider initialProps={{}}>
<NuqsAdapter>
<Command>
<RouterProvider router={router} />
</Command>
</NuqsAdapter>
</DayPickerProvider>
</QueryClientProvider>
);
};
};
/**
* A decorator that provides URL state management for stories
* Use this when you need URL query parameters in your stories
*/
export const withURLState = (initialPath = '/'): Decorator => {
return withReactRouterStubDecorator({
routes: [{ path: '/' }],
initialPath,
});
};