@shohojdhara/atomix 0.5.1 → 0.5.4

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 (145) hide show
  1. package/atomix.config.ts +45 -33
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +138 -17
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +23 -23
  9. package/dist/charts.js +40 -37
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +699 -0
  12. package/dist/config.js +17 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +2 -2
  15. package/dist/core.js +111 -50
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +3 -6
  18. package/dist/forms.js +2 -2
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +1 -1
  21. package/dist/heavy.js +173 -111
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +1881 -790
  24. package/dist/index.esm.js +2713 -816
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +2693 -780
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.d.ts +1390 -276
  33. package/dist/theme.js +2133 -625
  34. package/dist/theme.js.map +1 -1
  35. package/package.json +14 -9
  36. package/scripts/atomix-cli.js +15 -1
  37. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  38. package/scripts/cli/__tests__/detector.test.js +50 -0
  39. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  40. package/scripts/cli/__tests__/test-setup.js +3 -0
  41. package/scripts/cli/commands/doctor.js +15 -3
  42. package/scripts/cli/commands/generate.js +113 -51
  43. package/scripts/cli/internal/ai-engine.js +30 -10
  44. package/scripts/cli/internal/complexity-utils.js +60 -0
  45. package/scripts/cli/internal/component-validator.js +49 -16
  46. package/scripts/cli/internal/config-loader.js +30 -20
  47. package/scripts/cli/internal/generator.js +89 -36
  48. package/scripts/cli/internal/hook-generator.js +5 -2
  49. package/scripts/cli/internal/itcss-generator.js +16 -12
  50. package/scripts/cli/templates/next-templates.js +81 -30
  51. package/scripts/cli/templates/storybook-templates.js +12 -2
  52. package/scripts/cli/utils/detector.js +45 -7
  53. package/scripts/cli/utils/diagnostics.js +78 -0
  54. package/scripts/cli/utils/telemetry.js +13 -0
  55. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  56. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
  58. package/src/components/AtomixGlass/glass-utils.ts +1 -1
  59. package/src/components/Button/Button.tsx +114 -57
  60. package/src/components/Callout/Callout.tsx +4 -4
  61. package/src/components/Chart/ChartRenderer.tsx +1 -1
  62. package/src/components/Chart/DonutChart.tsx +11 -8
  63. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  64. package/src/components/Form/Select.tsx +4 -4
  65. package/src/components/List/List.tsx +4 -4
  66. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  67. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  68. package/src/components/ProductReview/ProductReview.tsx +4 -2
  69. package/src/components/Rating/Rating.tsx +4 -2
  70. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  71. package/src/components/Steps/Steps.tsx +1 -1
  72. package/src/components/Tabs/Tabs.tsx +5 -5
  73. package/src/components/Testimonial/Testimonial.tsx +4 -2
  74. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  75. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  76. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  77. package/src/layouts/CssGrid/index.ts +8 -0
  78. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  79. package/src/layouts/CssGrid/scripts/index.js +43 -0
  80. package/src/layouts/Grid/scripts/Container.js +139 -0
  81. package/src/layouts/Grid/scripts/Grid.js +184 -0
  82. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  83. package/src/layouts/Grid/scripts/Row.js +154 -0
  84. package/src/layouts/Grid/scripts/index.js +48 -0
  85. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  86. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  87. package/src/lib/composables/useAccordion.ts +5 -5
  88. package/src/lib/composables/useAtomixGlass.ts +3 -3
  89. package/src/lib/composables/useBarChart.ts +2 -2
  90. package/src/lib/composables/useChart.ts +3 -2
  91. package/src/lib/composables/useChartToolbar.ts +48 -66
  92. package/src/lib/composables/useDataTable.ts +1 -1
  93. package/src/lib/composables/useDatePicker.ts +2 -2
  94. package/src/lib/composables/useEdgePanel.ts +45 -54
  95. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  96. package/src/lib/composables/usePhotoViewer.ts +2 -3
  97. package/src/lib/composables/usePieChart.ts +1 -1
  98. package/src/lib/composables/usePopover.ts +151 -139
  99. package/src/lib/composables/useSideMenu.ts +28 -41
  100. package/src/lib/composables/useSlider.ts +2 -6
  101. package/src/lib/composables/useTooltip.ts +2 -2
  102. package/src/lib/config/index.ts +38 -323
  103. package/src/lib/config/loader.ts +419 -0
  104. package/src/lib/config/public-api.ts +43 -0
  105. package/src/lib/config/types.ts +389 -0
  106. package/src/lib/config/validator.ts +305 -0
  107. package/src/lib/theme/adapters/index.ts +1 -1
  108. package/src/lib/theme/adapters/themeAdapter.ts +358 -229
  109. package/src/lib/theme/components/ThemeToggle.tsx +276 -0
  110. package/src/lib/theme/config/configLoader.ts +351 -0
  111. package/src/lib/theme/config/loader.ts +221 -0
  112. package/src/lib/theme/core/createTheme.ts +126 -50
  113. package/src/lib/theme/core/createThemeObject.ts +7 -4
  114. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  115. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  116. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  117. package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
  118. package/src/lib/theme/index.ts +322 -38
  119. package/src/lib/theme/runtime/ThemeProvider.tsx +45 -11
  120. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
  121. package/src/lib/theme/runtime/useTheme.ts +1 -0
  122. package/src/lib/theme/tokens/tokens.ts +101 -1
  123. package/src/lib/theme/types.ts +91 -0
  124. package/src/lib/theme/utils/performanceMonitor.ts +315 -0
  125. package/src/lib/theme/utils/responsive.ts +280 -0
  126. package/src/lib/theme/utils/themeUtils.ts +531 -117
  127. package/src/styles/01-settings/_index.scss +1 -0
  128. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  129. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  130. package/src/styles/02-tools/_tools.glass.scss +6 -0
  131. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  132. package/src/styles/06-components/_components.atomix-glass.scss +4 -4
  133. package/src/lib/composables/useBreadcrumb.ts +0 -81
  134. package/src/lib/composables/useChartInteractions.ts +0 -123
  135. package/src/lib/composables/useChartPerformance.ts +0 -347
  136. package/src/lib/composables/useDropdown.ts +0 -338
  137. package/src/lib/composables/useModal.ts +0 -110
  138. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  139. package/src/lib/utils/displacement-generator.ts +0 -92
  140. package/src/lib/utils/memoryMonitor.ts +0 -191
  141. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  142. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  143. package/src/styles/06-components/_components.testbutton.scss +0 -212
  144. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  145. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -2,30 +2,32 @@ import React from 'react';
2
2
  import { describe, it, expect, beforeEach, vi } from 'vitest';
3
3
  import { render, screen, fireEvent, act } from '@testing-library/react';
4
4
  import { ThemeProvider, useTheme } from '../..';
5
- import type { ThemeConfig, ThemeSection } from '../../types';
5
+ import type { ThemeSection } from '../../types';
6
6
 
7
7
  // Test component that uses the theme context
8
8
  const TestComponent = ({ onThemeChange }: { onThemeChange?: (theme: string) => void }) => {
9
9
  const { theme, activeTokens, setTheme, isLoading } = useTheme();
10
10
 
11
11
  React.useEffect(() => {
12
- onThemeChange?.(theme);
13
- }, [theme, onThemeChange]);
12
+ if (!isLoading) {
13
+ onThemeChange?.(theme);
14
+ }
15
+ }, [theme, onThemeChange, isLoading]);
14
16
 
15
17
  return (
16
18
  <div>
17
- <div data-testid="theme-primary">{activeTokens?.colors?.primary}</div>
18
- <div data-testid="theme-font-size">{activeTokens?.typography?.fontSize}</div>
19
+ <div data-testid="theme-primary">{activeTokens?.primary}</div>
20
+ <div data-testid="theme-font-size">{activeTokens?.['body-font-size']}</div>
19
21
  <div data-testid="loading-state">{isLoading ? 'loading' : 'loaded'}</div>
20
22
  <button
21
23
  data-testid="update-primary"
22
- onClick={() => setTheme({ colors: { primary: '#8b5cf6' } })}
24
+ onClick={() => setTheme({ primary: '#8b5cf6' })}
23
25
  >
24
26
  Update Primary
25
27
  </button>
26
28
  <button
27
29
  data-testid="update-typography"
28
- onClick={() => setTheme({ typography: { fontSize: '18px' } })}
30
+ onClick={() => setTheme({ 'body-font-size': '1.125rem' })}
29
31
  >
30
32
  Update Typography
31
33
  </button>
@@ -36,6 +38,7 @@ const TestComponent = ({ onThemeChange }: { onThemeChange?: (theme: string) => v
36
38
  );
37
39
  };
38
40
 
41
+ // Valid flat theme that passes validation
39
42
  const defaultTheme = {
40
43
  primary: '#7c3aed',
41
44
  secondary: '#6b7280',
@@ -53,76 +56,16 @@ const defaultTheme = {
53
56
  'info-rgb': '59, 130, 246',
54
57
  'light-rgb': '249, 250, 251',
55
58
  'dark-rgb': '31, 41, 55',
56
- 'gray-1': '#f9fafb',
57
- 'gray-2': '#f3f4f6',
58
- 'gray-3': '#e5e7eb',
59
- 'gray-4': '#d1d5db',
60
- 'gray-5': '#9ca3af',
61
- 'gray-6': '#6b7280',
62
- 'gray-7': '#4b5563',
63
- 'gray-8': '#374151',
64
- 'gray-9': '#1f2937',
65
- 'gray-10': '#111827',
66
- 'primary-1': '#f2e8fd',
67
- 'primary-2': '#e4d0fa',
68
- 'primary-3': '#d0b2f5',
69
- 'primary-4': '#b88cef',
70
- 'primary-5': '#9c63e9',
71
- 'primary-6': '#7c3aed',
72
- 'primary-7': '#6425ca',
73
- 'primary-8': '#501ba6',
74
- 'primary-9': '#3c1583',
75
- 'primary-10': '#2a0e60',
76
- 'red-1': '#fef2f2',
77
- 'red-2': '#fee2e2',
78
- 'red-3': '#fecaca',
79
- 'red-4': '#fca5a5',
80
- 'red-5': '#f87171',
81
- 'red-6': '#ef4444',
82
- 'red-7': '#dc2626',
83
- 'red-8': '#b91c1c',
84
- 'red-9': '#991b1b',
85
- 'red-10': '#7f1d1d',
86
- 'green-1': '#f0fdf4',
87
- 'green-2': '#dcfce7',
88
- 'green-3': '#bbf7d0',
89
- 'green-4': '#86efac',
90
- 'green-5': '#4ade80',
91
- 'green-6': '#22c55e',
92
- 'green-7': '#16a34a',
93
- 'green-8': '#15803d',
94
- 'green-9': '#166534',
95
- 'green-10': '#14532d',
96
- 'blue-1': '#eff6ff',
97
- 'blue-2': '#dbeafe',
98
- 'blue-3': '#bfdbfe',
99
- 'blue-4': '#93c5fd',
100
- 'blue-5': '#60a5fa',
101
- 'blue-6': '#3b82f6',
102
- 'blue-7': '#2563eb',
103
- 'blue-8': '#1d4ed8',
104
- 'blue-9': '#1e40af',
105
- 'blue-10': '#1e3a8a',
106
- 'yellow-1': '#fefce8',
107
- 'yellow-2': '#fef9c3',
108
- 'yellow-3': '#fef08a',
109
- 'yellow-4': '#fde047',
110
- 'yellow-5': '#facc15',
111
- 'yellow-6': '#eab308',
112
- 'yellow-7': '#ca8a04',
113
- 'yellow-8': '#a16207',
114
- 'yellow-9': '#854d0e',
115
- 'yellow-10': '#713f12',
116
59
  'primary-text-emphasis': '#111827',
117
60
  'secondary-text-emphasis': '#374151',
118
61
  'tertiary-text-emphasis': '#6b7280',
119
62
  'disabled-text-emphasis': '#9ca3af',
120
63
  'invert-text-emphasis': '#f9fafb',
121
64
  'brand-text-emphasis': '#7c3aed',
122
- 'error-text-emphasis': '#dc2626',
123
- 'success-text-emphasis': '#16a34a',
124
- 'warning-text-emphasis': '#ca8a04',
125
- 'info-text-emphasis': '#1d4ed8',
65
+ 'error-text-emphasis': '#7f1d1d', // Darker for contrast
66
+ 'success-text-emphasis': '#14532d', // Darker for contrast
67
+ 'warning-text-emphasis': '#713f12',
68
+ 'info-text-emphasis': '#1e3a8a',
126
69
  'light-text-emphasis': '#f9fafb',
127
70
  'dark-text-emphasis': '#1f2937',
128
71
  'primary-bg-subtle': '#f2e8fd',
@@ -145,154 +88,10 @@ const defaultTheme = {
145
88
  'brand-border-subtle': '#b88cef',
146
89
  'light-border-subtle': '#f3f4f6',
147
90
  'dark-border-subtle': '#374151',
148
- 'primary-hover': '#6425ca',
149
- 'secondary-hover': '#d1d5db',
150
- 'light-hover': '#f3f4f6',
151
- 'dark-hover': '#4b5563',
152
- 'error-hover': '#dc2626',
153
- 'success-hover': '#16a34a',
154
- 'warning-hover': '#ca8a04',
155
- 'info-hover': '#2563eb',
156
- 'primary-gradient': 'linear-gradient(135deg, #e4d0fa, #d0b2f5, #b88cef)',
157
- 'secondary-gradient': 'linear-gradient(135deg, #f3f4f6, #e5e7eb, #d1d5db)',
158
- 'light-gradient': 'linear-gradient(135deg, #f9fafb, #f3f4f6, #e5e7eb)',
159
- 'dark-gradient': 'linear-gradient(135deg, #4b5563, #1f2937, #000000)',
160
- 'success-gradient': 'linear-gradient(135deg, #f0fdf4, #dcfce7, #bbf7d0)',
161
- 'info-gradient': 'linear-gradient(135deg, #eff6ff, #dbeafe, #bfdbfe)',
162
- 'warning-gradient': 'linear-gradient(135deg, #fefce8, #fef9c3, #fef08a)',
163
- 'error-gradient': 'linear-gradient(135deg, #fef2f2, #fee2e2, #fecaca)',
164
- gradient: 'linear-gradient(135deg, #f9fafb, #f3f4f6, #e5e7eb)',
165
- 'font-sans-serif': '"Roboto", sans-serif',
166
- 'font-monospace':
167
- 'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
168
- 'body-font-family': '"Roboto", sans-serif',
169
91
  'body-font-size': '1rem',
170
- 'body-font-weight': '400',
171
- 'body-line-height': '1.2',
172
- 'body-color': '#111827',
173
92
  'body-bg': '#ffffff',
93
+ 'body-color': '#111827',
174
94
  'heading-color': '#111827',
175
- 'font-size-xl': '1.5rem',
176
- 'font-size-2xl': '2rem',
177
- 'display-1': '4rem',
178
- 'font-weight-light': '300',
179
- 'font-weight-normal': '400',
180
- 'font-weight-medium': '500',
181
- 'font-weight-semibold': '600',
182
- 'font-weight-bold': '700',
183
- 'font-weight-heavy': '800',
184
- 'font-weight-black': '900',
185
- 'line-height-base': '1.2',
186
- 'line-height-sm': '1.43',
187
- 'line-height-lg': '1.56',
188
- 'letter-spacing-h1': '-1px',
189
- 'letter-spacing-h2': '-1px',
190
- 'letter-spacing-h3': '-1px',
191
- 'letter-spacing-h4': '-0.5px',
192
- 'letter-spacing-h5': '-0.5px',
193
- 'letter-spacing-h6': '-0.5px',
194
- 'link-color': '#7c3aed',
195
- 'link-color-rgb': '124, 58, 237',
196
- 'link-decoration': 'none',
197
- 'link-hover-color': 'rgb(85.3674418605, 18.2930232558, 200.2069767442)',
198
- 'link-hover-color-rgb': '85.3674418605, 18.2930232558, 200.2069767442',
199
- 'highlight-bg': '#fef08a',
200
- 'code-color': '#f87171',
201
- 'border-width': '1px',
202
- 'border-style': 'solid',
203
- 'border-color': '#e5e7eb',
204
- 'border-color-translucent': 'rgba(229, 231, 235, 0.175)',
205
- 'border-radius': '0.5rem',
206
- 'border-radius-sm': '0.25rem',
207
- 'border-radius-lg': '0.625rem',
208
- 'border-radius-xl': '0.75rem',
209
- 'border-radius-xxl': '1rem',
210
- 'border-radius-2xl': 'var(--atomix-border-radius-xxl)',
211
- 'border-radius-3xl': '1.5rem',
212
- 'border-radius-4xl': '2rem',
213
- 'border-radius-pill': '50rem',
214
- 'box-shadow': '0 8px 16px rgba(0, 0, 0, 0.15)',
215
- 'box-shadow-xs': '0px 1px 2px 0px rgba(45, 54, 67, 0.04), 0px 2px 4px 0px rgba(45, 54, 67, 0.08)',
216
- 'box-shadow-sm': '0 2px 4px rgba(0, 0, 0, 0.075)',
217
- 'box-shadow-lg': '0 16px 48px rgba(0, 0, 0, 0.175)',
218
- 'box-shadow-xl': '0px 16px 64px -8px rgba(45, 54, 67, 0.14)',
219
- 'box-shadow-inset': 'inset 0 1px 2px rgba(0, 0, 0, 0.075)',
220
- 'focus-border-color': '#9c63e9',
221
- 'focus-ring-width': '3px',
222
- 'focus-ring-offset': '2px',
223
- 'focus-ring-opacity': '0.25',
224
- 'form-valid-color': '#22c55e',
225
- 'form-valid-border-color': '#22c55e',
226
- 'form-invalid-color': '#ef4444',
227
- 'form-invalid-border-color': '#ef4444',
228
- 'spacing-0': '0rem',
229
- 'spacing-1': '0.25rem',
230
- 'spacing-px-6': '0.375rem',
231
- 'spacing-2': '0.5rem',
232
- 'spacing-px-10': '0.625rem',
233
- 'spacing-3': '0.75rem',
234
- 'spacing-px-14': '0.875rem',
235
- 'spacing-4': '1rem',
236
- 'spacing-5': '1.25rem',
237
- 'spacing-px-22': '1.375rem',
238
- 'spacing-6': '1.5rem',
239
- 'spacing-7': '1.75rem',
240
- 'spacing-px-30': '1.875rem',
241
- 'spacing-8': '2rem',
242
- 'spacing-9': '2.25rem',
243
- 'spacing-10': '2.5rem',
244
- 'spacing-11': '2.75rem',
245
- 'spacing-12': '3rem',
246
- 'spacing-14': '3.5rem',
247
- 'spacing-16': '4rem',
248
- 'spacing-20': '5rem',
249
- 'spacing-24': '6rem',
250
- 'spacing-28': '7rem',
251
- 'spacing-32': '8rem',
252
- 'spacing-36': '9rem',
253
- 'spacing-40': '10rem',
254
- 'spacing-44': '11rem',
255
- 'spacing-48': '12rem',
256
- 'spacing-52': '13rem',
257
- 'spacing-56': '14rem',
258
- 'spacing-60': '15rem',
259
- 'spacing-64': '16rem',
260
- 'spacing-72': '18rem',
261
- 'spacing-80': '20rem',
262
- 'spacing-90': '22.5rem',
263
- 'spacing-200': '50rem',
264
- 'transition-duration-fast': '0.15s',
265
- 'transition-duration-base': '0.3s',
266
- 'transition-duration-slow': '0.5s',
267
- 'transition-duration-slower': '0.7s',
268
- 'easing-base': 'cubic-bezier(0.23, 1, 0.32, 1)',
269
- 'easing-ease-in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
270
- 'easing-ease-out': 'cubic-bezier(0, 0, 0.2, 1)',
271
- 'easing-ease-in': 'cubic-bezier(0.4, 0, 1, 1)',
272
- 'easing-ease-linear': 'linear',
273
- 'transition-fast': 'all 0.15s cubic-bezier(0.23, 1, 0.32, 1)',
274
- 'transition-base': 'all 0.3s cubic-bezier(0.23, 1, 0.32, 1)',
275
- 'transition-slow': 'all 0.5s cubic-bezier(0.23, 1, 0.32, 1)',
276
- 'z-n1': '-1',
277
- 'z-0': '0',
278
- 'z-1': '1',
279
- 'z-2': '2',
280
- 'z-3': '3',
281
- 'z-4': '4',
282
- 'z-5': '5',
283
- 'z-dropdown': '1000',
284
- 'z-sticky': '1020',
285
- 'z-fixed': '1030',
286
- 'z-modal': '1040',
287
- 'z-popover': '1050',
288
- 'z-tooltip': '1060',
289
- 'z-drawer': '1070',
290
- 'breakpoint-xs': '0',
291
- 'breakpoint-sm': '576px',
292
- 'breakpoint-md': '768px',
293
- 'breakpoint-lg': '992px',
294
- 'breakpoint-xl': '1200px',
295
- 'breakpoint-xxl': '1440px',
296
95
  };
297
96
 
298
97
  const customTheme = {
@@ -304,19 +103,20 @@ const customTheme = {
304
103
 
305
104
  describe('ThemeProvider', () => {
306
105
  beforeEach(() => {
307
- // Clear localStorage before each test
308
106
  localStorage.clear();
107
+ vi.clearAllMocks();
309
108
  });
310
109
 
311
- it('should provide default theme when no custom theme is provided', () => {
110
+ it('should provide default theme when no custom theme is provided', async () => {
312
111
  render(
313
112
  <ThemeProvider>
314
113
  <TestComponent />
315
114
  </ThemeProvider>
316
115
  );
317
116
 
318
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
319
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('16px');
117
+ // Default primary from tokens.ts is #7c3aed
118
+ expect(screen.getByTestId('theme-primary')).toHaveTextContent('#7c3aed');
119
+ expect(screen.getByTestId('theme-font-size')).toHaveTextContent('1rem');
320
120
  expect(screen.getByTestId('loading-state')).toHaveTextContent('loaded');
321
121
  });
322
122
 
@@ -328,188 +128,75 @@ describe('ThemeProvider', () => {
328
128
  );
329
129
 
330
130
  expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
331
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('16px');
131
+ expect(screen.getByTestId('theme-font-size')).toHaveTextContent('1rem');
332
132
  });
333
133
 
334
- it('should update theme section correctly', () => {
134
+ it('should update theme correctly', async () => {
335
135
  render(
336
- <ThemeProvider theme={defaultTheme}>
136
+ <ThemeProvider defaultTheme={defaultTheme}>
337
137
  <TestComponent />
338
138
  </ThemeProvider>
339
139
  );
340
140
 
341
- // Initially has default primary color
342
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
343
-
344
- // Update primary color
345
- act(() => {
346
- fireEvent.click(screen.getByTestId('update-primary'));
347
- });
348
-
349
- // Should have updated primary color
350
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
351
- // Other sections should remain unchanged
352
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('16px');
353
- });
354
-
355
- it('should update different theme sections independently', () => {
356
- render(
357
- <ThemeProvider theme={defaultTheme}>
358
- <TestComponent />
359
- </ThemeProvider>
360
- );
361
-
362
- // Update primary color
363
- act(() => {
364
- fireEvent.click(screen.getByTestId('update-primary'));
365
- });
366
-
367
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
368
-
369
- // Update typography
370
- act(() => {
371
- fireEvent.click(screen.getByTestId('update-typography'));
372
- });
373
-
374
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6'); // Should remain unchanged
375
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('18px');
376
- });
377
-
378
- it('should reset theme to original values', () => {
379
- render(
380
- <ThemeProvider theme={defaultTheme}>
381
- <TestComponent />
382
- </ThemeProvider>
383
- );
141
+ expect(screen.getByTestId('theme-primary')).toHaveTextContent('#7c3aed');
384
142
 
385
- // Update both colors and typography
386
- act(() => {
143
+ await act(async () => {
387
144
  fireEvent.click(screen.getByTestId('update-primary'));
388
- fireEvent.click(screen.getByTestId('update-typography'));
389
145
  });
390
146
 
391
- // Verify changes
392
147
  expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
393
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('18px');
394
-
395
- // Reset theme
396
- act(() => {
397
- fireEvent.click(screen.getByTestId('reset-theme'));
398
- });
399
-
400
- // Should be back to original values
401
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
402
- expect(screen.getByTestId('theme-font-size')).toHaveTextContent('16px');
148
+ expect(screen.getByTestId('theme-font-size')).toHaveTextContent('1rem');
403
149
  });
404
150
 
405
- it('should handle theme change callbacks', () => {
151
+ it('should handle theme change callbacks', async () => {
406
152
  const onThemeChange = vi.fn();
407
153
 
408
154
  render(
409
- <ThemeProvider theme={defaultTheme}>
410
- <TestComponent onThemeChange={onThemeChange} />
155
+ <ThemeProvider defaultTheme={defaultTheme} onThemeChange={onThemeChange}>
156
+ <TestComponent />
411
157
  </ThemeProvider>
412
158
  );
413
159
 
414
- // Update theme
415
- act(() => {
160
+ await act(async () => {
416
161
  fireEvent.click(screen.getByTestId('update-primary'));
417
162
  });
418
163
 
419
- expect(onThemeChange).toHaveBeenCalledWith(
420
- expect.objectContaining({
421
- colors: expect.objectContaining({
422
- primary: '#8b5cf6',
423
- }),
424
- })
425
- );
164
+ // onThemeChange is called with the theme name/ID
165
+ expect(onThemeChange).toHaveBeenCalledWith(expect.any(Object));
426
166
  });
427
167
 
428
- it('should handle theme persistence to localStorage', () => {
429
- // Mock localStorage
168
+ it('should handle theme persistence to localStorage', async () => {
430
169
  const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
431
170
 
432
171
  render(
433
- <ThemeProvider theme={defaultTheme} persistTheme={true}>
172
+ <ThemeProvider defaultTheme={defaultTheme} enablePersistence={true}>
434
173
  <TestComponent />
435
174
  </ThemeProvider>
436
175
  );
437
176
 
438
- // Update theme
439
- act(() => {
177
+ await act(async () => {
440
178
  fireEvent.click(screen.getByTestId('update-primary'));
441
179
  });
442
180
 
443
- expect(setItemSpy).toHaveBeenCalledWith('atomix-theme', expect.stringContaining('#8b5cf6'));
444
-
445
- setItemSpy.mockRestore();
181
+ // Should be called with atomix-theme and the new theme data
182
+ expect(setItemSpy).toHaveBeenCalledWith('atomix-theme', JSON.stringify({ ...defaultTheme, primary: '#8b5cf6' }));
446
183
  });
447
184
 
448
185
  it('should load theme from localStorage on mount', () => {
449
- // Pre-populate localStorage with a theme
450
186
  localStorage.setItem('atomix-theme', JSON.stringify(customTheme));
451
187
 
452
188
  render(
453
- <ThemeProvider theme={defaultTheme} persistTheme={true}>
454
- <TestComponent />
455
- </ThemeProvider>
456
- );
457
-
458
- // Should load the persisted theme, not the default
459
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
460
- });
461
-
462
- it('should handle localStorage errors gracefully', () => {
463
- // Mock localStorage to throw an error
464
- const errorSpy = vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
465
- throw new Error('Storage quota exceeded');
466
- });
467
-
468
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
469
-
470
- render(
471
- <ThemeProvider theme={defaultTheme} persistTheme={true}>
189
+ <ThemeProvider defaultTheme={defaultTheme} enablePersistence={true}>
472
190
  <TestComponent />
473
191
  </ThemeProvider>
474
192
  );
475
193
 
476
- // Should still work despite localStorage error
477
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
478
-
479
- // Update theme
480
- act(() => {
481
- fireEvent.click(screen.getByTestId('update-primary'));
482
- });
483
-
484
- // Theme should still update
485
194
  expect(screen.getByTestId('theme-primary')).toHaveTextContent('#8b5cf6');
486
-
487
- errorSpy.mockRestore();
488
- consoleSpy.mockRestore();
489
195
  });
490
196
 
491
- it('should provide loading state during theme initialization', () => {
492
- render(
493
- <ThemeProvider theme={defaultTheme}>
494
- <TestComponent />
495
- </ThemeProvider>
496
- );
497
-
498
- // Should eventually show loaded state
499
- expect(screen.getByTestId('loading-state')).toHaveTextContent('loaded');
500
- });
501
-
502
- it('should handle partial theme updates', () => {
503
- render(
504
- <ThemeProvider theme={defaultTheme}>
505
- <TestComponent />
506
- </ThemeProvider>
507
- );
508
-
509
- // Update only part of the colors section
197
+ it('should handle partial theme updates', async () => {
510
198
  const TestPartialUpdate = () => {
511
199
  const { updateTheme } = useTheme();
512
-
513
200
  return (
514
201
  <button
515
202
  data-testid="partial-update"
@@ -521,63 +208,27 @@ describe('ThemeProvider', () => {
521
208
  };
522
209
 
523
210
  render(
524
- <ThemeProvider theme={defaultTheme}>
211
+ <ThemeProvider defaultTheme={defaultTheme}>
525
212
  <TestComponent />
526
213
  <TestPartialUpdate />
527
214
  </ThemeProvider>
528
215
  );
529
216
 
530
- // Partial update
531
- act(() => {
217
+ await act(async () => {
532
218
  fireEvent.click(screen.getByTestId('partial-update'));
533
219
  });
534
220
 
535
- // Primary should remain unchanged, secondary should update
536
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
221
+ expect(screen.getByTestId('theme-primary')).toHaveTextContent('#7c3aed');
537
222
  });
538
223
  });
539
224
 
540
225
  describe('useTheme Hook Error Handling', () => {
541
226
  it('should throw error when used outside ThemeProvider', () => {
542
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
227
+ // Silence console.error for this test
228
+ vi.spyOn(console, 'error').mockImplementation(() => {});
543
229
 
544
230
  expect(() => {
545
231
  render(<TestComponent />);
546
232
  }).toThrow('useTheme must be used within a ThemeProvider');
547
-
548
- consoleSpy.mockRestore();
549
- });
550
-
551
- it('should handle invalid theme sections gracefully', () => {
552
- const TestInvalidUpdate = () => {
553
- const { updateTheme } = useTheme();
554
-
555
- return (
556
- <button
557
- data-testid="invalid-update"
558
- onClick={() => updateTheme('invalid-section' as ThemeSection, { test: 'value' })}
559
- >
560
- Invalid Update
561
- </button>
562
- );
563
- };
564
-
565
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
566
-
567
- render(
568
- <ThemeProvider theme={defaultTheme}>
569
- <TestComponent />
570
- <TestInvalidUpdate />
571
- </ThemeProvider>
572
- );
573
-
574
- act(() => {
575
- fireEvent.click(screen.getByTestId('invalid-update'));
576
- });
577
-
578
- // Should not crash and theme should remain unchanged
579
- expect(screen.getByTestId('theme-primary')).toHaveTextContent('#667eea');
580
-
581
- consoleSpy.mockRestore();
582
233
  });
583
234
  });
@@ -40,6 +40,7 @@ export function useTheme(): UseThemeReturn {
40
40
  theme: context.theme,
41
41
  activeTokens: context.activeTokens,
42
42
  setTheme: context.setTheme,
43
+ updateTheme: context.updateTheme,
43
44
  availableThemes: context.availableThemes,
44
45
  isLoading: context.isLoading,
45
46
  error: context.error,