@shohojdhara/atomix 0.3.15 → 0.4.0

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 (245) hide show
  1. package/build-tools/index.d.ts +31 -30
  2. package/build-tools/package.json +4 -21
  3. package/dist/atomix.css +20924 -2611
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +76 -2
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/index.d.ts +31 -30
  8. package/dist/build-tools/package.json +4 -21
  9. package/dist/charts.js.map +1 -1
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js.map +1 -1
  12. package/dist/heavy.js.map +1 -1
  13. package/dist/index.d.ts +144 -18
  14. package/dist/index.esm.js +110 -55
  15. package/dist/index.esm.js.map +1 -1
  16. package/dist/index.js +110 -55
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.min.js +1 -1
  19. package/dist/index.min.js.map +1 -1
  20. package/dist/layout.js.map +1 -1
  21. package/dist/theme.d.ts +9 -9
  22. package/dist/theme.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/Accordion/Accordion.stories.tsx +32 -23
  25. package/src/components/Accordion/Accordion.test.tsx +70 -50
  26. package/src/components/Accordion/Accordion.tsx +99 -94
  27. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  28. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  29. package/src/components/AtomixGlass/glass-utils.ts +4 -3
  30. package/src/components/AtomixGlass/shader-utils.ts +128 -52
  31. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
  32. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
  33. package/src/components/Avatar/Avatar.stories.tsx +45 -62
  34. package/src/components/Avatar/Avatar.tsx +58 -56
  35. package/src/components/Badge/Badge.stories.tsx +20 -9
  36. package/src/components/Badge/Badge.test.tsx +41 -41
  37. package/src/components/Badge/Badge.tsx +64 -62
  38. package/src/components/Block/Block.stories.tsx +14 -4
  39. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
  40. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
  41. package/src/components/Button/Button.stories.tsx +13 -22
  42. package/src/components/Button/Button.test.tsx +97 -81
  43. package/src/components/Button/Button.tsx +46 -14
  44. package/src/components/Button/ButtonGroup.stories.tsx +37 -32
  45. package/src/components/Button/ButtonGroup.tsx +4 -15
  46. package/src/components/Callout/Callout.stories.tsx +109 -16
  47. package/src/components/Card/Card.stories.tsx +67 -36
  48. package/src/components/Card/Card.tsx +30 -14
  49. package/src/components/Chart/AreaChart.tsx +1 -1
  50. package/src/components/Chart/CandlestickChart.tsx +23 -16
  51. package/src/components/Chart/Chart.stories.tsx +4 -9
  52. package/src/components/Chart/Chart.tsx +40 -44
  53. package/src/components/Chart/ChartRenderer.tsx +39 -12
  54. package/src/components/Chart/ChartToolbar.tsx +21 -5
  55. package/src/components/Chart/DonutChart.tsx +1 -1
  56. package/src/components/Chart/FunnelChart.tsx +4 -1
  57. package/src/components/Chart/GaugeChart.tsx +3 -1
  58. package/src/components/Chart/HeatmapChart.tsx +50 -37
  59. package/src/components/Chart/LineChart.tsx +3 -2
  60. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  61. package/src/components/Chart/RadarChart.tsx +19 -17
  62. package/src/components/Chart/ScatterChart.tsx +29 -21
  63. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
  64. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  65. package/src/components/Countdown/Countdown.stories.tsx +7 -7
  66. package/src/components/DataTable/DataTable.stories.tsx +43 -38
  67. package/src/components/DataTable/DataTable.test.tsx +26 -148
  68. package/src/components/DataTable/DataTable.tsx +485 -456
  69. package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
  70. package/src/components/DatePicker/DatePicker.tsx +31 -26
  71. package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
  72. package/src/components/Dropdown/Dropdown.tsx +313 -299
  73. package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
  74. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  75. package/src/components/Footer/Footer.stories.tsx +21 -16
  76. package/src/components/Footer/Footer.tsx +130 -128
  77. package/src/components/Footer/FooterLink.tsx +2 -2
  78. package/src/components/Form/Checkbox.test.tsx +49 -49
  79. package/src/components/Form/Checkbox.tsx +108 -100
  80. package/src/components/Form/Form.stories.tsx +2 -10
  81. package/src/components/Form/Input.stories.tsx +22 -39
  82. package/src/components/Form/Input.test.tsx +38 -44
  83. package/src/components/Form/Radio.stories.tsx +6 -12
  84. package/src/components/Form/Radio.tsx +68 -66
  85. package/src/components/Form/Select.tsx +184 -182
  86. package/src/components/Form/Textarea.test.tsx +27 -32
  87. package/src/components/Hero/Hero.stories.tsx +56 -23
  88. package/src/components/Hero/Hero.tsx +201 -55
  89. package/src/components/Icon/index.ts +7 -1
  90. package/src/components/List/List.tsx +19 -23
  91. package/src/components/Modal/Modal.stories.tsx +2 -1
  92. package/src/components/Modal/Modal.tsx +130 -127
  93. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  94. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  95. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  96. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  97. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  98. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  99. package/src/components/Pagination/Pagination.tsx +199 -202
  100. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  101. package/src/components/Popover/Popover.stories.tsx +99 -192
  102. package/src/components/Popover/Popover.tsx +41 -37
  103. package/src/components/Progress/Progress.stories.tsx +35 -44
  104. package/src/components/River/River.stories.tsx +2 -1
  105. package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
  106. package/src/components/Slider/Slider.stories.tsx +12 -4
  107. package/src/components/Spinner/Spinner.stories.tsx +3 -1
  108. package/src/components/Spinner/Spinner.test.tsx +23 -23
  109. package/src/components/Spinner/Spinner.tsx +43 -46
  110. package/src/components/Steps/Steps.stories.tsx +8 -6
  111. package/src/components/Tabs/Tabs.stories.tsx +12 -9
  112. package/src/components/Tabs/Tabs.tsx +74 -72
  113. package/src/components/Toggle/Toggle.stories.tsx +27 -13
  114. package/src/components/Toggle/Toggle.test.tsx +65 -70
  115. package/src/components/Toggle/Toggle.tsx +4 -1
  116. package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
  117. package/src/components/Tooltip/Tooltip.tsx +104 -106
  118. package/src/components/Upload/Upload.stories.tsx +129 -127
  119. package/src/components/Upload/Upload.tsx +287 -283
  120. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  121. package/src/components/index.ts +13 -2
  122. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  123. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  124. package/src/lib/__tests__/theme-tools.test.ts +32 -6
  125. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  126. package/src/lib/composables/useAtomixGlass.ts +106 -49
  127. package/src/lib/composables/useChartExport.ts +1 -1
  128. package/src/lib/composables/useDataTable.ts +29 -17
  129. package/src/lib/composables/useHero.ts +58 -14
  130. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  131. package/src/lib/composables/useInput.ts +10 -8
  132. package/src/lib/composables/useSideMenu.ts +6 -5
  133. package/src/lib/composables/useTooltip.ts +1 -2
  134. package/src/lib/composables/useVideoPlayer.ts +44 -35
  135. package/src/lib/config/index.ts +154 -154
  136. package/src/lib/constants/cssVariables.ts +29 -29
  137. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  138. package/src/lib/hooks/index.ts +1 -1
  139. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  140. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  141. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  142. package/src/lib/patterns/index.ts +1 -1
  143. package/src/lib/patterns/slots.tsx +8 -13
  144. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  145. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  146. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  147. package/src/lib/storybook/index.ts +1 -1
  148. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  149. package/src/lib/theme/adapters/index.ts +3 -9
  150. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  151. package/src/lib/theme/config/index.ts +1 -1
  152. package/src/lib/theme/config/types.ts +2 -2
  153. package/src/lib/theme/config/validator.ts +10 -5
  154. package/src/lib/theme/constants/constants.ts +2 -2
  155. package/src/lib/theme/constants/index.ts +1 -2
  156. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  157. package/src/lib/theme/core/composeTheme.ts +32 -26
  158. package/src/lib/theme/core/createTheme.ts +1 -1
  159. package/src/lib/theme/core/createThemeObject.ts +308 -301
  160. package/src/lib/theme/core/index.ts +3 -3
  161. package/src/lib/theme/devtools/CLI.ts +106 -104
  162. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  163. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  164. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  165. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  166. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  167. package/src/lib/theme/devtools/Preview.tsx +150 -106
  168. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  169. package/src/lib/theme/devtools/index.ts +3 -9
  170. package/src/lib/theme/devtools/useHistory.ts +23 -21
  171. package/src/lib/theme/errors/errors.ts +12 -11
  172. package/src/lib/theme/errors/index.ts +2 -7
  173. package/src/lib/theme/generators/generateCSS.ts +9 -13
  174. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  175. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  176. package/src/lib/theme/generators/index.ts +1 -4
  177. package/src/lib/theme/i18n/index.ts +1 -1
  178. package/src/lib/theme/i18n/rtl.ts +13 -13
  179. package/src/lib/theme/index.ts +7 -16
  180. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  181. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  182. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  183. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  184. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  185. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  186. package/src/lib/theme/runtime/index.ts +2 -5
  187. package/src/lib/theme/runtime/useTheme.ts +18 -18
  188. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  189. package/src/lib/theme/test/testTheme.ts +15 -16
  190. package/src/lib/theme/tokens/index.ts +2 -7
  191. package/src/lib/theme/tokens/tokens.ts +25 -24
  192. package/src/lib/theme/types.ts +428 -411
  193. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  194. package/src/lib/theme/utils/componentTheming.ts +18 -18
  195. package/src/lib/theme/utils/domUtils.ts +277 -289
  196. package/src/lib/theme/utils/index.ts +1 -2
  197. package/src/lib/theme/utils/injectCSS.ts +10 -14
  198. package/src/lib/theme/utils/naming.ts +20 -16
  199. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  200. package/src/lib/theme/utils/themeUtils.ts +85 -86
  201. package/src/lib/theme/utils/themeValidation.ts +82 -33
  202. package/src/lib/theme-tools.ts +8 -6
  203. package/src/lib/types/components.ts +172 -71
  204. package/src/lib/types/partProps.ts +1 -1
  205. package/src/lib/utils/__tests__/csv.test.ts +1 -1
  206. package/src/lib/utils/componentUtils.ts +8 -12
  207. package/src/lib/utils/csv.ts +3 -1
  208. package/src/lib/utils/dataTableExport.ts +1 -5
  209. package/src/lib/utils/fontPreloader.ts +10 -19
  210. package/src/lib/utils/icons.ts +4 -1
  211. package/src/lib/utils/index.ts +2 -6
  212. package/src/lib/utils/memoryMonitor.ts +10 -8
  213. package/src/lib/utils/themeNaming.ts +2 -2
  214. package/src/styles/01-settings/_index.scss +0 -1
  215. package/src/styles/01-settings/_settings.colors.scss +8 -8
  216. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  217. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  218. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  219. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  220. package/src/styles/01-settings/_settings.typography.scss +1 -1
  221. package/src/styles/02-tools/_tools.button.scss +51 -21
  222. package/src/styles/02-tools/_tools.utility-api.scss +30 -18
  223. package/src/styles/03-generic/_generic.root.scss +4 -3
  224. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  225. package/src/styles/06-components/_components.button.scss +16 -4
  226. package/src/styles/06-components/_components.callout.scss +27 -21
  227. package/src/styles/06-components/_components.card.scss +5 -14
  228. package/src/styles/06-components/_components.chart.scss +22 -19
  229. package/src/styles/06-components/_components.checkbox.scss +3 -1
  230. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  231. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  232. package/src/styles/06-components/_components.footer.scss +1 -1
  233. package/src/styles/06-components/_components.side-menu.scss +5 -5
  234. package/src/styles/06-components/_components.toggle.scss +18 -0
  235. package/src/styles/06-components/_index.scss +1 -1
  236. package/src/styles/06-components/old.chart.styles.scss +0 -2
  237. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  238. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  239. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  240. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  241. package/src/styles/99-utilities/_utilities.scss +1 -1
  242. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  243. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  244. package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
  245. package/src/styles/99-utilities/_utilities.text.scss +67 -46
@@ -8,100 +8,116 @@ expect.extend(toHaveNoViolations);
8
8
 
9
9
  // Mock AtomixGlass component
10
10
  vi.mock('../AtomixGlass/AtomixGlass', () => ({
11
- AtomixGlass: ({ children, ...props }: any) => (
12
- <div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
13
- {children}
14
- </div>
15
- ),
11
+ AtomixGlass: ({ children, ...props }: any) => (
12
+ <div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
13
+ {children}
14
+ </div>
15
+ ),
16
16
  }));
17
17
 
18
18
  describe('Button Component', () => {
19
- it('renders correctly with label', () => {
20
- render(<Button label="Click Me" />);
21
- expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
22
- });
19
+ it('renders correctly with label', () => {
20
+ render(<Button label="Click Me" />);
21
+ expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
22
+ });
23
23
 
24
- it('renders correctly with children', () => {
25
- render(<Button>Click Me</Button>);
26
- expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
27
- });
24
+ it('renders correctly with children', () => {
25
+ render(<Button>Click Me</Button>);
26
+ expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
27
+ });
28
28
 
29
- it('handles onClick event', () => {
30
- const handleClick = vi.fn();
31
- render(<Button onClick={handleClick}>Click Me</Button>);
32
- fireEvent.click(screen.getByRole('button'));
33
- expect(handleClick).toHaveBeenCalledTimes(1);
34
- });
29
+ it('handles onClick event', () => {
30
+ const handleClick = vi.fn();
31
+ render(<Button onClick={handleClick}>Click Me</Button>);
32
+ fireEvent.click(screen.getByRole('button'));
33
+ expect(handleClick).toHaveBeenCalledTimes(1);
34
+ });
35
35
 
36
- it('does not fire onClick when disabled', () => {
37
- const handleClick = vi.fn();
38
- render(<Button disabled onClick={handleClick}>Click Me</Button>);
39
- const button = screen.getByRole('button');
40
- expect(button).toBeDisabled();
41
- fireEvent.click(button);
42
- expect(handleClick).not.toHaveBeenCalled();
43
- });
36
+ it('does not fire onClick when disabled', () => {
37
+ const handleClick = vi.fn();
38
+ render(
39
+ <Button disabled onClick={handleClick}>
40
+ Click Me
41
+ </Button>
42
+ );
43
+ const button = screen.getByRole('button');
44
+ expect(button).toBeDisabled();
45
+ fireEvent.click(button);
46
+ expect(handleClick).not.toHaveBeenCalled();
47
+ });
44
48
 
45
- it('renders loading state correctly', () => {
46
- render(<Button loading>Submit</Button>);
47
- const button = screen.getByRole('button');
48
- // It should be disabled or aria-disabled
49
- expect(button).toHaveAttribute('aria-busy', 'true');
50
- // Check for spinner (implementation detail: spinner usually has specific class or role)
51
- // Based on Button.tsx: <span className="...spinner...">...</span>
52
- // Let's look for the spinner component or class if we can't find by role
53
- // Or check if text is still there.
54
- expect(screen.getByText('Submit')).toBeInTheDocument();
55
- });
49
+ it('renders loading state correctly', () => {
50
+ render(<Button loading>Submit</Button>);
51
+ const button = screen.getByRole('button');
52
+ // It should be disabled or aria-disabled
53
+ expect(button).toHaveAttribute('aria-busy', 'true');
54
+ // Check for spinner (implementation detail: spinner usually has specific class or role)
55
+ // Based on Button.tsx: <span className="...spinner...">...</span>
56
+ // Let's look for the spinner component or class if we can't find by role
57
+ // Or check if text is still there.
58
+ expect(screen.getByText('Submit')).toBeInTheDocument();
59
+ });
56
60
 
57
- it('replaces text with loadingText when loading', () => {
58
- render(<Button loading loadingText="Processing...">Submit</Button>);
59
- expect(screen.queryByText('Submit')).not.toBeInTheDocument();
60
- expect(screen.getByText('Processing...')).toBeInTheDocument();
61
- });
61
+ it('replaces text with loadingText when loading', () => {
62
+ render(
63
+ <Button loading loadingText="Processing...">
64
+ Submit
65
+ </Button>
66
+ );
67
+ expect(screen.queryByText('Submit')).not.toBeInTheDocument();
68
+ expect(screen.getByText('Processing...')).toBeInTheDocument();
69
+ });
62
70
 
63
- it('renders as a link when href is provided', () => {
64
- render(<Button href="/home">Home</Button>);
65
- const link = screen.getByRole('link', { name: /home/i });
66
- expect(link).toHaveAttribute('href', '/home');
67
- });
71
+ it('renders as a link when href is provided', () => {
72
+ render(<Button href="/home">Home</Button>);
73
+ const link = screen.getByRole('link', { name: /home/i });
74
+ expect(link).toHaveAttribute('href', '/home');
75
+ });
68
76
 
69
- it('renders as disabled link when disabled and href provided', () => {
70
- // Current implementation might be buggy here, let's see
71
- render(<Button href="/home" disabled>Home</Button>);
72
- const link = screen.queryByRole('link');
73
- const button = screen.queryByRole('button');
77
+ it('renders as disabled link when disabled and href provided', () => {
78
+ // Current implementation might be buggy here, let's see
79
+ render(
80
+ <Button href="/home" disabled>
81
+ Home
82
+ </Button>
83
+ );
84
+ const link = screen.queryByRole('link');
85
+ const button = screen.queryByRole('button');
74
86
 
75
- // If it renders as button when disabled (which logic suggested), then:
76
- if (button) {
77
- expect(button).toBeDisabled();
78
- } else if (link) {
79
- expect(link).toHaveAttribute('aria-disabled', 'true');
80
- // Should not navigate
81
- }
82
- });
87
+ // If it renders as button when disabled (which logic suggested), then:
88
+ if (button) {
89
+ expect(button).toBeDisabled();
90
+ } else if (link) {
91
+ expect(link).toHaveAttribute('aria-disabled', 'true');
92
+ // Should not navigate
93
+ }
94
+ });
83
95
 
84
- it('forwards ref', () => {
85
- const ref = React.createRef<HTMLButtonElement>();
86
- render(<Button ref={ref}>Ref Button</Button>);
87
- expect(ref.current).toBeInstanceOf(HTMLButtonElement);
88
- });
96
+ it('forwards ref', () => {
97
+ const ref = React.createRef<HTMLButtonElement>();
98
+ render(<Button ref={ref}>Ref Button</Button>);
99
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement);
100
+ });
89
101
 
90
- it('handles iconOnly correctly with aria-label', () => {
91
- render(<Button iconOnly icon={<span>Icon</span>} aria-label="Icon Button" />);
92
- expect(screen.getByLabelText('Icon Button')).toBeInTheDocument();
93
- });
102
+ it('handles iconOnly correctly with aria-label', () => {
103
+ render(<Button iconOnly icon={<span>Icon</span>} aria-label="Icon Button" />);
104
+ expect(screen.getByLabelText('Icon Button')).toBeInTheDocument();
105
+ });
94
106
 
95
- it('does not render [object Object] as aria-label when children is an element', () => {
96
- const { container } = render(<Button iconOnly icon={<span>Icon</span>}><span>Text</span></Button>);
97
- const button = screen.getByRole('button');
98
- // aria-label should probably be undefined or empty, or extracted text, but definitely not "[object Object]"
99
- expect(button.getAttribute('aria-label')).not.toBe('[object Object]');
100
- });
107
+ it('does not render [object Object] as aria-label when children is an element', () => {
108
+ const { container } = render(
109
+ <Button iconOnly icon={<span>Icon</span>}>
110
+ <span>Text</span>
111
+ </Button>
112
+ );
113
+ const button = screen.getByRole('button');
114
+ // aria-label should probably be undefined or empty, or extracted text, but definitely not "[object Object]"
115
+ expect(button.getAttribute('aria-label')).not.toBe('[object Object]');
116
+ });
101
117
 
102
- it('should have no accessibility violations', async () => {
103
- const { container } = render(<Button>Accessible Button</Button>);
104
- const results = await axe(container);
105
- expect(results).toHaveNoViolations();
106
- });
118
+ it('should have no accessibility violations', async () => {
119
+ const { container } = render(<Button>Accessible Button</Button>);
120
+ const results = await axe(container);
121
+ expect(results).toHaveNoViolations();
122
+ });
107
123
  });
@@ -65,13 +65,19 @@ export const Button = React.memo(
65
65
  const shouldRenderAsLink = Boolean(href && !isDisabled);
66
66
 
67
67
  // Resolve icon element - support both icon (ReactNode) and iconName (string)
68
- const iconElement = iconName ? <Icon name={iconName as PhosphorIconsType} size={iconSize} /> : icon;
68
+ const iconElement = iconName ? (
69
+ <Icon name={iconName as PhosphorIconsType} size={iconSize} />
70
+ ) : (
71
+ icon
72
+ );
69
73
 
70
74
  const buttonClass = [
71
75
  BUTTON.BASE_CLASS,
72
76
  ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
73
77
  size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
74
- iconOnly ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT) : '',
78
+ iconOnly
79
+ ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)
80
+ : '',
75
81
  rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
76
82
  isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
77
83
  glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
@@ -134,30 +140,60 @@ export const Button = React.memo(
134
140
  const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
135
141
 
136
142
  // Safe Aria Label
137
- const safeAriaLabel = ariaLabel || (iconOnly ? (typeof label === 'string' ? label : (typeof children === 'string' ? children : undefined)) : undefined);
143
+ const safeAriaLabel =
144
+ ariaLabel ||
145
+ (iconOnly
146
+ ? typeof label === 'string'
147
+ ? label
148
+ : typeof children === 'string'
149
+ ? children
150
+ : undefined
151
+ : undefined);
138
152
 
139
153
  // Button content with icon positioning
140
154
  const buttonContent = (
141
155
  <>
142
156
  {loading && (
143
- <span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.SPINNER_ELEMENT)} aria-hidden="true">
157
+ <span
158
+ className={ThemeNaming.bemClass(
159
+ THEME_NAMING.BUTTON_PREFIX,
160
+ THEME_NAMING.SPINNER_ELEMENT
161
+ )}
162
+ aria-hidden="true"
163
+ >
144
164
  <Spinner
145
165
  size={spinnerSize}
146
166
  variant={
147
- variant === 'link' || (typeof variant === 'string' && variant.startsWith('outline-'))
167
+ variant === 'link' ||
168
+ (typeof variant === 'string' && variant.startsWith('outline-'))
148
169
  ? 'primary'
149
- : (variant === 'danger' ? 'error' : (variant as any))
170
+ : variant === 'danger'
171
+ ? 'error'
172
+ : (variant as any)
150
173
  }
151
174
  />
152
175
  </span>
153
176
  )}
154
177
  {iconElement && !loading && (
155
- <span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)} aria-hidden="true">
178
+ <span
179
+ className={ThemeNaming.bemClass(
180
+ THEME_NAMING.BUTTON_PREFIX,
181
+ THEME_NAMING.ICON_ELEMENT
182
+ )}
183
+ aria-hidden="true"
184
+ >
156
185
  {iconElement}
157
186
  </span>
158
187
  )}
159
188
  {!iconOnly && buttonText && (
160
- <span className={ThemeNaming.bemClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.LABEL_ELEMENT)}>{buttonText}</span>
189
+ <span
190
+ className={ThemeNaming.bemClass(
191
+ THEME_NAMING.BUTTON_PREFIX,
192
+ THEME_NAMING.LABEL_ELEMENT
193
+ )}
194
+ >
195
+ {buttonText}
196
+ </span>
161
197
  )}
162
198
  </>
163
199
  );
@@ -175,7 +211,7 @@ export const Button = React.memo(
175
211
  'aria-describedby': ariaDescribedBy,
176
212
  'aria-expanded': ariaExpanded,
177
213
  'aria-controls': ariaControls,
178
- tabIndex: tabIndex !== undefined ? tabIndex : (isDisabled ? -1 : 0),
214
+ tabIndex: tabIndex !== undefined ? tabIndex : isDisabled ? -1 : 0,
179
215
  style,
180
216
  ...props,
181
217
  };
@@ -195,11 +231,7 @@ export const Button = React.memo(
195
231
  rel: target === '_blank' ? 'noopener noreferrer' : undefined,
196
232
  };
197
233
 
198
- content = (
199
- <LinkComp {...linkProps}>
200
- {buttonContent}
201
- </LinkComp>
202
- );
234
+ content = <LinkComp {...linkProps}>{buttonContent}</LinkComp>;
203
235
  } else {
204
236
  // Fallback to regular anchor tag
205
237
  content = (
@@ -38,7 +38,6 @@ const sampleButtonTitles = {
38
38
  filters: ['All', 'Active', 'Completed'],
39
39
  };
40
40
 
41
-
42
41
  // ============================================================================
43
42
  // META CONFIGURATION
44
43
  // ============================================================================
@@ -198,7 +197,7 @@ export const GroupWithIcons: Story = {
198
197
  <path d="M12 8v4M12 16h.01" />
199
198
  </svg>
200
199
  );
201
-
200
+
202
201
  return (
203
202
  <ButtonGroup>
204
203
  <Button label="First" icon={<Icon />} variant="primary" />
@@ -216,15 +215,15 @@ export const GroupWithIcons: Story = {
216
215
  export const InFormIntegration: Story = {
217
216
  render: () => (
218
217
  <form className="u-flex u-flex-col u-gap-4">
219
- <input
220
- type="text"
221
- placeholder="Username"
222
- className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
218
+ <input
219
+ type="text"
220
+ placeholder="Username"
221
+ className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
223
222
  />
224
- <input
225
- type="password"
226
- placeholder="Password"
227
- className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
223
+ <input
224
+ type="password"
225
+ placeholder="Password"
226
+ className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
228
227
  />
229
228
  <ButtonGroup className="u-justify-end">
230
229
  <Button label="Cancel" variant="secondary" />
@@ -258,7 +257,7 @@ export const WithOtherComponents: Story = {
258
257
  <Button label="Inactive" variant="outline-primary" />
259
258
  </ButtonGroup>
260
259
  </div>
261
-
260
+
262
261
  <div className="u-flex u-justify-between u-items-center">
263
262
  <div className="u-text-sm u-text-gray-600">Showing 1-10 of 42 results</div>
264
263
  <ButtonGroup>
@@ -278,24 +277,24 @@ export const ResponsiveButtonGroup: Story = {
278
277
  render: () => (
279
278
  <div className="u-w-full">
280
279
  <ButtonGroup className="u-flex-col md:u-flex-row u-gap-0">
281
- <Button
282
- label="Home"
283
- variant="outline-primary"
280
+ <Button
281
+ label="Home"
282
+ variant="outline-primary"
284
283
  className="u-w-full md:u-w-auto u-rounded-none md:u-rounded-l md:u-rounded-r-none"
285
284
  />
286
- <Button
287
- label="About"
288
- variant="outline-primary"
285
+ <Button
286
+ label="About"
287
+ variant="outline-primary"
289
288
  className="u-w-full md:u-w-auto u-rounded-none"
290
289
  />
291
- <Button
292
- label="Services"
293
- variant="outline-primary"
290
+ <Button
291
+ label="Services"
292
+ variant="outline-primary"
294
293
  className="u-w-full md:u-w-auto u-rounded-none"
295
294
  />
296
- <Button
297
- label="Contact"
298
- variant="outline-primary"
295
+ <Button
296
+ label="Contact"
297
+ variant="outline-primary"
299
298
  className="u-w-full md:u-w-auto u-rounded-none md:u-rounded-r md:u-rounded-l-none"
300
299
  />
301
300
  </ButtonGroup>
@@ -410,17 +409,17 @@ export const VerticalLayout: Story = {
410
409
  export const WithKeyboardNavigation: Story = {
411
410
  render: () => {
412
411
  const [activeIndex, setActiveIndex] = useState(0);
413
-
412
+
414
413
  const buttons = [
415
- { label: "Button 1", variant: "primary" },
416
- { label: "Button 2", variant: "secondary" },
417
- { label: "Button 3", variant: "outline-primary" },
418
- { label: "Button 4", variant: "success" },
414
+ { label: 'Button 1', variant: 'primary' },
415
+ { label: 'Button 2', variant: 'secondary' },
416
+ { label: 'Button 3', variant: 'outline-primary' },
417
+ { label: 'Button 4', variant: 'success' },
419
418
  ];
420
419
 
421
420
  const handleKeyDown = (index: number, e: React.KeyboardEvent) => {
422
421
  let newIndex = index;
423
-
422
+
424
423
  switch (e.key) {
425
424
  case 'ArrowRight':
426
425
  case 'ArrowDown':
@@ -439,7 +438,7 @@ export const WithKeyboardNavigation: Story = {
439
438
  default:
440
439
  return;
441
440
  }
442
-
441
+
443
442
  e.preventDefault();
444
443
  setActiveIndex(newIndex);
445
444
  };
@@ -454,7 +453,13 @@ export const WithKeyboardNavigation: Story = {
454
453
  variant={btn.variant}
455
454
  active={activeIndex === idx}
456
455
  onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(idx, e)}
457
- className={idx === 0 ? 'u-rounded-r-none' : idx === buttons.length - 1 ? 'u-rounded-l-none' : 'u-rounded-none'}
456
+ className={
457
+ idx === 0
458
+ ? 'u-rounded-r-none'
459
+ : idx === buttons.length - 1
460
+ ? 'u-rounded-l-none'
461
+ : 'u-rounded-none'
462
+ }
458
463
  />
459
464
  ))}
460
465
  </div>
@@ -468,4 +473,4 @@ export const WithKeyboardNavigation: Story = {
468
473
  },
469
474
  },
470
475
  },
471
- };
476
+ };
@@ -5,10 +5,10 @@ import { Button } from './Button';
5
5
 
6
6
  /**
7
7
  * ButtonGroup - A component for grouping buttons together
8
- *
8
+ *
9
9
  * Groups buttons together with proper border radius handling and spacing.
10
10
  * The buttons will be visually connected with shared borders.
11
- *
11
+ *
12
12
  * @example
13
13
  * ```tsx
14
14
  * <ButtonGroup>
@@ -26,12 +26,7 @@ export const ButtonGroup: React.FC<ButtonGroupProps> = ({
26
26
  role = 'group',
27
27
  }) => {
28
28
  // Generate CSS classes
29
- const buttonGroupClasses = [
30
- BUTTON_GROUP.CLASSES.BASE,
31
- className,
32
- ]
33
- .filter(Boolean)
34
- .join(' ');
29
+ const buttonGroupClasses = [BUTTON_GROUP.CLASSES.BASE, className].filter(Boolean).join(' ');
35
30
 
36
31
  // Get valid Button children
37
32
  const buttonChildren = Children.toArray(children).filter(
@@ -39,12 +34,7 @@ export const ButtonGroup: React.FC<ButtonGroupProps> = ({
39
34
  );
40
35
 
41
36
  return (
42
- <div
43
- className={buttonGroupClasses}
44
- style={style}
45
- role={role}
46
- aria-label={ariaLabel}
47
- >
37
+ <div className={buttonGroupClasses} style={style} role={role} aria-label={ariaLabel}>
48
38
  {buttonChildren.map((child, index) => {
49
39
  if (isValidElement<ButtonProps>(child)) {
50
40
  // Clone the Button element to ensure proper styling
@@ -64,4 +54,3 @@ export type { ButtonGroupProps };
64
54
  ButtonGroup.displayName = 'ButtonGroup';
65
55
 
66
56
  export default ButtonGroup;
67
-