11import React from "react" ;
22import type { Preview } from "@storybook/react-vite" ;
3- import { ThemeProvider , type ThemeMode } from "../src/browser/contexts/ThemeContext" ;
4- import { getStorageChangeEvent } from "@/common/constants/events" ;
3+ import {
4+ ThemeProvider ,
5+ type ThemeMode ,
6+ useTheme ,
7+ } from "../src/browser/contexts/ThemeContext" ;
58import isChromatic from "chromatic/isChromatic" ;
69import { UI_THEME_KEY } from "@/common/constants/storage" ;
10+ import { updatePersistedState } from "@/browser/hooks/usePersistedState" ;
711import "../src/browser/styles/globals.css" ;
812
13+ const DEFAULT_THEME : ThemeMode = "light" ;
14+
915const isAutomatedChromaticRun = ( ) => {
1016 if ( typeof window === "undefined" || ! isChromatic ( ) ) {
1117 return false ;
@@ -30,65 +36,45 @@ const isE2ETestEnv = () => {
3036 return false ;
3137} ;
3238
33- const applyStorybookTheme = ( mode : ThemeMode ) => {
39+ const getPersistedTheme = ( ) : ThemeMode | undefined => {
3440 if ( typeof window === "undefined" ) {
35- return ;
41+ return undefined ;
3642 }
3743
3844 try {
39- const serialized = JSON . stringify ( mode ) ;
40- const prev = window . localStorage . getItem ( UI_THEME_KEY ) ;
41- const root = document . documentElement ;
42- const current = root ?. dataset ?. theme as ThemeMode | undefined ;
43- if ( prev === serialized && current === mode ) {
44- return ; // no-op if already applied
45- }
46-
47- window . localStorage . setItem ( UI_THEME_KEY , serialized ) ;
48- root . dataset . theme = mode ;
49- root . style . colorScheme = mode ;
50-
51- const event = new CustomEvent ( getStorageChangeEvent ( UI_THEME_KEY ) , {
52- detail : { key : UI_THEME_KEY , newValue : mode , origin : "storybook" } ,
53- } ) ;
54- window . dispatchEvent ( event ) ;
45+ const stored = window . localStorage . getItem ( UI_THEME_KEY ) ;
46+ return stored ? ( JSON . parse ( stored ) as ThemeMode ) : undefined ;
5547 } catch ( error ) {
56- console . warn ( "Failed to write Storybook theme:" , error ) ;
48+ console . warn ( "Failed to read Storybook theme:" , error ) ;
49+ return undefined ;
5750 }
5851} ;
5952
6053const syncStorybookTheme = ( mode ?: ThemeMode ) : ThemeMode => {
6154 if ( typeof window === "undefined" ) {
62- return mode ?? "light" ; // Keep default aligned with baseline to avoid Chromatic diffs
55+ return mode ?? DEFAULT_THEME ; // Keep default aligned with baseline to avoid Chromatic diffs
6356 }
6457
65- const stored = window . localStorage . getItem ( UI_THEME_KEY ) ;
66- const persisted = stored ? ( JSON . parse ( stored ) as ThemeMode ) : undefined ;
67- const resolved = mode ?? persisted ?? "light" ; // Default to light for Latest when unset
68- applyStorybookTheme ( resolved ) ;
69- return resolved ;
70- } ;
58+ const persisted = getPersistedTheme ( ) ;
59+ const resolved = mode ?? persisted ?? DEFAULT_THEME ; // Default to light for Latest when unset
7160
72- const StorybookThemeToggle : React . FC < { initialTheme : ThemeMode } > = ( { initialTheme } ) => {
73- const [ theme , setTheme ] = React . useState ( initialTheme ) ;
61+ if ( resolved !== persisted ) {
62+ updatePersistedState ( UI_THEME_KEY , resolved ) ;
63+ }
7464
75- React . useEffect ( ( ) => {
76- setTheme ( initialTheme ) ;
77- } , [ initialTheme ] ) ;
65+ return resolved ;
66+ } ;
7867
79- const handleToggle = ( ) => {
80- const next = theme === "dark" ? "light" : "dark" ;
81- applyStorybookTheme ( next ) ;
82- setTheme ( next ) ;
83- } ;
68+ const StorybookThemeToggle : React . FC = ( ) => {
69+ const { theme, toggleTheme } = useTheme ( ) ;
8470
8571 if ( typeof window === "undefined" ) {
8672 return null ;
8773 }
8874
8975 return (
9076 < button
91- onClick = { handleToggle }
77+ onClick = { toggleTheme }
9278 style = { {
9379 position : "fixed" ,
9480 bottom : "1rem" ,
@@ -128,13 +114,11 @@ const preview: Preview = {
128114 decorators : [
129115 ( Story , context ) => {
130116 const mode = context . globals . theme as ThemeMode | undefined ;
131- const resolved = syncStorybookTheme ( mode ) ;
117+ syncStorybookTheme ( mode ) ;
132118 return (
133119 < ThemeProvider >
134120 < Story />
135- { ! mode && ! isE2ETestEnv ( ) && (
136- < StorybookThemeToggle initialTheme = { resolved } />
137- ) }
121+ { ! mode && ! isE2ETestEnv ( ) && < StorybookThemeToggle /> }
138122 </ ThemeProvider >
139123 ) ;
140124 } ,
0 commit comments