@shohojdhara/atomix 0.3.12 → 0.3.14

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