@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/index.d.ts +7 -0
  3. package/dist/index.esm.js +18 -1
  4. package/dist/index.js +18 -1
  5. package/dist/setupTests.d.ts +124 -0
  6. package/dist/setupTests.esm.js +122 -0
  7. package/dist/setupTests.js +122 -0
  8. package/dist/styles.css +1 -1
  9. package/package.json +1 -1
  10. package/src/index.css +1046 -0
  11. package/src/index.ts +18 -0
  12. package/src/plugins/theme-css-generator.ts +354 -0
  13. package/src/setupTests.ts +124 -0
  14. package/src/stories/README.md +39 -0
  15. package/src/stories/components/ThemeDebugger.tsx +143 -0
  16. package/src/stories/index.ts +29 -0
  17. package/src/stories/storybook-theme-imports.css +51 -0
  18. package/src/styles/base/fonts.css +30 -0
  19. package/src/styles/base/generated-theme-variables.css +573 -0
  20. package/src/styles/base/index.css +7 -0
  21. package/src/styles/base/reset.css +48 -0
  22. package/src/styles/base/theme.css +1068 -0
  23. package/src/styles/base/typography.css +68 -0
  24. package/src/styles/base/variables.css +5 -0
  25. package/src/styles/components/CLAUDE.md +62 -0
  26. package/src/styles/components/base/badge.css +428 -0
  27. package/src/styles/components/base/button.css +774 -0
  28. package/src/styles/components/base/card.css +601 -0
  29. package/src/styles/components/base/checkbox.css +442 -0
  30. package/src/styles/components/base/index.css +9 -0
  31. package/src/styles/components/base/input.css +887 -0
  32. package/src/styles/components/base/label.css +296 -0
  33. package/src/styles/components/data-display/chart.css +353 -0
  34. package/src/styles/components/data-display/data-grid.css +619 -0
  35. package/src/styles/components/data-display/index.css +9 -0
  36. package/src/styles/components/data-display/list.css +560 -0
  37. package/src/styles/components/data-display/table.css +498 -0
  38. package/src/styles/components/data-display/timeline.css +764 -0
  39. package/src/styles/components/data-display/tree.css +881 -0
  40. package/src/styles/components/feedback/alert.css +358 -0
  41. package/src/styles/components/feedback/index.css +7 -0
  42. package/src/styles/components/feedback/progress.css +435 -0
  43. package/src/styles/components/feedback/skeleton.css +337 -0
  44. package/src/styles/components/feedback/toast.css +564 -0
  45. package/src/styles/components/index.css +17 -0
  46. package/src/styles/components/navigation/breadcrumb.css +465 -0
  47. package/src/styles/components/navigation/index.css +9 -0
  48. package/src/styles/components/navigation/menu.css +572 -0
  49. package/src/styles/components/navigation/pagination.css +635 -0
  50. package/src/styles/components/navigation/sidebar.css +807 -0
  51. package/src/styles/components/navigation/stepper.css +519 -0
  52. package/src/styles/components/navigation/tabs.css +404 -0
  53. package/src/styles/components/overlay/backdrop.css +243 -0
  54. package/src/styles/components/overlay/index.css +8 -0
  55. package/src/styles/components/overlay/modal.css +482 -0
  56. package/src/styles/components/overlay/popover.css +607 -0
  57. package/src/styles/components/overlay/portal.css +213 -0
  58. package/src/styles/components/overlay/tooltip.css +488 -0
  59. package/src/styles/generated-theme-variables.css +573 -0
  60. package/src/styles/index.css +5 -0
  61. package/src/styles/layers/index.css +54 -0
  62. package/src/styles/layers/overrides.css +108 -0
  63. package/src/styles/layers/validation.css +159 -0
  64. package/src/styles/layers/validation.js +310 -0
  65. package/src/styles/themes/default.css +450 -0
  66. package/src/styles/themes/enterprise.css +370 -0
  67. package/src/styles/themes/harvey.css +436 -0
  68. package/src/styles/themes/index.css +4 -0
  69. package/src/styles/themes/stan-design.css +572 -0
  70. package/src/styles/utilities/advanced-transition-system.css +467 -0
  71. package/src/styles/utilities/battery-conscious-animations.css +289 -0
  72. package/src/styles/utilities/enterprise-mobile-experience.css +817 -0
  73. package/src/styles/utilities/hardware-acceleration.css +121 -0
  74. package/src/styles/utilities/index.css +20 -0
  75. package/src/styles/utilities/mobile-skeleton-loading.css +596 -0
  76. package/src/styles/utilities/semantic-input-system.css +451 -0
  77. package/src/styles/utilities/touch-friendly-interface.css +247 -0
  78. package/src/styles/utilities/touch-optimization.css +165 -0
  79. package/src/test-utils/index.ts +7 -0
  80. package/src/test-utils/theme-testing.tsx +219 -0
  81. package/src/testing/test-automation.ts +627 -0
  82. package/src/testing/test-utils.tsx +367 -0
package/src/index.ts CHANGED
@@ -9,6 +9,11 @@
9
9
  * - 30 theme system components
10
10
  * - 131 TypeScript types
11
11
  * - 4 design tokens
12
+ * - 0 build plugins
13
+ * - 0 test utilities
14
+ * - 2 testing tools
15
+ * - 0 development tools
16
+ * - Complete CSS system (59+ files)
12
17
  */
13
18
 
14
19
  // UI Components
@@ -169,6 +174,19 @@ export { TokenGenerator } from './tokens/tokenGenerator';
169
174
  export { TokenManager } from './tokens/tokenManager';
170
175
  export { TokenValidator } from './tokens/tokenValidator';
171
176
 
177
+ // Build Plugins
178
+
179
+
180
+ // Test Utilities
181
+
182
+
183
+ // Testing Framework
184
+ export { TestAutomation } from './testing/test-automation';
185
+ export { TestCLI } from './testing/test-automation';
186
+
187
+ // Development Tools
188
+
189
+
172
190
  // Types
173
191
  export type { NavigationBaseProps } from './components/ui/navigation/types';
174
192
  export type { NavigationItem } from './components/ui/navigation/types';
@@ -0,0 +1,354 @@
1
+ import { resolve } from 'path'
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs'
3
+ import type { Plugin } from 'vite'
4
+ import type { MultiThemeConfig } from '../themes/types'
5
+ import { defaultThemes } from '../themes/base-themes'
6
+
7
+ export default function themeCSSGenerator(): Plugin {
8
+ let config: any
9
+
10
+ // Helper function to safely convert values to strings
11
+ const valueToString = (value: any): string => {
12
+ if (Array.isArray(value)) {
13
+ return value.join(', ')
14
+ }
15
+ if (typeof value === 'object' && value !== null) {
16
+ return JSON.stringify(value)
17
+ }
18
+ return String(value)
19
+ }
20
+
21
+ // Helper function to create CSS variable name
22
+ const createCSSVarName = (path: string[]): string => {
23
+ return `--cs-${path.join('-').replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`)}`
24
+ }
25
+
26
+ // Generate @font-face declarations from font configuration
27
+ const generateFontFaces = (themeObj: MultiThemeConfig): string => {
28
+ let css = ''
29
+
30
+ if (!themeObj.fonts) return css
31
+
32
+ Object.entries(themeObj.fonts).forEach(([, fontConfig]) => {
33
+ if (fontConfig.source?.type === 'custom' && fontConfig.source.files?.woff2) {
34
+ const { family, source, weights, display } = fontConfig
35
+
36
+ if (weights && Array.isArray(weights) && weights.length > 0) {
37
+ const minWeight = Math.min(...weights)
38
+ const maxWeight = Math.max(...weights)
39
+
40
+ css += `\n@font-face {\n`
41
+ css += ` font-family: '${family}';\n`
42
+ css += ` src: url('${source.files.woff2}') format('woff2-variations');\n`
43
+ css += ` font-weight: ${minWeight} ${maxWeight};\n`
44
+ css += ` font-display: ${display || 'swap'};\n`
45
+ css += `}\n`
46
+ }
47
+ }
48
+ })
49
+
50
+ return css
51
+ }
52
+
53
+ // NEW: Extract breakpoints from anywhere in the theme object
54
+ const extractBreakpoints = (obj: any): any => {
55
+ let breakpoints: any = {}
56
+
57
+ const searchForBreakpoints = (currentObj: any, currentPath: string[] = []) => {
58
+ if (typeof currentObj !== 'object' || currentObj === null) {
59
+ return
60
+ }
61
+
62
+ Object.entries(currentObj).forEach(([key, value]) => {
63
+ if (key === 'breakpoints' && typeof value === 'object' && value !== null) {
64
+ // Found breakpoints, extract them
65
+ Object.entries(value).forEach(([bpKey, bpValue]) => {
66
+ if (typeof bpValue === 'string' || typeof bpValue === 'number') {
67
+ breakpoints[bpKey] = bpValue
68
+ }
69
+ })
70
+ } else if (typeof value === 'object' && value !== null) {
71
+ // Recursively search deeper
72
+ searchForBreakpoints(value, [...currentPath, key])
73
+ }
74
+ })
75
+ }
76
+
77
+ searchForBreakpoints(obj)
78
+ return breakpoints
79
+ }
80
+
81
+ // NEW: Generate breakpoint CSS variables and @custom-media queries
82
+ const generateBreakpointVariables = (breakpoints: any): string => {
83
+ let css = ''
84
+ if (Object.keys(breakpoints).length === 0) { return css }
85
+
86
+ css += ` /* Breakpoint Variables */\n`
87
+ Object.entries(breakpoints).forEach(([key, value]) => {
88
+ css += ` --cs-breakpoints-${key}: ${value};\n`
89
+ })
90
+ css += '\n'
91
+
92
+ // NEW: Generate @custom-media queries for use in media queries
93
+ css += ` /* Custom Media Queries for Breakpoints */\n`
94
+ Object.entries(breakpoints).forEach(([key, value]) => {
95
+ css += ` @custom-media --bp-${key} (min-width: ${value});\n`
96
+ })
97
+ css += '\n'
98
+
99
+ return css
100
+ }
101
+
102
+ // Recursively generate CSS variables from theme object
103
+ const generateCSSVariables = (obj: any, path: string[] = []): string => {
104
+ let css = ''
105
+
106
+ if (typeof obj !== 'object' || obj === null) {
107
+ return css
108
+ }
109
+
110
+ Object.entries(obj).forEach(([key, value]) => {
111
+ const currentPath = [...path, key]
112
+
113
+ if (typeof value === 'string' || typeof value === 'number') {
114
+ // Generate CSS variable for primitive values
115
+ const cssVarName = createCSSVarName(currentPath)
116
+ css += ` ${cssVarName}: ${value};\n`
117
+ } else if (Array.isArray(value)) {
118
+ // Handle arrays (like font weights, tags)
119
+ const cssVarName = createCSSVarName(currentPath)
120
+ css += ` ${cssVarName}: ${valueToString(value)};\n`
121
+ } else if (typeof value === 'object' && value !== null) {
122
+ // Recursively process nested objects
123
+ css += generateCSSVariables(value, currentPath)
124
+ }
125
+ })
126
+
127
+ return css
128
+ }
129
+
130
+ // Generate CSS from theme object using structured traversal
131
+ const generateSingleThemeCSS = (themeName: string, themeObj: MultiThemeConfig): string => {
132
+ // Always generate light + dark mode CSS
133
+ return generateLightDarkCSS(themeName, themeObj)
134
+ }
135
+
136
+ // Generate light and dark mode CSS
137
+ const generateLightDarkCSS = (themeName: string, themeObj: MultiThemeConfig): string => {
138
+ let css = `/* ${themeName} Theme - Light & Dark Modes */\n`
139
+
140
+ // Generate @font-face declarations
141
+ css += generateFontFaces(themeObj)
142
+
143
+ // NEW: Extract breakpoints from anywhere in the theme
144
+ const breakpoints = extractBreakpoints(themeObj)
145
+
146
+ // Generate light mode variables (default)
147
+ css += `:root {\n`
148
+ css += ` /* Light Mode Variables */\n`
149
+
150
+ // NEW: Generate breakpoint variables first
151
+ css += generateBreakpointVariables(breakpoints)
152
+
153
+ // Generate all other CSS variables
154
+ css += generateCSSVariables(themeObj)
155
+ css += '}\n\n'
156
+
157
+ // Generate dark mode variables
158
+ css += `.dark {\n`
159
+ css += ` /* Dark Mode Variables */\n`
160
+
161
+ if (themeObj.modes?.dark?.colors && Object.keys(themeObj.modes.dark.colors).length > 0) {
162
+ // Use custom dark mode colors if defined
163
+ css += generateDarkModeColors(themeObj.modes.dark.colors)
164
+ } else {
165
+ // Generate automatic dark mode by adapting colors
166
+ css += generateDarkModeVariables(themeObj)
167
+ }
168
+
169
+ css += '}\n'
170
+ return css
171
+ }
172
+
173
+ // Generate dark mode colors from explicit color definitions
174
+ const generateDarkModeColors = (darkColors: any): string => {
175
+ let css = ''
176
+
177
+ Object.entries(darkColors).forEach(([colorKey, colorValue]) => {
178
+ if (typeof colorValue === 'object' && colorValue !== null) {
179
+ // Handle nested color objects (like surface, text, etc.)
180
+ Object.entries(colorValue).forEach(([subKey, subValue]) => {
181
+ if (typeof subValue === 'string' || typeof subValue === 'number') {
182
+ css += ` --cs-colors-${colorKey}-${subKey}: ${subValue};\n`
183
+ }
184
+ })
185
+ } else if (typeof colorValue === 'string' || typeof colorValue === 'number') {
186
+ // Handle direct color values
187
+ css += ` --cs-colors-${colorKey}: ${colorValue};\n`
188
+ }
189
+ })
190
+
191
+ return css
192
+ }
193
+
194
+ // Generate dark mode variables by adapting light mode colors
195
+ const generateDarkModeVariables = (themeObj: MultiThemeConfig): string => {
196
+ // Create a dark variant of the theme by adapting colors
197
+ const darkTheme = adaptThemeForDarkMode(themeObj)
198
+
199
+ // Generate CSS variables only for the properties that exist in the dark theme
200
+ let css = ''
201
+
202
+ if (darkTheme.colors) {
203
+ // Generate CSS variables for colors
204
+ Object.entries(darkTheme.colors).forEach(([colorKey, colorValue]) => {
205
+ if (typeof colorValue === 'object' && colorValue !== null) {
206
+ // Handle nested color objects (like surface, text, etc.)
207
+ Object.entries(colorValue).forEach(([subKey, subValue]) => {
208
+ if (typeof subValue === 'string' || typeof subValue === 'number') {
209
+ css += ` --cs-colors-${colorKey}-${subKey}: ${subValue};\n`
210
+ }
211
+ })
212
+ } else if (typeof colorValue === 'string' || typeof colorValue === 'number') {
213
+ // Handle direct color values
214
+ css += ` --cs-colors-${colorKey}: ${colorValue};\n`
215
+ }
216
+ })
217
+ }
218
+
219
+ // Add some basic dark mode variables for testing
220
+ css += ` --cs-colors-surface-background: #0f172a;\n`
221
+ css += ` --cs-colors-surface-surface: #1e293b;\n`
222
+ css += ` --cs-colors-surface-border: #334155;\n`
223
+ css += ` --cs-colors-surface-divider: #475569;\n`
224
+ css += ` --cs-colors-text-primary: #f8fafc;\n`
225
+ css += ` --cs-colors-text-secondary: #cbd5e1;\n`
226
+ css += ` --cs-colors-text-muted: #94a3b8;\n`
227
+
228
+ return css
229
+ }
230
+
231
+ // Adapt theme for dark mode
232
+ const adaptThemeForDarkMode = (_themeObj: MultiThemeConfig): Partial<MultiThemeConfig> => {
233
+ // This would implement your dark mode color adaptation logic
234
+ // You can use the existing ColorManager or implement custom logic
235
+ return {
236
+ colors: {
237
+ surface: {
238
+ background: '#0f172a', // Dark background
239
+ surface: '#1e293b', // Dark surface
240
+ border: '#334155', // Dark border
241
+ divider: '#475569' // Dark divider
242
+ },
243
+ text: {
244
+ primary: '#f8fafc', // Light text
245
+ secondary: '#cbd5e1', // Muted text
246
+ muted: '#94a3b8', // Muted text
247
+ inverse: '#0f172a', // Dark text for light backgrounds
248
+ onPrimary: '#0f172a', // Text on primary color
249
+ onSecondary: '#0f172a', // Text on secondary color
250
+ onSurface: '#f8fafc' // Text on surface
251
+ }
252
+ // ... other color adaptations
253
+ } as any // Type assertion to bypass strict typing for now
254
+ }
255
+ }
256
+
257
+ // Generate CSS for all themes using structured approach
258
+ const generateAllThemesCSS = () => {
259
+ try {
260
+ // Create themes directory if it doesn't exist
261
+ const themesDir = resolve(config.root, 'src/styles/themes')
262
+ if (!existsSync(themesDir)) {
263
+ mkdirSync(themesDir, { recursive: true })
264
+ }
265
+
266
+ let indexCSS = '/* Theme Index - Import all themes */\n'
267
+ let defaultThemeCSS = '/* Default Theme Variables (Coach-Stan) */\n'
268
+
269
+ // Use defaultThemes instead of hardcoded themeFiles
270
+ for (const [themeKey, themeObj] of Object.entries(defaultThemes)) {
271
+ try {
272
+ // Generate CSS for this specific theme
273
+ const themeCSS = generateSingleThemeCSS(themeObj.meta.name, themeObj)
274
+
275
+ // Write individual theme file
276
+ const themeFileName = `${themeKey}.css`
277
+ const themeOutputPath = resolve(themesDir, themeFileName)
278
+ writeFileSync(themeOutputPath, themeCSS, 'utf-8')
279
+
280
+ console.log(`✅ Generated dual-mode CSS for theme: ${themeObj.meta.name} -> ${themeFileName}`)
281
+
282
+ // Add import to index file
283
+ indexCSS += `@import './${themeFileName}';\n`
284
+
285
+ // Set stan-design theme as default
286
+ if (themeKey === 'stan-design') {
287
+ defaultThemeCSS += themeCSS
288
+ }
289
+
290
+ } catch (error) {
291
+ console.error(`❌ Error processing ${themeKey}:`, error)
292
+ }
293
+ }
294
+
295
+ // Write theme index file
296
+ const indexPath = resolve(config.root, 'src/styles/themes/index.css')
297
+ writeFileSync(indexPath, indexCSS, 'utf-8')
298
+
299
+ // Write default theme file (for backward compatibility)
300
+ const defaultPath = resolve(config.root, 'src/styles/generated-theme-variables.css')
301
+ writeFileSync(defaultPath, defaultThemeCSS, 'utf-8')
302
+
303
+ console.log('✅ Theme CSS Generator: Generated all dual-mode theme files successfully')
304
+
305
+ } catch (error) {
306
+ console.error('❌ Theme CSS Generator Error:', error)
307
+ }
308
+ }
309
+
310
+ // Main function to generate theme CSS
311
+ const generateThemeCSS = () => {
312
+ try {
313
+ // Check if themes directory exists
314
+ const themesSourceDir = resolve(config.root, 'src/themes/themes')
315
+
316
+ if (!existsSync(themesSourceDir)) {
317
+ console.warn('⚠️ Theme CSS Generator: themes/themes/ directory not found')
318
+ return
319
+ }
320
+
321
+ // Generate CSS for all themes
322
+ generateAllThemesCSS()
323
+
324
+ } catch (error) {
325
+ console.error('❌ Theme CSS Generator Error:', error)
326
+ }
327
+ }
328
+
329
+ return {
330
+ name: 'theme-css-generator',
331
+
332
+ configResolved(resolvedConfig) {
333
+ config = resolvedConfig
334
+ },
335
+
336
+ buildStart() {
337
+ generateThemeCSS()
338
+ },
339
+
340
+ handleHotUpdate({ file }) {
341
+ // Use defaultThemes keys instead of hardcoded file paths
342
+ const themeKeys = Object.keys(defaultThemes)
343
+ const shouldRegenerate = themeKeys.some(themeKey =>
344
+ file.includes(`themes/${themeKey}.ts`) ||
345
+ file.includes('inheritance.ts') ||
346
+ file.includes('types.ts')
347
+ )
348
+
349
+ if (shouldRegenerate) {
350
+ generateThemeCSS()
351
+ }
352
+ }
353
+ }
354
+ }
@@ -0,0 +1,124 @@
1
+ import '@testing-library/jest-dom';
2
+
3
+ // Mock localStorage for Jest environment
4
+ const localStorageMock = {
5
+ getItem: jest.fn(),
6
+ setItem: jest.fn(),
7
+ removeItem: jest.fn(),
8
+ clear: jest.fn(),
9
+ length: 0,
10
+ key: jest.fn(),
11
+ };
12
+
13
+ Object.defineProperty(window, 'localStorage', {
14
+ value: localStorageMock,
15
+ writable: true,
16
+ });
17
+
18
+ // Mock sessionStorage for Jest environment
19
+ const sessionStorageMock = {
20
+ getItem: jest.fn(),
21
+ setItem: jest.fn(),
22
+ removeItem: jest.fn(),
23
+ clear: jest.fn(),
24
+ length: 0,
25
+ key: jest.fn(),
26
+ };
27
+
28
+ Object.defineProperty(window, 'sessionStorage', {
29
+ value: sessionStorageMock,
30
+ writable: true,
31
+ });
32
+
33
+ // Mock performance API for Jest environment
34
+ Object.defineProperty(window, 'performance', {
35
+ value: {
36
+ now: jest.fn(() => Date.now()),
37
+ memory: {
38
+ usedJSHeapSize: 1000000,
39
+ totalJSHeapSize: 2000000,
40
+ jsHeapSizeLimit: 3000000,
41
+ },
42
+ },
43
+ writable: true,
44
+ });
45
+
46
+ // Mock ResizeObserver for Jest environment
47
+ global.ResizeObserver = jest.fn().mockImplementation(() => ({
48
+ observe: jest.fn(),
49
+ unobserve: jest.fn(),
50
+ disconnect: jest.fn(),
51
+ }));
52
+
53
+ // Mock IntersectionObserver for Jest environment
54
+ global.IntersectionObserver = jest.fn().mockImplementation(() => ({
55
+ observe: jest.fn(),
56
+ unobserve: jest.fn(),
57
+ disconnect: jest.fn(),
58
+ }));
59
+
60
+ // Mock React 18 createRoot for Jest environment
61
+ const mockCreateRoot = jest.fn(() => ({
62
+ render: jest.fn(),
63
+ unmount: jest.fn(),
64
+ }));
65
+
66
+ Object.defineProperty(window, 'createRoot', {
67
+ value: mockCreateRoot,
68
+ writable: true,
69
+ });
70
+
71
+ // Suppress console warnings for expected test behavior
72
+ const originalWarn = console.warn;
73
+ const originalError = console.error;
74
+
75
+ beforeAll(() => {
76
+ // Suppress specific warnings that are expected in tests
77
+ console.warn = jest.fn((...args) => {
78
+ const message = args[0];
79
+ if (
80
+ typeof message === 'string' && (
81
+ message.includes('Failed to read theme from storage') ||
82
+ message.includes('Failed to store theme preference') ||
83
+ message.includes('Failed to read system theme preference from storage')
84
+ )
85
+ ) {
86
+ return; // Suppress expected storage warnings
87
+ }
88
+ originalWarn(...args);
89
+ });
90
+
91
+ // Suppress specific errors that are expected in tests
92
+ console.error = jest.fn((...args) => {
93
+ const message = args[0];
94
+ if (
95
+ typeof message === 'string' && (
96
+ message.includes('Warning: An update to ThemeProvider inside a test was not wrapped in act') ||
97
+ message.includes('createRoot') ||
98
+ message.includes('Target container is not a DOM element')
99
+ )
100
+ ) {
101
+ return; // Suppress expected React act warnings and createRoot errors
102
+ }
103
+ originalError(...args);
104
+ });
105
+ });
106
+
107
+ afterAll(() => {
108
+ // Restore original console methods
109
+ console.warn = originalWarn;
110
+ console.error = originalError;
111
+ });
112
+
113
+ // Reset mocks before each test
114
+ beforeEach(() => {
115
+ localStorageMock.getItem.mockClear();
116
+ localStorageMock.setItem.mockClear();
117
+ localStorageMock.removeItem.mockClear();
118
+ localStorageMock.clear.mockClear();
119
+
120
+ sessionStorageMock.getItem.mockClear();
121
+ sessionStorageMock.setItem.mockClear();
122
+ sessionStorageMock.removeItem.mockClear();
123
+ sessionStorageMock.clear.mockClear();
124
+ });
@@ -0,0 +1,39 @@
1
+ # Storybook Stories
2
+
3
+ This directory contains all the Storybook stories for the Roger UI Bank design system.
4
+
5
+ ## Structure
6
+
7
+ - `components/` - Component stories organized by category
8
+ - `ui/` - Basic UI component stories
9
+ - `layouts/` - Layout component stories
10
+ - `feedback/` - Feedback component stories
11
+ - `navigation/` - Navigation component stories
12
+ - `overlay/` - Overlay component stories
13
+ - `data-display/` - Data display component stories
14
+ - `themes/` - Theme documentation and examples
15
+ - `decorators/` - Story decorators for common functionality
16
+
17
+ ## Usage
18
+
19
+ 1. Start Storybook: `npm run storybook`
20
+ 2. Navigate to http://localhost:6006
21
+ 3. Use the theme switcher in the toolbar to see different themes
22
+ 4. Explore component variations and documentation
23
+
24
+ ## Adding New Stories
25
+
26
+ 1. Create a new story file in the appropriate category
27
+ 2. Import the component and create stories
28
+ 3. Add theme decorator for multi-theme support
29
+ 4. Include comprehensive documentation
30
+ 5. Test with all available themes
31
+
32
+ ## Theme Support
33
+
34
+ All components support three themes:
35
+ - **Stan Design** - Modern, professional design
36
+ - **Enterprise** - Corporate, business-focused design
37
+ - **Harvey Creative** - Creative, vibrant design
38
+
39
+ Use the theme decorator to ensure proper theme integration in your stories.
@@ -0,0 +1,143 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useTheme } from '../../themes';
3
+
4
+ /**
5
+ * Theme Debugger Component for Storybook
6
+ * Shows current theme state and CSS variables for debugging
7
+ */
8
+ export const ThemeDebugger: React.FC = () => {
9
+ const { currentTheme, currentThemeConfig, availableThemes } = useTheme();
10
+ const [cssVariables, setCssVariables] = useState<Record<string, string>>({});
11
+ const [isVisible, setIsVisible] = useState(false);
12
+
13
+ // Extract CSS variables from document root
14
+ useEffect(() => {
15
+ const extractCSSVariables = () => {
16
+ const root = document.documentElement;
17
+ const variables: Record<string, string> = {};
18
+
19
+ // Get all CSS custom properties
20
+ const styles = getComputedStyle(root);
21
+
22
+ // Method 1: Try to get all CSS properties
23
+ try {
24
+ for (let i = 0; i < styles.length; i++) {
25
+ const property = styles[i];
26
+ if (property.startsWith('--cs-')) {
27
+ variables[property] = styles.getPropertyValue(property);
28
+ }
29
+ }
30
+ } catch (error) {
31
+ console.warn('Could not iterate through styles:', error);
32
+ }
33
+
34
+ // Method 2: Try to get specific known CSS variables
35
+ const knownVariables = [
36
+ '--cs-primary-500', '--cs-secondary-500', '--cs-success', '--cs-warning', '--cs-error', '--cs-info',
37
+ '--cs-text-primary', '--cs-text-secondary', '--cs-surface-bg', '--cs-border',
38
+ '--cs-fonts-primary-family', '--cs-fonts-primary-sizes-md', '--cs-spacing-4'
39
+ ];
40
+
41
+ knownVariables.forEach(varName => {
42
+ try {
43
+ const value = styles.getPropertyValue(varName);
44
+ if (value && value.trim() !== '') {
45
+ variables[varName] = value;
46
+ }
47
+ } catch (error) {
48
+ // Variable doesn't exist
49
+ }
50
+ });
51
+
52
+ // Method 3: Check if variables are set via inline styles
53
+ const inlineVars = root.style.cssText.match(/--cs-[^:]+:[^;]+/g) || [];
54
+ inlineVars.forEach(varDeclaration => {
55
+ const [name, value] = varDeclaration.split(':');
56
+ if (name && value) {
57
+ variables[name.trim()] = value.trim();
58
+ }
59
+ });
60
+
61
+ console.log('Extracted CSS variables:', variables);
62
+ setCssVariables(variables);
63
+ };
64
+
65
+ extractCSSVariables();
66
+
67
+ // Listen for theme changes
68
+ const handleThemeChange = () => {
69
+ setTimeout(extractCSSVariables, 200); // Increased delay to ensure CSS is updated
70
+ };
71
+
72
+ window.addEventListener('themeChange', handleThemeChange);
73
+
74
+ // Also check periodically for changes
75
+ const interval = setInterval(extractCSSVariables, 1000);
76
+
77
+ return () => {
78
+ window.removeEventListener('themeChange', handleThemeChange);
79
+ clearInterval(interval);
80
+ };
81
+ }, [currentTheme]);
82
+
83
+ if (!isVisible) {
84
+ return (
85
+ <button
86
+ onClick={() => setIsVisible(true)}
87
+ className="fixed bottom-4 right-4 bg-blue-600 text-white px-3 py-2 rounded-lg text-sm z-50"
88
+ >
89
+ 🐛 Debug Theme
90
+ </button>
91
+ );
92
+ }
93
+
94
+ return (
95
+ <div className="fixed bottom-4 right-4 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg p-4 max-w-md max-h-96 overflow-auto shadow-lg z-50">
96
+ <div className="flex items-center justify-between mb-3">
97
+ <h3 className="font-semibold text-gray-900 dark:text-white">Theme Debugger</h3>
98
+ <button
99
+ onClick={() => setIsVisible(false)}
100
+ className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
101
+ >
102
+
103
+ </button>
104
+ </div>
105
+
106
+ <div className="space-y-3 text-sm">
107
+ <div>
108
+ <strong>Current Theme:</strong> {currentTheme}
109
+ </div>
110
+
111
+ <div>
112
+ <strong>Available Themes:</strong> {availableThemes.join(', ')}
113
+ </div>
114
+
115
+ {currentThemeConfig && (
116
+ <div>
117
+ <strong>Theme Config:</strong>
118
+ <pre className="mt-1 p-2 bg-gray-100 dark:bg-gray-700 rounded text-xs overflow-auto max-h-20">
119
+ {JSON.stringify({
120
+ name: currentThemeConfig.meta?.name,
121
+ description: currentThemeConfig.meta?.description,
122
+ category: currentThemeConfig.meta?.category,
123
+ primaryColor: currentThemeConfig.colors?.primary?.[500],
124
+ fontFamily: currentThemeConfig.fonts?.primary?.family
125
+ }, null, 2)}
126
+ </pre>
127
+ </div>
128
+ )}
129
+
130
+ <div>
131
+ <strong>CSS Variables ({Object.keys(cssVariables).length}):</strong>
132
+ <div className="mt-1 max-h-32 overflow-auto">
133
+ {Object.entries(cssVariables).map(([variable, value]) => (
134
+ <div key={variable} className="text-xs font-mono">
135
+ <span className="text-blue-600">{variable}:</span> {value}
136
+ </div>
137
+ ))}
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ );
143
+ };