|
| 1 | +//@ts-check |
| 2 | +/** |
| 3 | + *@file Defines the main configuration file for all of our Storybook tests. |
| 4 | + * This file must be a JSX/JS file, but we can at least add some type safety via |
| 5 | + * the ts-check directive. |
| 6 | + *@see {@link https://storybook.js.org/docs/configure#configure-story-rendering} |
| 7 | + * |
| 8 | + *@typedef {import("react").ReactElement} ReactElement |
| 9 | + *@typedef {import("react").PropsWithChildren} PropsWithChildren |
| 10 | + *@typedef {import("react").FC<PropsWithChildren>} FC |
| 11 | + * |
| 12 | + *@typedef {import("@storybook/react").StoryContext} StoryContext |
| 13 | + *@typedef {import("@storybook/react").Preview} Preview |
| 14 | + * |
| 15 | + *@typedef {(Story: FC, Context: StoryContext) => React.JSX.Element} Decorator A |
| 16 | + * Storybook decorator function used to inject baseline data dependencies into |
| 17 | + * our React components during testing. |
| 18 | + */ |
| 19 | +import{ThemeProviderasEmotionThemeProvider}from"@emotion/react"; |
1 | 20 | importCssBaselinefrom"@mui/material/CssBaseline";
|
2 | 21 | import{
|
3 |
| -StyledEngineProvider, |
4 |
| -ThemeProviderasMuiThemeProvider, |
| 22 | +ThemeProviderasMuiThemeProvider, |
| 23 | +StyledEngineProvider, |
5 | 24 | }from"@mui/material/styles";
|
6 |
| -import{ThemeProviderasEmotionThemeProvider}from"@emotion/react"; |
7 | 25 | import{DecoratorHelpers}from"@storybook/addon-themes";
|
8 |
| -import{withRouter}from"storybook-addon-remix-react-router"; |
9 |
| -import{StrictMode}from"react"; |
10 |
| -import{parseQueryArgs,QueryClient,QueryClientProvider}from"react-query"; |
| 26 | +importisChromaticfrom"chromatic/isChromatic"; |
| 27 | +importReact,{StrictMode}from"react"; |
11 | 28 | import{HelmetProvider}from"react-helmet-async";
|
12 |
| -importthemesfrom"theme"; |
| 29 | +import{parseQueryArgs,QueryClient,QueryClientProvider}from"react-query"; |
| 30 | +import{withRouter}from"storybook-addon-remix-react-router"; |
13 | 31 | import"theme/globalFonts";
|
14 |
| -importisChromaticfrom"chromatic/isChromatic"; |
| 32 | +importthemesfrom"../src/theme"; |
15 | 33 |
|
16 | 34 | DecoratorHelpers.initializeThemeState(Object.keys(themes),"dark");
|
17 | 35 |
|
18 |
| -exportconstdecorators=[ |
19 |
| -withRouter, |
20 |
| -withQuery, |
21 |
| -(Story)=>{ |
22 |
| -return( |
23 |
| -<HelmetProvider> |
24 |
| -<Story/> |
25 |
| -</HelmetProvider> |
26 |
| -); |
27 |
| -}, |
28 |
| -(Story,context)=>{ |
29 |
| -constselectedTheme=DecoratorHelpers.pluckThemeFromContext(context); |
30 |
| -const{ themeOverride}=DecoratorHelpers.useThemeParameters(); |
31 |
| -constselected=themeOverride||selectedTheme||"dark"; |
32 |
| - |
33 |
| -return( |
34 |
| -<StrictMode> |
35 |
| -<StyledEngineProviderinjectFirst> |
36 |
| -<MuiThemeProvidertheme={themes[selected]}> |
37 |
| -<EmotionThemeProvidertheme={themes[selected]}> |
38 |
| -<CssBaseline/> |
39 |
| -<Story/> |
40 |
| -</EmotionThemeProvider> |
41 |
| -</MuiThemeProvider> |
42 |
| -</StyledEngineProvider> |
43 |
| -</StrictMode> |
44 |
| -); |
45 |
| -}, |
46 |
| -]; |
| 36 | +/**@type {readonly Decorator[]} */ |
| 37 | +exportconstdecorators=[withRouter,withQuery,withHelmet,withTheme]; |
47 | 38 |
|
| 39 | +/**@type {Preview["parameters"]} */ |
48 | 40 | exportconstparameters={
|
49 |
| -options:{ |
50 |
| -storySort:{ |
51 |
| -method:"alphabetical", |
52 |
| -order:["design","pages","modules","components"], |
53 |
| -locales:"en-US", |
54 |
| -}, |
55 |
| -}, |
56 |
| -controls:{ |
57 |
| -expanded:true, |
58 |
| -matchers:{ |
59 |
| -color:/(background|color)$/i, |
60 |
| -date:/Date$/, |
61 |
| -}, |
62 |
| -}, |
63 |
| -viewport:{ |
64 |
| -viewports:{ |
65 |
| -ipad:{ |
66 |
| -name:"iPad Mini", |
67 |
| -styles:{ |
68 |
| -height:"1024px", |
69 |
| -width:"768px", |
70 |
| -}, |
71 |
| -type:"tablet", |
72 |
| -}, |
73 |
| -terminal:{ |
74 |
| -name:"Terminal", |
75 |
| -styles:{ |
76 |
| -height:"400", |
77 |
| -width:"400", |
78 |
| -}, |
79 |
| -}, |
80 |
| -}, |
81 |
| -}, |
| 41 | +options:{ |
| 42 | +storySort:{ |
| 43 | +method:"alphabetical", |
| 44 | +order:["design","pages","modules","components"], |
| 45 | +locales:"en-US", |
| 46 | +}, |
| 47 | +}, |
| 48 | +controls:{ |
| 49 | +expanded:true, |
| 50 | +matchers:{ |
| 51 | +color:/(background|color)$/i, |
| 52 | +date:/Date$/, |
| 53 | +}, |
| 54 | +}, |
| 55 | +viewport:{ |
| 56 | +viewports:{ |
| 57 | +ipad:{ |
| 58 | +name:"iPad Mini", |
| 59 | +styles:{ |
| 60 | +height:"1024px", |
| 61 | +width:"768px", |
| 62 | +}, |
| 63 | +type:"tablet", |
| 64 | +}, |
| 65 | +terminal:{ |
| 66 | +name:"Terminal", |
| 67 | +styles:{ |
| 68 | +height:"400", |
| 69 | +width:"400", |
| 70 | +}, |
| 71 | +}, |
| 72 | +}, |
| 73 | +}, |
82 | 74 | };
|
83 | 75 |
|
| 76 | +/** |
| 77 | + * There's a mismatch on the React Helmet return type that causes issues when |
| 78 | + * mounting the component in JS files only. Have to do type assertion, which is |
| 79 | + * especially ugly in JSDoc |
| 80 | + */ |
| 81 | +constSafeHelmetProvider=/**@type {FC} */( |
| 82 | +/**@type {unknown} */(HelmetProvider) |
| 83 | +); |
| 84 | + |
| 85 | +/**@type {Decorator} */ |
| 86 | +functionwithHelmet(Story){ |
| 87 | +return( |
| 88 | +<SafeHelmetProvider> |
| 89 | +<Story/> |
| 90 | +</SafeHelmetProvider> |
| 91 | +); |
| 92 | +} |
| 93 | + |
| 94 | +/**@type {Decorator} */ |
84 | 95 | functionwithQuery(Story,{ parameters}){
|
85 |
| -constqueryClient=newQueryClient({ |
86 |
| -defaultOptions:{ |
87 |
| -queries:{ |
88 |
| -staleTime:Infinity, |
89 |
| -retry:false, |
90 |
| -}, |
91 |
| -}, |
92 |
| -}); |
| 96 | +constqueryClient=newQueryClient({ |
| 97 | +defaultOptions:{ |
| 98 | +queries:{ |
| 99 | +staleTime:Number.POSITIVE_INFINITY, |
| 100 | +retry:false, |
| 101 | +}, |
| 102 | +}, |
| 103 | +}); |
| 104 | + |
| 105 | +if(parameters.queries){ |
| 106 | +for(constqueryofparameters.queries){ |
| 107 | +if(query.datainstanceofError){ |
| 108 | +// This is copied from setQueryData() but sets the error. |
| 109 | +constcache=queryClient.getQueryCache(); |
| 110 | +constparsedOptions=parseQueryArgs(query.key); |
| 111 | +constdefaultedOptions=queryClient.defaultQueryOptions(parsedOptions); |
| 112 | +constcachedQuery=cache.build(queryClient,defaultedOptions); |
| 113 | +// Set manual data so react-query will not try to refetch. |
| 114 | +cachedQuery.setData(undefined,{manual:true}); |
| 115 | +cachedQuery.setState({error:query.data}); |
| 116 | +}else{ |
| 117 | +queryClient.setQueryData(query.key,query.data); |
| 118 | +} |
| 119 | +} |
| 120 | +} |
| 121 | + |
| 122 | +return( |
| 123 | +<QueryClientProviderclient={queryClient}> |
| 124 | +<Story/> |
| 125 | +</QueryClientProvider> |
| 126 | +); |
| 127 | +} |
93 | 128 |
|
94 |
| -if(parameters.queries){ |
95 |
| -parameters.queries.forEach((query)=>{ |
96 |
| -if(query.datainstanceofError){ |
97 |
| -// This is copied from setQueryData() but sets the error. |
98 |
| -constcache=queryClient.getQueryCache(); |
99 |
| -constparsedOptions=parseQueryArgs(query.key) |
100 |
| -constdefaultedOptions=queryClient.defaultQueryOptions(parsedOptions) |
101 |
| -constcachedQuery=cache.build(queryClient,defaultedOptions); |
102 |
| -// Set manual data so react-query will not try to refetch. |
103 |
| -cachedQuery.setData(undefined,{manual:true}); |
104 |
| -cachedQuery.setState({error:query.data}); |
105 |
| -}else{ |
106 |
| -queryClient.setQueryData(query.key,query.data); |
107 |
| -} |
108 |
| -}); |
109 |
| -} |
| 129 | +/**@type {Decorator} */ |
| 130 | +functionwithTheme(Story,context){ |
| 131 | +constselectedTheme=DecoratorHelpers.pluckThemeFromContext(context); |
| 132 | +const{ themeOverride}=DecoratorHelpers.useThemeParameters(); |
| 133 | +constselected=themeOverride||selectedTheme||"dark"; |
110 | 134 |
|
111 |
| -return( |
112 |
| -<QueryClientProviderclient={queryClient}> |
113 |
| -<Story/> |
114 |
| -</QueryClientProvider> |
115 |
| -); |
| 135 | +return( |
| 136 | +<StrictMode> |
| 137 | +<StyledEngineProviderinjectFirst> |
| 138 | +<MuiThemeProvidertheme={themes[selected]}> |
| 139 | +<EmotionThemeProvidertheme={themes[selected]}> |
| 140 | +<CssBaseline/> |
| 141 | +<Story/> |
| 142 | +</EmotionThemeProvider> |
| 143 | +</MuiThemeProvider> |
| 144 | +</StyledEngineProvider> |
| 145 | +</StrictMode> |
| 146 | +); |
116 | 147 | }
|
117 | 148 |
|
118 | 149 | // Try to fix storybook rendering fonts inconsistently
|
119 | 150 | // https://www.chromatic.com/docs/font-loading/#solution-c-check-fonts-have-loaded-in-a-loader
|
120 | 151 | constfontLoader=async()=>({
|
121 |
| -fonts:awaitdocument.fonts.ready, |
| 152 | +fonts:awaitdocument.fonts.ready, |
122 | 153 | });
|
123 | 154 |
|
124 | 155 | exportconstloaders=isChromatic()&&document.fonts ?[fontLoader] :[];
|