11// .storybook/preview.tsx
2- import addonDocs from "@storybook/addon-docs" ;
32import { definePreview } from '@storybook/nextjs-vite'
43import { Provider as WrapBalancer } from 'react-wrap-balancer'
5- import addonLinks from '@storybook/addon-links'
6- import addonThemes from '@storybook/addon-themes'
7- import addonA11y from '@storybook/addon-a11y'
4+ import clsx from 'clsx'
85
96import '@fontsource/roboto/300.css'
107import '@fontsource/roboto/400.css'
@@ -23,20 +20,29 @@ import '../src/theme/styles.css'
2320import { theme } from '../src/theme/theme'
2421
2522/**
26- * Optional Jest results. If the file doesn’ t exist, we pass `undefined`
23+ * Optional Jest results. If the file doesn' t exist, we pass `undefined`
2724 * so the addon silently does nothing (no runtime error).
28- * Vite will inline JSON when present .
25+ * Using dynamic import with error handling for better compatibility .
2926 */
30- // @ts -ignore – file may not exist in CI/locally
31- import testResults from '../test-results.json?url'
27+ let testResults : any = undefined
28+ try {
29+ // @ts -ignore – file may not exist in CI/locally
30+ const testResultsModule = await import ( '../test-results.json' )
31+ testResults = testResultsModule . default || testResultsModule
32+ } catch {
33+ // Silently ignore if file doesn't exist
34+ testResults = undefined
35+ }
36+
37+ // Viewport presets for both Storybook viewport addon and Chromatic
38+ const viewportPresets = {
39+ mobile : { name : 'mobile' , styles : { width : '375px' , height : '667px' } } ,
40+ tablet : { name : 'tablet' , styles : { width : '768px' , height : '1024px' } } ,
41+ desktop : { name : 'desktop' , styles : { width : '1280px' , height : '720px' } } ,
42+ wide : { name : 'wide' , styles : { width : '1920px' , height : '1080px' } } ,
43+ }
3244
3345const preview = definePreview ( {
34- addons : [
35- addonLinks ( ) ,
36- addonThemes ( ) ,
37- addonA11y ( ) ,
38- addonDocs ( )
39- ] ,
4046 parameters : {
4147 controls : {
4248 expanded : true ,
@@ -45,6 +51,11 @@ const preview = definePreview({
4551 date : / D a t e $ / i,
4652 } ,
4753 } ,
54+ // Viewport configuration for Storybook viewport addon
55+ viewport : {
56+ viewports : viewportPresets ,
57+ } ,
58+ // Chromatic viewports (using same presets)
4859 chromatic : {
4960 delay : 1000 ,
5061 diffThreshold : 0.1 ,
@@ -55,6 +66,10 @@ const preview = definePreview({
5566 { name : 'wide' , width : 1920 , height : 1080 } ,
5667 ] ,
5768 } ,
69+ // Coverage results for addon-coverage
70+ ...( testResults && { coverageResults : testResults } ) ,
71+ // Default layout for page components
72+ layout : 'fullscreen' ,
5873 backgrounds : {
5974 default : 'light' ,
6075 options : {
@@ -84,7 +99,7 @@ const preview = definePreview({
8499 } ,
85100
86101 decorators : [
87- // Show jest results if available
102+ // Theme and provider decorator
88103 ( Story , context ) => {
89104 const forcedTheme =
90105 context . globals . theme === 'system' ? undefined : ( context . globals . theme as 'light' | 'dark' )
@@ -97,15 +112,43 @@ const preview = definePreview({
97112 < MuiThemeProvider theme = { theme } >
98113 < LocalizationProvider dateAdapter = { AdapterDayjs } >
99114 < WrapBalancer >
100- < div className = "w-full max-w-2xl px-4 py-6" >
101- < Story />
102- </ div >
115+ < Story />
103116 </ WrapBalancer >
104117 </ LocalizationProvider >
105118 </ MuiThemeProvider >
106119 </ NextThemeProvider >
107120 ) ;
108121 } ,
122+ // Container decorator - only applies when container parameter is true
123+ // This allows components to opt-in to container wrapping instead of defaulting to it
124+ ( Story , context ) => {
125+ const shouldUseContainer =
126+ context . parameters . container === true ||
127+ ( context . parameters . layout !== 'fullscreen' &&
128+ context . parameters . fullscreen !== true &&
129+ context . parameters . container !== false )
130+
131+ // If explicitly set to false or fullscreen, don't wrap
132+ if ( context . parameters . container === false ||
133+ context . parameters . layout === 'fullscreen' ||
134+ context . parameters . fullscreen === true ) {
135+ return < Story />
136+ }
137+
138+ // Only apply container if explicitly requested
139+ if ( ! shouldUseContainer ) {
140+ return < Story />
141+ }
142+
143+ const containerWidth = context . parameters . containerWidth || 'max-w-2xl'
144+ const containerPadding = context . parameters . containerPadding || 'px-4 py-6'
145+
146+ return (
147+ < div className = { clsx ( 'w-full' , containerWidth , containerPadding ) } >
148+ < Story />
149+ </ div >
150+ )
151+ } ,
109152 ] ,
110153
111154 tags : [ ] ,
0 commit comments