@shohojdhara/atomix 0.3.14 → 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 (343) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build-tools/EXAMPLES.md +372 -0
  3. package/build-tools/README.md +242 -0
  4. package/build-tools/__tests__/error-handler.test.js +230 -0
  5. package/build-tools/__tests__/index.test.js +141 -0
  6. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  7. package/build-tools/__tests__/utils.test.js +161 -0
  8. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  9. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  10. package/build-tools/error-handler.js +308 -0
  11. package/build-tools/index.d.ts +44 -0
  12. package/build-tools/index.js +88 -0
  13. package/build-tools/package.json +50 -0
  14. package/build-tools/rollup-plugin.js +236 -0
  15. package/build-tools/types.d.ts +163 -0
  16. package/build-tools/utils.js +203 -0
  17. package/build-tools/vite-plugin.js +161 -0
  18. package/build-tools/webpack-loader.js +123 -0
  19. package/dist/atomix.css +21044 -2618
  20. package/dist/atomix.css.map +1 -1
  21. package/dist/atomix.min.css +77 -3
  22. package/dist/atomix.min.css.map +1 -1
  23. package/dist/build-tools/EXAMPLES.md +372 -0
  24. package/dist/build-tools/README.md +242 -0
  25. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  26. package/dist/build-tools/__tests__/index.test.js +141 -0
  27. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  28. package/dist/build-tools/__tests__/utils.test.js +161 -0
  29. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  30. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  31. package/dist/build-tools/error-handler.js +308 -0
  32. package/dist/build-tools/index.d.ts +44 -0
  33. package/dist/build-tools/index.js +88 -0
  34. package/dist/build-tools/package.json +50 -0
  35. package/dist/build-tools/rollup-plugin.js +236 -0
  36. package/dist/build-tools/types.d.ts +163 -0
  37. package/dist/build-tools/utils.js +203 -0
  38. package/dist/build-tools/vite-plugin.js +161 -0
  39. package/dist/build-tools/webpack-loader.js +123 -0
  40. package/dist/charts.d.ts +1 -1
  41. package/dist/charts.js +86 -57
  42. package/dist/charts.js.map +1 -1
  43. package/dist/core.d.ts +1 -1
  44. package/dist/core.js +136 -112
  45. package/dist/core.js.map +1 -1
  46. package/dist/forms.d.ts +2 -5
  47. package/dist/forms.js +140 -128
  48. package/dist/forms.js.map +1 -1
  49. package/dist/heavy.d.ts +1 -1
  50. package/dist/heavy.js +136 -112
  51. package/dist/heavy.js.map +1 -1
  52. package/dist/index.d.ts +152 -78
  53. package/dist/index.esm.js +346 -340
  54. package/dist/index.esm.js.map +1 -1
  55. package/dist/index.js +359 -353
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.min.js +1 -1
  58. package/dist/index.min.js.map +1 -1
  59. package/dist/layout.js.map +1 -1
  60. package/dist/theme.d.ts +9 -9
  61. package/dist/theme.js.map +1 -1
  62. package/package.json +23 -8
  63. package/scripts/atomix-cli.js +170 -73
  64. package/scripts/cli/__tests__/README.md +81 -0
  65. package/scripts/cli/__tests__/basic.test.js +115 -0
  66. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  67. package/scripts/cli/__tests__/integration.test.js +327 -0
  68. package/scripts/cli/__tests__/test-setup.js +133 -0
  69. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  70. package/scripts/cli/__tests__/utils.test.js +161 -0
  71. package/scripts/cli/component-generator.js +253 -299
  72. package/scripts/cli/dependency-checker.js +355 -0
  73. package/scripts/cli/interactive-init.js +46 -5
  74. package/scripts/cli/template-manager.js +0 -2
  75. package/scripts/cli/templates/common-templates.js +636 -0
  76. package/scripts/cli/templates/composable-templates.js +148 -126
  77. package/scripts/cli/templates/index.js +23 -16
  78. package/scripts/cli/templates/project-templates.js +151 -23
  79. package/scripts/cli/templates/react-templates.js +280 -210
  80. package/scripts/cli/templates/scss-templates.js +90 -91
  81. package/scripts/cli/templates/testing-templates.js +206 -27
  82. package/scripts/cli/templates/testing-utils.js +278 -0
  83. package/scripts/cli/templates/types-templates.js +70 -56
  84. package/scripts/cli/theme-bridge.js +8 -2
  85. package/scripts/cli/token-manager.js +318 -206
  86. package/scripts/cli/utils.js +0 -1
  87. package/src/components/Accordion/Accordion.stories.tsx +358 -850
  88. package/src/components/Accordion/Accordion.test.tsx +70 -50
  89. package/src/components/Accordion/Accordion.tsx +99 -94
  90. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  91. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  92. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  93. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  94. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  95. package/src/components/AtomixGlass/glass-utils.ts +6 -5
  96. package/src/components/AtomixGlass/shader-utils.ts +133 -52
  97. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  98. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
  99. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  100. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  101. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  102. package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -36
  103. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +2 -2
  104. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  105. package/src/components/Avatar/Avatar.stories.tsx +195 -0
  106. package/src/components/Avatar/Avatar.tsx +58 -56
  107. package/src/components/Badge/Badge.stories.tsx +122 -352
  108. package/src/components/Badge/Badge.test.tsx +41 -41
  109. package/src/components/Badge/Badge.tsx +64 -62
  110. package/src/components/Block/Block.stories.tsx +30 -11
  111. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +142 -23
  112. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
  113. package/src/components/Button/Button.stories.tsx +454 -1126
  114. package/src/components/Button/Button.test.tsx +123 -0
  115. package/src/components/Button/Button.tsx +88 -60
  116. package/src/components/Button/ButtonGroup.stories.tsx +376 -215
  117. package/src/components/Button/ButtonGroup.tsx +4 -15
  118. package/src/components/Callout/Callout.stories.tsx +316 -568
  119. package/src/components/Card/Card.stories.tsx +292 -81
  120. package/src/components/Card/Card.tsx +30 -14
  121. package/src/components/Chart/AreaChart.tsx +1 -1
  122. package/src/components/Chart/CandlestickChart.tsx +23 -16
  123. package/src/components/Chart/Chart.stories.tsx +153 -16
  124. package/src/components/Chart/Chart.tsx +40 -44
  125. package/src/components/Chart/ChartRenderer.tsx +39 -12
  126. package/src/components/Chart/ChartToolbar.tsx +21 -5
  127. package/src/components/Chart/DonutChart.tsx +1 -1
  128. package/src/components/Chart/FunnelChart.tsx +4 -1
  129. package/src/components/Chart/GaugeChart.tsx +3 -1
  130. package/src/components/Chart/HeatmapChart.tsx +50 -37
  131. package/src/components/Chart/LineChart.tsx +3 -2
  132. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  133. package/src/components/Chart/RadarChart.tsx +19 -17
  134. package/src/components/Chart/ScatterChart.tsx +29 -21
  135. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +152 -66
  136. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  137. package/src/components/Countdown/Countdown.stories.tsx +114 -7
  138. package/src/components/DataTable/DataTable.stories.tsx +349 -144
  139. package/src/components/DataTable/DataTable.test.tsx +26 -148
  140. package/src/components/DataTable/DataTable.tsx +485 -456
  141. package/src/components/DatePicker/DatePicker.stories.tsx +310 -1066
  142. package/src/components/DatePicker/DatePicker.tsx +31 -26
  143. package/src/components/Dropdown/Dropdown.stories.tsx +153 -36
  144. package/src/components/Dropdown/Dropdown.tsx +313 -299
  145. package/src/components/EdgePanel/EdgePanel.stories.tsx +221 -25
  146. package/src/components/EdgePanel/EdgePanel.tsx +1 -3
  147. package/src/components/Footer/Footer.stories.tsx +396 -327
  148. package/src/components/Footer/Footer.tsx +130 -128
  149. package/src/components/Footer/FooterLink.tsx +2 -2
  150. package/src/components/Form/Checkbox.stories.tsx +140 -6
  151. package/src/components/Form/Checkbox.test.tsx +63 -0
  152. package/src/components/Form/Checkbox.tsx +122 -78
  153. package/src/components/Form/Form.stories.tsx +110 -19
  154. package/src/components/Form/FormGroup.stories.tsx +127 -4
  155. package/src/components/Form/Input.stories.tsx +22 -39
  156. package/src/components/Form/Input.test.tsx +38 -44
  157. package/src/components/Form/Radio.stories.tsx +146 -17
  158. package/src/components/Form/Radio.tsx +68 -66
  159. package/src/components/Form/Select.stories.tsx +140 -8
  160. package/src/components/Form/Select.tsx +184 -182
  161. package/src/components/Form/Textarea.stories.tsx +149 -6
  162. package/src/components/Form/Textarea.test.tsx +27 -32
  163. package/src/components/Hero/Hero.stories.tsx +372 -38
  164. package/src/components/Hero/Hero.tsx +201 -55
  165. package/src/components/Icon/index.ts +7 -1
  166. package/src/components/List/List.stories.tsx +141 -3
  167. package/src/components/List/List.tsx +19 -23
  168. package/src/components/Modal/Modal.stories.tsx +183 -43
  169. package/src/components/Modal/Modal.tsx +130 -127
  170. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  171. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  172. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  173. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  174. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  175. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  176. package/src/components/Pagination/Pagination.tsx +199 -202
  177. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  178. package/src/components/Popover/Popover.stories.tsx +354 -97
  179. package/src/components/Popover/Popover.tsx +41 -37
  180. package/src/components/Progress/Progress.stories.tsx +160 -7
  181. package/src/components/River/River.stories.tsx +3 -2
  182. package/src/components/SectionIntro/SectionIntro.stories.tsx +239 -47
  183. package/src/components/Slider/Slider.stories.tsx +12 -4
  184. package/src/components/Spinner/Spinner.stories.tsx +104 -8
  185. package/src/components/Spinner/Spinner.test.tsx +23 -23
  186. package/src/components/Spinner/Spinner.tsx +43 -46
  187. package/src/components/Steps/Steps.stories.tsx +173 -42
  188. package/src/components/Tabs/Tabs.stories.tsx +141 -12
  189. package/src/components/Tabs/Tabs.tsx +74 -72
  190. package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
  191. package/src/components/Todo/Todo.stories.tsx +198 -9
  192. package/src/components/Toggle/Toggle.stories.tsx +137 -36
  193. package/src/components/Toggle/Toggle.test.tsx +65 -70
  194. package/src/components/Toggle/Toggle.tsx +4 -1
  195. package/src/components/Tooltip/Tooltip.stories.tsx +194 -100
  196. package/src/components/Tooltip/Tooltip.tsx +104 -106
  197. package/src/components/Upload/Upload.stories.tsx +241 -150
  198. package/src/components/Upload/Upload.tsx +287 -283
  199. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  200. package/src/components/index.ts +13 -2
  201. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  202. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  203. package/src/lib/README.md +2 -2
  204. package/src/lib/__tests__/theme-tools.test.ts +219 -0
  205. package/src/lib/composables/index.ts +2 -2
  206. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  207. package/src/lib/composables/useAtomixGlass.ts +126 -97
  208. package/src/lib/composables/useChartExport.ts +3 -8
  209. package/src/lib/composables/useDataTable.ts +72 -43
  210. package/src/lib/composables/useHero.ts +58 -14
  211. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  212. package/src/lib/composables/useInput.ts +10 -8
  213. package/src/lib/composables/useSideMenu.ts +6 -5
  214. package/src/lib/composables/useTooltip.ts +1 -2
  215. package/src/lib/composables/useVideoPlayer.ts +44 -35
  216. package/src/lib/config/index.ts +154 -154
  217. package/src/lib/constants/components.ts +9 -32
  218. package/src/lib/constants/cssVariables.ts +29 -29
  219. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  220. package/src/lib/hooks/index.ts +1 -1
  221. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  222. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  223. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  224. package/src/lib/patterns/index.ts +1 -1
  225. package/src/lib/patterns/slots.tsx +8 -13
  226. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  227. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  228. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  229. package/src/lib/storybook/index.ts +1 -1
  230. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  231. package/src/lib/theme/adapters/index.ts +3 -9
  232. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  233. package/src/lib/theme/config/index.ts +1 -1
  234. package/src/lib/theme/config/types.ts +2 -2
  235. package/src/lib/theme/config/validator.ts +10 -5
  236. package/src/lib/theme/constants/constants.ts +2 -2
  237. package/src/lib/theme/constants/index.ts +1 -2
  238. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  239. package/src/lib/theme/core/composeTheme.ts +32 -26
  240. package/src/lib/theme/core/createTheme.ts +1 -1
  241. package/src/lib/theme/core/createThemeObject.ts +308 -301
  242. package/src/lib/theme/core/index.ts +3 -3
  243. package/src/lib/theme/devtools/CLI.ts +107 -105
  244. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  245. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  246. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  247. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  248. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  249. package/src/lib/theme/devtools/Preview.tsx +150 -106
  250. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  251. package/src/lib/theme/devtools/index.ts +3 -9
  252. package/src/lib/theme/devtools/useHistory.ts +23 -21
  253. package/src/lib/theme/errors/errors.ts +12 -11
  254. package/src/lib/theme/errors/index.ts +2 -7
  255. package/src/lib/theme/generators/generateCSS.ts +9 -13
  256. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  257. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  258. package/src/lib/theme/generators/index.ts +1 -4
  259. package/src/lib/theme/i18n/index.ts +1 -1
  260. package/src/lib/theme/i18n/rtl.ts +13 -13
  261. package/src/lib/theme/index.ts +7 -16
  262. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  263. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  264. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  265. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  266. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  267. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  268. package/src/lib/theme/runtime/index.ts +2 -5
  269. package/src/lib/theme/runtime/useTheme.ts +18 -18
  270. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  271. package/src/lib/theme/test/testTheme.ts +15 -16
  272. package/src/lib/theme/tokens/index.ts +2 -7
  273. package/src/lib/theme/tokens/tokens.ts +25 -24
  274. package/src/lib/theme/types.ts +428 -411
  275. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  276. package/src/lib/theme/utils/componentTheming.ts +18 -18
  277. package/src/lib/theme/utils/domUtils.ts +277 -289
  278. package/src/lib/theme/utils/index.ts +1 -2
  279. package/src/lib/theme/utils/injectCSS.ts +10 -14
  280. package/src/lib/theme/utils/naming.ts +20 -16
  281. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  282. package/src/lib/theme/utils/themeUtils.ts +85 -86
  283. package/src/lib/theme/utils/themeValidation.ts +82 -33
  284. package/src/lib/theme-tools.ts +8 -6
  285. package/src/lib/types/components.ts +172 -71
  286. package/src/lib/types/partProps.ts +1 -1
  287. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  288. package/src/lib/utils/componentUtils.ts +8 -12
  289. package/src/lib/utils/csv.ts +19 -0
  290. package/src/lib/utils/dataTableExport.ts +2 -15
  291. package/src/lib/utils/fontPreloader.ts +10 -19
  292. package/src/lib/utils/icons.ts +4 -1
  293. package/src/lib/utils/index.ts +2 -6
  294. package/src/lib/utils/memoryMonitor.ts +10 -8
  295. package/src/lib/utils/themeNaming.ts +2 -2
  296. package/src/styles/01-settings/_index.scss +1 -1
  297. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  298. package/src/styles/01-settings/_settings.colors.scss +11 -11
  299. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  300. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  301. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  302. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  303. package/src/styles/01-settings/_settings.typography.scss +4 -4
  304. package/src/styles/02-tools/_tools.button.scss +51 -21
  305. package/src/styles/02-tools/_tools.utility-api.scss +38 -12
  306. package/src/styles/03-generic/_generic.root.scss +4 -3
  307. package/src/styles/06-components/_components.accordion.scss +56 -14
  308. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  309. package/src/styles/06-components/_components.button.scss +16 -4
  310. package/src/styles/06-components/_components.callout.scss +27 -21
  311. package/src/styles/06-components/_components.card.scss +5 -14
  312. package/src/styles/06-components/_components.chart.scss +22 -19
  313. package/src/styles/06-components/_components.checkbox.scss +25 -17
  314. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  315. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  316. package/src/styles/06-components/_components.footer.scss +1 -1
  317. package/src/styles/06-components/_components.side-menu.scss +5 -5
  318. package/src/styles/06-components/_components.toggle.scss +18 -0
  319. package/src/styles/06-components/_index.scss +1 -1
  320. package/src/styles/06-components/old.chart.styles.scss +0 -2
  321. package/src/styles/99-utilities/_index.scss +2 -0
  322. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  323. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  324. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  325. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  326. package/src/styles/99-utilities/_utilities.scss +2 -0
  327. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  328. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  329. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  330. package/src/styles/99-utilities/_utilities.text.scss +67 -46
  331. package/themes/dark-complementary/README.md +98 -0
  332. package/themes/dark-complementary/index.scss +158 -0
  333. package/themes/default-light/README.md +81 -0
  334. package/themes/default-light/index.scss +154 -0
  335. package/themes/high-contrast/README.md +105 -0
  336. package/themes/high-contrast/index.scss +172 -0
  337. package/themes/test-theme/README.md +38 -0
  338. package/themes/test-theme/index.scss +47 -0
  339. package/scripts/cli/templates-original-backup.js +0 -1655
  340. package/scripts/cli/templates_backup.js +0 -684
  341. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  342. package/src/lib/composables/useButton.ts +0 -93
  343. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -0,0 +1,123 @@
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
+ import { describe, it, expect, vi } from 'vitest';
3
+ import { axe, toHaveNoViolations } from 'jest-axe';
4
+ import { Button } from './Button';
5
+ import React from 'react';
6
+
7
+ expect.extend(toHaveNoViolations);
8
+
9
+ // Mock AtomixGlass component
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
+ ),
16
+ }));
17
+
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
+ });
23
+
24
+ it('renders correctly with children', () => {
25
+ render(<Button>Click Me</Button>);
26
+ expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
27
+ });
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
+ });
35
+
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
+ });
48
+
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
+ });
60
+
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
+ });
70
+
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
+ });
76
+
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');
86
+
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
+ });
95
+
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
+ });
101
+
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
+ });
106
+
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
+ });
117
+
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
+ });
123
+ });
@@ -1,5 +1,4 @@
1
1
  import React, { ElementType, forwardRef, useCallback } from 'react';
2
- import { useButton } from '../../lib/composables/useButton';
3
2
  import { ButtonProps } from '../../lib/types/components';
4
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
5
4
  import { Spinner } from '../Spinner/Spinner';
@@ -60,29 +59,25 @@ export const Button = React.memo(
60
59
  const isDisabled = disabled || loading;
61
60
 
62
61
  // Determine if we should render as a link
62
+ // If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
63
+ // The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
64
+ // This is a safe fallback for disabled links.
63
65
  const shouldRenderAsLink = Boolean(href && !isDisabled);
64
66
 
65
67
  // Resolve icon element - support both icon (ReactNode) and iconName (string)
66
- const iconElement = iconName ? <Icon name={iconName as PhosphorIconsType} size={iconSize} /> : icon;
67
-
68
- const { generateButtonClass, handleClick } = useButton({
69
- variant,
70
- size,
71
- disabled: isDisabled,
72
- rounded,
73
- glass,
74
- loading,
75
- fullWidth,
76
- block,
77
- active,
78
- selected,
79
- });
68
+ const iconElement = iconName ? (
69
+ <Icon name={iconName as PhosphorIconsType} size={iconSize} />
70
+ ) : (
71
+ icon
72
+ );
80
73
 
81
74
  const buttonClass = [
82
75
  BUTTON.BASE_CLASS,
83
76
  ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
84
77
  size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
85
- 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
+ : '',
86
81
  rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
87
82
  isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
88
83
  glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
@@ -144,107 +139,140 @@ export const Button = React.memo(
144
139
  // Determine spinner size based on button size
145
140
  const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
146
141
 
142
+ // Safe Aria Label
143
+ const safeAriaLabel =
144
+ ariaLabel ||
145
+ (iconOnly
146
+ ? typeof label === 'string'
147
+ ? label
148
+ : typeof children === 'string'
149
+ ? children
150
+ : undefined
151
+ : undefined);
152
+
147
153
  // Button content with icon positioning
148
154
  const buttonContent = (
149
155
  <>
150
156
  {loading && (
151
- <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
+ >
152
164
  <Spinner
153
165
  size={spinnerSize}
154
166
  variant={
155
- variant === 'link' || (typeof variant === 'string' && variant.startsWith('outline-'))
167
+ variant === 'link' ||
168
+ (typeof variant === 'string' && variant.startsWith('outline-'))
156
169
  ? 'primary'
157
- : (variant === 'danger' ? 'error' : (variant as any))
170
+ : variant === 'danger'
171
+ ? 'error'
172
+ : (variant as any)
158
173
  }
159
174
  />
160
175
  </span>
161
176
  )}
162
177
  {iconElement && !loading && (
163
- <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
+ >
164
185
  {iconElement}
165
186
  </span>
166
187
  )}
167
188
  {!iconOnly && buttonText && (
168
- <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>
169
197
  )}
170
198
  </>
171
199
  );
172
200
 
173
201
  // Button props
174
202
  const buttonProps = {
175
- ref,
176
203
  className: buttonClass,
177
- type: Component === 'button' && !shouldRenderAsLink ? type : undefined,
178
204
  onClick: handleClickEvent,
179
205
  onMouseEnter: onHover ? handleMouseEnter : undefined,
180
206
  onFocus: onFocus ? handleFocusEvent : undefined,
181
207
  onBlur: onBlur ? handleBlurEvent : undefined,
182
- disabled: isDisabled && Component === 'button' && !shouldRenderAsLink,
183
208
  'aria-disabled': isDisabled,
184
209
  'aria-busy': loading,
185
- 'aria-label': ariaLabel || (iconOnly ? label || children : undefined),
210
+ 'aria-label': safeAriaLabel,
186
211
  'aria-describedby': ariaDescribedBy,
187
212
  'aria-expanded': ariaExpanded,
188
213
  'aria-controls': ariaControls,
189
- tabIndex: tabIndex !== undefined ? tabIndex : (isDisabled ? -1 : 0),
214
+ tabIndex: tabIndex !== undefined ? tabIndex : isDisabled ? -1 : 0,
190
215
  style,
191
216
  ...props,
192
217
  };
193
218
 
194
- // Default glass props
195
- const defaultGlassProps = {
196
- displacementScale: 20,
197
- blurAmount: 0,
198
- saturation: 200,
199
- elasticity: 0,
200
- };
201
- const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
219
+ let content: React.ReactElement;
202
220
 
203
221
  // Render as anchor if href is provided
204
222
  if (shouldRenderAsLink) {
205
- const { ref: _, ...buttonPropsWithoutRef } = buttonProps;
206
- const anchorButtonProps = {
207
- ...buttonPropsWithoutRef,
208
- type: undefined,
209
- disabled: undefined,
210
- };
211
-
212
223
  // Use custom LinkComponent if provided (e.g., Next.js Link)
213
224
  if (LinkComponent) {
214
225
  const LinkComp = LinkComponent as React.ComponentType<any>;
215
226
  const linkProps = {
216
- ...anchorButtonProps,
217
- ref: ref as React.Ref<HTMLAnchorElement>,
227
+ ...buttonProps,
228
+ ref: ref as any, // LinkComponent usually forwards ref to anchor
218
229
  href,
219
230
  target,
220
231
  rel: target === '_blank' ? 'noopener noreferrer' : undefined,
221
232
  };
222
233
 
223
- const linkElement = (
224
- <LinkComp {...linkProps}>
234
+ content = <LinkComp {...linkProps}>{buttonContent}</LinkComp>;
235
+ } else {
236
+ // Fallback to regular anchor tag
237
+ content = (
238
+ <a
239
+ {...buttonProps}
240
+ ref={ref as React.Ref<HTMLAnchorElement>}
241
+ href={href}
242
+ target={target}
243
+ rel={target === '_blank' ? 'noopener noreferrer' : undefined}
244
+ >
225
245
  {buttonContent}
226
- </LinkComp>
246
+ </a>
227
247
  );
228
-
229
- return glass ? <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass> : linkElement;
230
248
  }
231
-
232
- // Fallback to regular anchor tag
233
- const anchorElement = (
234
- <a {...anchorButtonProps} ref={ref as React.Ref<HTMLAnchorElement>} href={href} target={target} rel={target === '_blank' ? 'noopener noreferrer' : undefined}>
249
+ } else {
250
+ // Default button rendering
251
+ content = (
252
+ <Component
253
+ {...buttonProps}
254
+ ref={ref}
255
+ type={Component === 'button' ? type : undefined}
256
+ disabled={isDisabled}
257
+ >
235
258
  {buttonContent}
236
- </a>
259
+ </Component>
237
260
  );
238
-
239
- return glass ? <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass> : anchorElement;
240
261
  }
241
262
 
242
- // Default button rendering
243
- const buttonElement = (
244
- <Component {...buttonProps}>{buttonContent}</Component>
245
- );
263
+ if (glass) {
264
+ // Default glass props
265
+ const defaultGlassProps = {
266
+ displacementScale: 20,
267
+ blurAmount: 0,
268
+ saturation: 200,
269
+ elasticity: 0,
270
+ };
271
+ const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
272
+ return <AtomixGlass {...glassProps}>{content}</AtomixGlass>;
273
+ }
246
274
 
247
- return glass ? <AtomixGlass {...glassProps}>{buttonElement}</AtomixGlass> : buttonElement;
275
+ return content;
248
276
  }
249
277
  )
250
278
  );