@shohojdhara/atomix 0.3.2 → 0.3.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 (84) hide show
  1. package/README.md +58 -21
  2. package/dist/atomix.css +96 -121
  3. package/dist/atomix.min.css +3 -3
  4. package/dist/index.d.ts +7937 -7765
  5. package/dist/index.esm.js +3677 -4031
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +3648 -3952
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/package.json +44 -16
  12. package/scripts/atomix-cli.js +1764 -0
  13. package/scripts/build-themes.js +208 -0
  14. package/scripts/cli/interactive-init.js +520 -0
  15. package/scripts/cli/migration-tools.js +603 -0
  16. package/scripts/cli/theme-bridge.js +129 -0
  17. package/scripts/cli/token-manager.js +519 -0
  18. package/scripts/sync-theme-config.js +309 -0
  19. package/src/components/Button/Button.tsx +36 -1
  20. package/src/components/List/ListGroup.tsx +1 -2
  21. package/src/components/Popover/Popover.tsx +2 -2
  22. package/src/components/Tooltip/Tooltip.stories.tsx +49 -12
  23. package/src/components/Tooltip/Tooltip.tsx +32 -58
  24. package/src/lib/composables/useTooltip.ts +285 -0
  25. package/src/lib/config/index.ts +275 -0
  26. package/src/lib/config/loader.ts +105 -0
  27. package/src/lib/constants/cssVariables.ts +390 -0
  28. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +151 -0
  29. package/src/lib/hooks/index.ts +19 -0
  30. package/src/lib/hooks/useComponentCustomization.ts +175 -0
  31. package/src/lib/index.ts +14 -1
  32. package/src/lib/patterns/__tests__/slots.test.ts +108 -0
  33. package/src/lib/patterns/index.ts +35 -0
  34. package/src/lib/patterns/slots.tsx +421 -0
  35. package/src/lib/theme/composeTheme.ts +0 -5
  36. package/src/lib/theme/config/index.ts +1 -1
  37. package/src/lib/theme/config/loader.ts +75 -41
  38. package/src/lib/theme/config/types.ts +21 -7
  39. package/src/lib/theme/config/validator.ts +1 -1
  40. package/src/lib/theme/constants.ts +12 -2
  41. package/src/lib/theme/createTheme.ts +2 -135
  42. package/src/lib/theme/createThemeFromConfig.ts +132 -0
  43. package/src/lib/theme/cssVariableMapper.ts +261 -0
  44. package/src/lib/theme/devtools/CLI.ts +161 -76
  45. package/src/lib/theme/devtools/Comparator.tsx +343 -0
  46. package/src/lib/theme/devtools/IMPROVEMENTS.md +429 -0
  47. package/src/lib/theme/devtools/Inspector.tsx +21 -6
  48. package/src/lib/theme/devtools/LiveEditor.tsx +393 -0
  49. package/src/lib/theme/devtools/README.md +433 -0
  50. package/src/lib/theme/devtools/index.ts +12 -11
  51. package/src/lib/theme/generateCSSVariables.ts +79 -38
  52. package/src/lib/theme/index.ts +45 -246
  53. package/src/lib/theme/runtime/ThemeApplicator.ts +252 -0
  54. package/src/lib/theme/runtime/ThemeManager.test.ts +17 -1
  55. package/src/lib/theme/runtime/ThemeManager.ts +7 -7
  56. package/src/lib/theme/themeUtils.ts +27 -5
  57. package/src/lib/theme/types.ts +59 -1
  58. package/src/lib/theme-tools.ts +125 -0
  59. package/src/lib/types/components.ts +260 -72
  60. package/src/lib/types/partProps.ts +426 -0
  61. package/src/lib/utils/__tests__/componentUtils.test.ts +144 -0
  62. package/src/lib/utils/componentUtils.ts +163 -0
  63. package/src/lib/utils/index.ts +17 -57
  64. package/src/styles/01-settings/_settings.colors.scss +10 -10
  65. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  66. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  67. package/src/styles/03-generic/_generated-root.css +5 -0
  68. package/src/styles/06-components/_components.navbar.scss +12 -5
  69. package/src/styles/06-components/_components.tooltip.scss +31 -81
  70. package/src/themes/README.md +442 -0
  71. package/src/themes/themes.config.js +35 -0
  72. package/src/lib/theme/errors.test.ts +0 -207
  73. package/src/lib/theme/generators/CSSGenerator.ts +0 -311
  74. package/src/lib/theme/generators/ConfigGenerator.ts +0 -287
  75. package/src/lib/theme/generators/TypeGenerator.ts +0 -228
  76. package/src/lib/theme/generators/index.ts +0 -21
  77. package/src/lib/theme/monitoring/ThemeAnalytics.ts +0 -409
  78. package/src/lib/theme/monitoring/index.ts +0 -17
  79. package/src/lib/theme/overrides/ComponentOverrides.ts +0 -243
  80. package/src/lib/theme/overrides/index.ts +0 -15
  81. package/src/lib/theme/studio/ThemeStudio.tsx +0 -312
  82. package/src/lib/theme/studio/index.ts +0 -8
  83. package/src/lib/theme/whitelabel/WhiteLabelManager.ts +0 -364
  84. package/src/lib/theme/whitelabel/index.ts +0 -13
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Part-Based Styling Props
3
+ *
4
+ * Type definitions for styling individual component parts with className and style props.
5
+ * This enables granular customization of component internals.
6
+ */
7
+
8
+ import React from 'react';
9
+
10
+ /**
11
+ * Style properties for a component part
12
+ */
13
+ export interface PartStyleProps {
14
+ /** Additional CSS class name for the part */
15
+ className?: string;
16
+ /** Inline styles for the part */
17
+ style?: React.CSSProperties;
18
+ }
19
+
20
+ /**
21
+ * Generic component parts type
22
+ * T is a union of part names as strings
23
+ */
24
+ export type ComponentParts<T extends string> = {
25
+ [K in T]?: PartStyleProps;
26
+ };
27
+
28
+ /**
29
+ * Button component parts
30
+ */
31
+ export type ButtonParts = ComponentParts<'root' | 'icon' | 'label' | 'spinner'> & {
32
+ /** Root button element */
33
+ root?: PartStyleProps;
34
+ /** Icon wrapper element */
35
+ icon?: PartStyleProps;
36
+ /** Label/text wrapper element */
37
+ label?: PartStyleProps;
38
+ /** Loading spinner element */
39
+ spinner?: PartStyleProps;
40
+ };
41
+
42
+ /**
43
+ * Card component parts
44
+ */
45
+ export type CardParts = ComponentParts<
46
+ 'root' | 'header' | 'body' | 'footer' | 'title' | 'text' | 'image' | 'actions' | 'icon'
47
+ > & {
48
+ /** Root card element */
49
+ root?: PartStyleProps;
50
+ /** Header section */
51
+ header?: PartStyleProps;
52
+ /** Body/content section */
53
+ body?: PartStyleProps;
54
+ /** Footer section */
55
+ footer?: PartStyleProps;
56
+ /** Title element */
57
+ title?: PartStyleProps;
58
+ /** Text/description element */
59
+ text?: PartStyleProps;
60
+ /** Image element */
61
+ image?: PartStyleProps;
62
+ /** Actions container */
63
+ actions?: PartStyleProps;
64
+ /** Icon element */
65
+ icon?: PartStyleProps;
66
+ };
67
+
68
+ /**
69
+ * Input component parts
70
+ */
71
+ export type InputParts = ComponentParts<'root' | 'input' | 'wrapper' | 'prefix' | 'suffix'> & {
72
+ /** Root container element */
73
+ root?: PartStyleProps;
74
+ /** Input element itself */
75
+ input?: PartStyleProps;
76
+ /** Input wrapper element */
77
+ wrapper?: PartStyleProps;
78
+ /** Prefix element (icon/text before input) */
79
+ prefix?: PartStyleProps;
80
+ /** Suffix element (icon/text after input) */
81
+ suffix?: PartStyleProps;
82
+ };
83
+
84
+ /**
85
+ * Modal component parts
86
+ */
87
+ export type ModalParts = ComponentParts<
88
+ 'root' | 'backdrop' | 'dialog' | 'content' | 'header' | 'body' | 'footer' | 'close'
89
+ > & {
90
+ /** Root modal container */
91
+ root?: PartStyleProps;
92
+ /** Backdrop overlay */
93
+ backdrop?: PartStyleProps;
94
+ /** Dialog container */
95
+ dialog?: PartStyleProps;
96
+ /** Content wrapper */
97
+ content?: PartStyleProps;
98
+ /** Header section */
99
+ header?: PartStyleProps;
100
+ /** Body section */
101
+ body?: PartStyleProps;
102
+ /** Footer section */
103
+ footer?: PartStyleProps;
104
+ /** Close button */
105
+ close?: PartStyleProps;
106
+ };
107
+
108
+ /**
109
+ * Dropdown component parts
110
+ */
111
+ export type DropdownParts = ComponentParts<
112
+ 'root' | 'toggle' | 'menu' | 'menuWrapper' | 'item' | 'divider' | 'header'
113
+ > & {
114
+ /** Root dropdown container */
115
+ root?: PartStyleProps;
116
+ /** Toggle/trigger element */
117
+ toggle?: PartStyleProps;
118
+ /** Menu container */
119
+ menu?: PartStyleProps;
120
+ /** Menu wrapper (positioning) */
121
+ menuWrapper?: PartStyleProps;
122
+ /** Menu item */
123
+ item?: PartStyleProps;
124
+ /** Divider element */
125
+ divider?: PartStyleProps;
126
+ /** Header element */
127
+ header?: PartStyleProps;
128
+ };
129
+
130
+ /**
131
+ * Badge component parts
132
+ */
133
+ export type BadgeParts = ComponentParts<'root' | 'icon' | 'label'> & {
134
+ /** Root badge element */
135
+ root?: PartStyleProps;
136
+ /** Icon element */
137
+ icon?: PartStyleProps;
138
+ /** Label/text element */
139
+ label?: PartStyleProps;
140
+ };
141
+
142
+ /**
143
+ * Tabs component parts
144
+ */
145
+ export type TabsParts = ComponentParts<
146
+ 'root' | 'nav' | 'navItem' | 'navBtn' | 'panels' | 'panel' | 'panelBody'
147
+ > & {
148
+ /** Root tabs container */
149
+ root?: PartStyleProps;
150
+ /** Navigation container */
151
+ nav?: PartStyleProps;
152
+ /** Navigation item wrapper */
153
+ navItem?: PartStyleProps;
154
+ /** Navigation button */
155
+ navBtn?: PartStyleProps;
156
+ /** Panels container */
157
+ panels?: PartStyleProps;
158
+ /** Individual panel */
159
+ panel?: PartStyleProps;
160
+ /** Panel body/content */
161
+ panelBody?: PartStyleProps;
162
+ };
163
+
164
+ /**
165
+ * Progress component parts
166
+ */
167
+ export type ProgressParts = ComponentParts<'root' | 'bar' | 'label'> & {
168
+ /** Root progress container */
169
+ root?: PartStyleProps;
170
+ /** Progress bar element */
171
+ bar?: PartStyleProps;
172
+ /** Label/text element */
173
+ label?: PartStyleProps;
174
+ };
175
+
176
+ /**
177
+ * Tooltip component parts
178
+ */
179
+ export type TooltipParts = ComponentParts<'root' | 'trigger' | 'content' | 'arrow'> & {
180
+ /** Root tooltip container */
181
+ root?: PartStyleProps;
182
+ /** Trigger element */
183
+ trigger?: PartStyleProps;
184
+ /** Tooltip content */
185
+ content?: PartStyleProps;
186
+ /** Arrow element */
187
+ arrow?: PartStyleProps;
188
+ };
189
+
190
+ /**
191
+ * Select component parts
192
+ */
193
+ export type SelectParts = ComponentParts<'root' | 'select' | 'wrapper' | 'icon' | 'option'> & {
194
+ /** Root container */
195
+ root?: PartStyleProps;
196
+ /** Select element */
197
+ select?: PartStyleProps;
198
+ /** Select wrapper */
199
+ wrapper?: PartStyleProps;
200
+ /** Dropdown icon */
201
+ icon?: PartStyleProps;
202
+ /** Option element */
203
+ option?: PartStyleProps;
204
+ };
205
+
206
+ /**
207
+ * Checkbox component parts
208
+ */
209
+ export type CheckboxParts = ComponentParts<'root' | 'input' | 'box' | 'icon' | 'label'> & {
210
+ /** Root container */
211
+ root?: PartStyleProps;
212
+ /** Input element */
213
+ input?: PartStyleProps;
214
+ /** Checkbox box */
215
+ box?: PartStyleProps;
216
+ /** Check icon */
217
+ icon?: PartStyleProps;
218
+ /** Label text */
219
+ label?: PartStyleProps;
220
+ };
221
+
222
+ /**
223
+ * Radio component parts
224
+ */
225
+ export type RadioParts = ComponentParts<'root' | 'input' | 'circle' | 'dot' | 'label'> & {
226
+ /** Root container */
227
+ root?: PartStyleProps;
228
+ /** Input element */
229
+ input?: PartStyleProps;
230
+ /** Radio circle */
231
+ circle?: PartStyleProps;
232
+ /** Inner dot */
233
+ dot?: PartStyleProps;
234
+ /** Label text */
235
+ label?: PartStyleProps;
236
+ };
237
+
238
+ /**
239
+ * Textarea component parts
240
+ */
241
+ export type TextareaParts = ComponentParts<'root' | 'textarea' | 'wrapper'> & {
242
+ /** Root container */
243
+ root?: PartStyleProps;
244
+ /** Textarea element */
245
+ textarea?: PartStyleProps;
246
+ /** Textarea wrapper */
247
+ wrapper?: PartStyleProps;
248
+ };
249
+
250
+ /**
251
+ * FormGroup component parts
252
+ */
253
+ export type FormGroupParts = ComponentParts<'root' | 'label' | 'input' | 'helper' | 'error'> & {
254
+ /** Root container */
255
+ root?: PartStyleProps;
256
+ /** Label element */
257
+ label?: PartStyleProps;
258
+ /** Input wrapper */
259
+ input?: PartStyleProps;
260
+ /** Helper text */
261
+ helper?: PartStyleProps;
262
+ /** Error message */
263
+ error?: PartStyleProps;
264
+ };
265
+
266
+ /**
267
+ * Navbar component parts
268
+ */
269
+ export type NavbarParts = ComponentParts<
270
+ 'root' | 'container' | 'brand' | 'nav' | 'item' | 'link' | 'toggle'
271
+ > & {
272
+ /** Root navbar */
273
+ root?: PartStyleProps;
274
+ /** Container */
275
+ container?: PartStyleProps;
276
+ /** Brand element */
277
+ brand?: PartStyleProps;
278
+ /** Navigation */
279
+ nav?: PartStyleProps;
280
+ /** Nav item */
281
+ item?: PartStyleProps;
282
+ /** Nav link */
283
+ link?: PartStyleProps;
284
+ /** Mobile toggle */
285
+ toggle?: PartStyleProps;
286
+ };
287
+
288
+ /**
289
+ * Accordion component parts
290
+ */
291
+ export type AccordionParts = ComponentParts<
292
+ 'root' | 'item' | 'header' | 'trigger' | 'icon' | 'panel' | 'content'
293
+ > & {
294
+ /** Root accordion */
295
+ root?: PartStyleProps;
296
+ /** Accordion item */
297
+ item?: PartStyleProps;
298
+ /** Item header */
299
+ header?: PartStyleProps;
300
+ /** Trigger button */
301
+ trigger?: PartStyleProps;
302
+ /** Expand icon */
303
+ icon?: PartStyleProps;
304
+ /** Content panel */
305
+ panel?: PartStyleProps;
306
+ /** Panel content */
307
+ content?: PartStyleProps;
308
+ };
309
+
310
+ /**
311
+ * DataTable component parts
312
+ */
313
+ export type DataTableParts = ComponentParts<
314
+ 'root' | 'wrapper' | 'table' | 'thead' | 'tbody' | 'tfoot' | 'tr' | 'th' | 'td'
315
+ > & {
316
+ /** Root container */
317
+ root?: PartStyleProps;
318
+ /** Table wrapper */
319
+ wrapper?: PartStyleProps;
320
+ /** Table element */
321
+ table?: PartStyleProps;
322
+ /** Table head */
323
+ thead?: PartStyleProps;
324
+ /** Table body */
325
+ tbody?: PartStyleProps;
326
+ /** Table foot */
327
+ tfoot?: PartStyleProps;
328
+ /** Table row */
329
+ tr?: PartStyleProps;
330
+ /** Table header cell */
331
+ th?: PartStyleProps;
332
+ /** Table data cell */
333
+ td?: PartStyleProps;
334
+ };
335
+
336
+ /**
337
+ * Avatar component parts
338
+ */
339
+ export type AvatarParts = ComponentParts<'root' | 'image' | 'fallback' | 'badge'> & {
340
+ /** Root avatar */
341
+ root?: PartStyleProps;
342
+ /** Image element */
343
+ image?: PartStyleProps;
344
+ /** Fallback element */
345
+ fallback?: PartStyleProps;
346
+ /** Status badge */
347
+ badge?: PartStyleProps;
348
+ };
349
+
350
+ /**
351
+ * List component parts
352
+ */
353
+ export type ListParts = ComponentParts<
354
+ 'root' | 'item' | 'icon' | 'content' | 'title' | 'description'
355
+ > & {
356
+ /** Root list */
357
+ root?: PartStyleProps;
358
+ /** List item */
359
+ item?: PartStyleProps;
360
+ /** Item icon */
361
+ icon?: PartStyleProps;
362
+ /** Item content */
363
+ content?: PartStyleProps;
364
+ /** Item title */
365
+ title?: PartStyleProps;
366
+ /** Item description */
367
+ description?: PartStyleProps;
368
+ };
369
+
370
+ /**
371
+ * Utility function to merge part styles
372
+ */
373
+ export function mergePartStyles(
374
+ base?: PartStyleProps,
375
+ override?: PartStyleProps
376
+ ): PartStyleProps | undefined {
377
+ if (!base && !override) return undefined;
378
+ if (!base) return override;
379
+ if (!override) return base;
380
+
381
+ return {
382
+ className: [base.className, override.className].filter(Boolean).join(' '),
383
+ style: { ...base.style, ...override.style },
384
+ };
385
+ }
386
+
387
+ /**
388
+ * Utility function to apply part styles to props
389
+ */
390
+ export function applyPartStyles<T extends Record<string, any>>(
391
+ props: T,
392
+ part?: PartStyleProps
393
+ ): T {
394
+ if (!part) return props;
395
+
396
+ return {
397
+ ...props,
398
+ className: [props.className, part.className].filter(Boolean).join(' '),
399
+ style: { ...props.style, ...part.style },
400
+ };
401
+ }
402
+
403
+ /**
404
+ * Map of component names to their part types
405
+ */
406
+ export type ComponentPartsMap = {
407
+ Button: ButtonParts;
408
+ Card: CardParts;
409
+ Input: InputParts;
410
+ Modal: ModalParts;
411
+ Dropdown: DropdownParts;
412
+ Badge: BadgeParts;
413
+ Tabs: TabsParts;
414
+ Progress: ProgressParts;
415
+ Tooltip: TooltipParts;
416
+ Select: SelectParts;
417
+ Checkbox: CheckboxParts;
418
+ Radio: RadioParts;
419
+ Textarea: TextareaParts;
420
+ FormGroup: FormGroupParts;
421
+ Navbar: NavbarParts;
422
+ Accordion: AccordionParts;
423
+ DataTable: DataTableParts;
424
+ Avatar: AvatarParts;
425
+ List: ListParts;
426
+ };
@@ -0,0 +1,144 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ mergeClassNames,
4
+ applyPartStyles,
5
+ createCSSVarStyle,
6
+ mergeComponentProps,
7
+ } from '../componentUtils';
8
+
9
+ describe('componentUtils', () => {
10
+ describe('mergeClassNames', () => {
11
+ it('should merge multiple class names', () => {
12
+ const result = mergeClassNames('class1', 'class2', 'class3');
13
+ expect(result).toBe('class1 class2 class3');
14
+ });
15
+
16
+ it('should filter out falsy values', () => {
17
+ const result = mergeClassNames('class1', undefined, null, false, '', 'class2');
18
+ expect(result).toBe('class1 class2');
19
+ });
20
+
21
+ it('should handle empty input', () => {
22
+ const result = mergeClassNames();
23
+ expect(result).toBe('');
24
+ });
25
+
26
+ it('should handle all falsy values', () => {
27
+ const result = mergeClassNames(undefined, null, false, '');
28
+ expect(result).toBe('');
29
+ });
30
+ });
31
+
32
+ describe('applyPartStyles', () => {
33
+ it('should merge className', () => {
34
+ const baseProps = { className: 'base' };
35
+ const partStyles = { className: 'custom' };
36
+ const result = applyPartStyles(baseProps, partStyles);
37
+ expect(result.className).toBe('base custom');
38
+ });
39
+
40
+ it('should merge style objects', () => {
41
+ const baseProps = { style: { color: 'red' } };
42
+ const partStyles = { style: { fontSize: '16px' } };
43
+ const result = applyPartStyles(baseProps, partStyles);
44
+ expect(result.style).toEqual({ color: 'red', fontSize: '16px' });
45
+ });
46
+
47
+ it('should handle undefined partStyles', () => {
48
+ const baseProps = { className: 'base', style: { color: 'red' } };
49
+ const result = applyPartStyles(baseProps, undefined);
50
+ expect(result).toEqual(baseProps);
51
+ });
52
+
53
+ it('should preserve other props', () => {
54
+ const baseProps = { className: 'base', id: 'test', 'data-test': 'value' };
55
+ const partStyles = { className: 'custom' };
56
+ const result = applyPartStyles(baseProps, partStyles);
57
+ expect(result).toEqual({
58
+ className: 'base custom',
59
+ id: 'test',
60
+ 'data-test': 'value',
61
+ });
62
+ });
63
+ });
64
+
65
+ describe('createCSSVarStyle', () => {
66
+ it('should create style object from CSS variables', () => {
67
+ const cssVars = {
68
+ '--atomix-button-bg': '#FF0000',
69
+ '--atomix-button-border-radius': '20px',
70
+ };
71
+ const result = createCSSVarStyle(cssVars);
72
+ expect(result).toEqual({
73
+ '--atomix-button-bg': '#FF0000',
74
+ '--atomix-button-border-radius': '20px',
75
+ });
76
+ });
77
+
78
+ it('should merge with base style', () => {
79
+ const cssVars = {
80
+ '--atomix-button-bg': '#FF0000',
81
+ };
82
+ const baseStyle = { marginTop: '10px' };
83
+ const result = createCSSVarStyle(cssVars, baseStyle);
84
+ expect(result).toEqual({
85
+ '--atomix-button-bg': '#FF0000',
86
+ marginTop: '10px',
87
+ });
88
+ });
89
+
90
+ it('should handle numeric values', () => {
91
+ const cssVars = {
92
+ '--atomix-button-padding-x': 16,
93
+ '--atomix-button-padding-y': 8,
94
+ };
95
+ const result = createCSSVarStyle(cssVars);
96
+ expect(result).toEqual({
97
+ '--atomix-button-padding-x': 16,
98
+ '--atomix-button-padding-y': 8,
99
+ });
100
+ });
101
+
102
+ it('should handle empty cssVars', () => {
103
+ const result = createCSSVarStyle(undefined);
104
+ expect(result).toEqual({});
105
+ });
106
+ });
107
+
108
+ describe('mergeComponentProps', () => {
109
+ it('should merge className', () => {
110
+ const baseProps = { className: 'base' };
111
+ const customization = { className: 'custom' };
112
+ const result = mergeComponentProps(baseProps, customization);
113
+ expect(result.className).toBe('base custom');
114
+ });
115
+
116
+ it('should merge style with CSS variables', () => {
117
+ const baseProps = { style: { color: 'red' } };
118
+ const customization = {
119
+ style: { fontSize: '16px' },
120
+ cssVars: { '--atomix-button-bg': '#FF0000' },
121
+ };
122
+ const result = mergeComponentProps(baseProps, customization);
123
+ expect(result.style).toEqual({
124
+ color: 'red',
125
+ fontSize: '16px',
126
+ '--atomix-button-bg': '#FF0000',
127
+ });
128
+ });
129
+
130
+ it('should preserve other props', () => {
131
+ const baseProps = { className: 'base', id: 'test', onClick: () => {} };
132
+ const customization = { className: 'custom' };
133
+ const result = mergeComponentProps(baseProps, customization);
134
+ expect(result.id).toBe('test');
135
+ expect(result.onClick).toBeDefined();
136
+ });
137
+
138
+ it('should handle empty customization', () => {
139
+ const baseProps = { className: 'base', style: { color: 'red' } };
140
+ const result = mergeComponentProps(baseProps, {});
141
+ expect(result).toEqual(baseProps);
142
+ });
143
+ });
144
+ });