@shohojdhara/atomix 0.3.15 → 0.4.1

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 (276) hide show
  1. package/build-tools/index.d.ts +31 -30
  2. package/build-tools/package.json +4 -21
  3. package/dist/atomix.css +20234 -2027
  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 +4 -5
  10. package/dist/charts.js.map +1 -1
  11. package/dist/core.d.ts +87 -10
  12. package/dist/core.js +673 -480
  13. package/dist/core.js.map +1 -1
  14. package/dist/forms.d.ts +15 -3
  15. package/dist/forms.js +530 -97
  16. package/dist/forms.js.map +1 -1
  17. package/dist/heavy.js +5 -6
  18. package/dist/heavy.js.map +1 -1
  19. package/dist/index.d.ts +644 -277
  20. package/dist/index.esm.js +1948 -1347
  21. package/dist/index.esm.js.map +1 -1
  22. package/dist/index.js +3333 -2728
  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/layout.js.map +1 -1
  27. package/dist/theme.d.ts +9 -9
  28. package/dist/theme.js.map +1 -1
  29. package/package.json +2 -2
  30. package/scripts/atomix-cli.js +10 -1
  31. package/scripts/cli/__tests__/utils.test.js +6 -2
  32. package/scripts/cli/migration-tools.js +2 -2
  33. package/scripts/cli/theme-bridge.js +7 -9
  34. package/scripts/cli/utils.js +2 -1
  35. package/src/components/Accordion/Accordion.stories.tsx +72 -23
  36. package/src/components/Accordion/Accordion.test.tsx +70 -50
  37. package/src/components/Accordion/Accordion.tsx +219 -96
  38. package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
  39. package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
  40. package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
  41. package/src/components/AtomixGlass/glass-utils.ts +4 -3
  42. package/src/components/AtomixGlass/shader-utils.ts +128 -52
  43. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
  44. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
  45. package/src/components/Avatar/Avatar.stories.tsx +45 -62
  46. package/src/components/Avatar/Avatar.tsx +58 -56
  47. package/src/components/Badge/Badge.stories.tsx +20 -9
  48. package/src/components/Badge/Badge.test.tsx +41 -41
  49. package/src/components/Badge/Badge.tsx +64 -62
  50. package/src/components/Block/Block.stories.tsx +14 -4
  51. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
  52. package/src/components/Breadcrumb/Breadcrumb.tsx +173 -65
  53. package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
  54. package/src/components/Button/Button.stories.tsx +13 -22
  55. package/src/components/Button/Button.test.tsx +97 -81
  56. package/src/components/Button/Button.tsx +46 -14
  57. package/src/components/Button/ButtonGroup.stories.tsx +37 -32
  58. package/src/components/Button/ButtonGroup.tsx +4 -15
  59. package/src/components/Callout/Callout.stories.tsx +166 -918
  60. package/src/components/Callout/Callout.tsx +196 -84
  61. package/src/components/Callout/CalloutCompound.test.tsx +72 -0
  62. package/src/components/Card/Card.stories.tsx +67 -36
  63. package/src/components/Card/Card.tsx +30 -14
  64. package/src/components/Chart/AreaChart.tsx +1 -1
  65. package/src/components/Chart/CandlestickChart.tsx +23 -16
  66. package/src/components/Chart/Chart.stories.tsx +4 -9
  67. package/src/components/Chart/Chart.tsx +40 -44
  68. package/src/components/Chart/ChartRenderer.tsx +39 -12
  69. package/src/components/Chart/ChartToolbar.tsx +21 -5
  70. package/src/components/Chart/DonutChart.tsx +1 -1
  71. package/src/components/Chart/FunnelChart.tsx +4 -1
  72. package/src/components/Chart/GaugeChart.tsx +3 -1
  73. package/src/components/Chart/HeatmapChart.tsx +50 -37
  74. package/src/components/Chart/LineChart.tsx +3 -2
  75. package/src/components/Chart/MultiAxisChart.tsx +24 -16
  76. package/src/components/Chart/RadarChart.tsx +19 -17
  77. package/src/components/Chart/ScatterChart.tsx +29 -21
  78. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
  79. package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
  80. package/src/components/Countdown/Countdown.stories.tsx +7 -7
  81. package/src/components/DataTable/DataTable.stories.tsx +43 -38
  82. package/src/components/DataTable/DataTable.test.tsx +26 -148
  83. package/src/components/DataTable/DataTable.tsx +485 -456
  84. package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
  85. package/src/components/DatePicker/DatePicker.tsx +31 -26
  86. package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
  87. package/src/components/Dropdown/Dropdown.tsx +425 -298
  88. package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
  89. package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
  90. package/src/components/EdgePanel/EdgePanel.tsx +163 -113
  91. package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
  92. package/src/components/Footer/Footer.stories.tsx +21 -16
  93. package/src/components/Footer/Footer.tsx +130 -128
  94. package/src/components/Footer/FooterLink.tsx +2 -2
  95. package/src/components/Form/Checkbox.test.tsx +49 -49
  96. package/src/components/Form/Checkbox.tsx +108 -100
  97. package/src/components/Form/Form.stories.tsx +2 -10
  98. package/src/components/Form/Input.stories.tsx +22 -39
  99. package/src/components/Form/Input.test.tsx +38 -44
  100. package/src/components/Form/Radio.stories.tsx +6 -12
  101. package/src/components/Form/Radio.tsx +68 -66
  102. package/src/components/Form/Select.stories.tsx +23 -0
  103. package/src/components/Form/Select.test.tsx +99 -0
  104. package/src/components/Form/Select.tsx +239 -186
  105. package/src/components/Form/SelectOption.tsx +88 -0
  106. package/src/components/Form/Textarea.test.tsx +27 -32
  107. package/src/components/Hero/Hero.stories.tsx +93 -23
  108. package/src/components/Hero/Hero.test.tsx +142 -0
  109. package/src/components/Hero/Hero.tsx +343 -58
  110. package/src/components/Icon/index.ts +7 -1
  111. package/src/components/List/List.test.tsx +62 -0
  112. package/src/components/List/List.tsx +32 -25
  113. package/src/components/List/ListItem.tsx +20 -0
  114. package/src/components/Modal/Modal.stories.tsx +67 -2
  115. package/src/components/Modal/Modal.tsx +208 -125
  116. package/src/components/Modal/ModalCompound.test.tsx +94 -0
  117. package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
  118. package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
  119. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
  120. package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
  121. package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
  122. package/src/components/Pagination/Pagination.stories.tsx +7 -4
  123. package/src/components/Pagination/Pagination.tsx +199 -202
  124. package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
  125. package/src/components/Popover/Popover.stories.tsx +99 -192
  126. package/src/components/Popover/Popover.tsx +41 -37
  127. package/src/components/Progress/Progress.stories.tsx +35 -44
  128. package/src/components/River/River.stories.tsx +2 -1
  129. package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
  130. package/src/components/Slider/Slider.stories.tsx +12 -4
  131. package/src/components/Spinner/Spinner.stories.tsx +3 -1
  132. package/src/components/Spinner/Spinner.test.tsx +23 -23
  133. package/src/components/Spinner/Spinner.tsx +43 -46
  134. package/src/components/Steps/Steps.stories.tsx +8 -6
  135. package/src/components/Steps/Steps.tsx +124 -21
  136. package/src/components/Steps/StepsCompound.test.tsx +81 -0
  137. package/src/components/Tabs/Tabs.stories.tsx +12 -9
  138. package/src/components/Tabs/Tabs.tsx +230 -75
  139. package/src/components/Tabs/TabsCompound.test.tsx +64 -0
  140. package/src/components/Toggle/Toggle.stories.tsx +27 -13
  141. package/src/components/Toggle/Toggle.test.tsx +65 -70
  142. package/src/components/Toggle/Toggle.tsx +4 -1
  143. package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
  144. package/src/components/Tooltip/Tooltip.tsx +104 -106
  145. package/src/components/Upload/Upload.stories.tsx +129 -127
  146. package/src/components/Upload/Upload.tsx +287 -283
  147. package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
  148. package/src/components/index.ts +13 -2
  149. package/src/layouts/Grid/Grid.stories.tsx +9 -3
  150. package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
  151. package/src/lib/__tests__/theme-tools.test.ts +32 -6
  152. package/src/lib/composables/index.ts +0 -4
  153. package/src/lib/composables/shared-mouse-tracker.ts +13 -14
  154. package/src/lib/composables/useAtomixGlass.ts +102 -60
  155. package/src/lib/composables/useChartExport.ts +1 -1
  156. package/src/lib/composables/useDataTable.ts +29 -17
  157. package/src/lib/composables/useHero.ts +58 -14
  158. package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
  159. package/src/lib/composables/useInput.ts +10 -8
  160. package/src/lib/composables/useSideMenu.ts +6 -5
  161. package/src/lib/composables/useTooltip.ts +1 -2
  162. package/src/lib/composables/useVideoPlayer.ts +44 -35
  163. package/src/lib/config/index.ts +154 -154
  164. package/src/lib/constants/cssVariables.ts +29 -29
  165. package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
  166. package/src/lib/hooks/index.ts +1 -1
  167. package/src/lib/hooks/useComponentCustomization.ts +11 -17
  168. package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
  169. package/src/lib/patterns/__tests__/slots.test.ts +1 -1
  170. package/src/lib/patterns/index.ts +1 -1
  171. package/src/lib/patterns/slots.tsx +8 -13
  172. package/src/lib/storybook/InteractiveDemo.tsx +13 -18
  173. package/src/lib/storybook/PreviewContainer.tsx +1 -1
  174. package/src/lib/storybook/VariantsGrid.tsx +3 -7
  175. package/src/lib/storybook/index.ts +1 -1
  176. package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
  177. package/src/lib/theme/adapters/index.ts +3 -9
  178. package/src/lib/theme/adapters/themeAdapter.ts +41 -26
  179. package/src/lib/theme/config/index.ts +1 -1
  180. package/src/lib/theme/config/types.ts +2 -2
  181. package/src/lib/theme/config/validator.ts +10 -5
  182. package/src/lib/theme/constants/constants.ts +2 -2
  183. package/src/lib/theme/constants/index.ts +1 -2
  184. package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
  185. package/src/lib/theme/core/composeTheme.ts +32 -26
  186. package/src/lib/theme/core/createTheme.ts +1 -1
  187. package/src/lib/theme/core/createThemeObject.ts +308 -301
  188. package/src/lib/theme/core/index.ts +3 -3
  189. package/src/lib/theme/devtools/CLI.ts +105 -111
  190. package/src/lib/theme/devtools/Comparator.tsx +50 -32
  191. package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
  192. package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
  193. package/src/lib/theme/devtools/Inspector.tsx +75 -60
  194. package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
  195. package/src/lib/theme/devtools/Preview.tsx +150 -106
  196. package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
  197. package/src/lib/theme/devtools/index.ts +3 -9
  198. package/src/lib/theme/devtools/useHistory.ts +23 -21
  199. package/src/lib/theme/errors/errors.ts +12 -11
  200. package/src/lib/theme/errors/index.ts +2 -7
  201. package/src/lib/theme/generators/generateCSS.ts +9 -13
  202. package/src/lib/theme/generators/generateCSSNested.ts +1 -6
  203. package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
  204. package/src/lib/theme/generators/index.ts +1 -4
  205. package/src/lib/theme/i18n/index.ts +1 -1
  206. package/src/lib/theme/i18n/rtl.ts +13 -13
  207. package/src/lib/theme/index.ts +7 -16
  208. package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
  209. package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
  210. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
  211. package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
  212. package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
  213. package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
  214. package/src/lib/theme/runtime/index.ts +2 -5
  215. package/src/lib/theme/runtime/useTheme.ts +18 -18
  216. package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
  217. package/src/lib/theme/test/testTheme.ts +15 -16
  218. package/src/lib/theme/tokens/index.ts +2 -7
  219. package/src/lib/theme/tokens/tokens.ts +25 -24
  220. package/src/lib/theme/types.ts +428 -411
  221. package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
  222. package/src/lib/theme/utils/componentTheming.ts +18 -18
  223. package/src/lib/theme/utils/domUtils.ts +277 -289
  224. package/src/lib/theme/utils/index.ts +1 -2
  225. package/src/lib/theme/utils/injectCSS.ts +10 -14
  226. package/src/lib/theme/utils/naming.ts +20 -16
  227. package/src/lib/theme/utils/themeHelpers.ts +10 -12
  228. package/src/lib/theme/utils/themeUtils.ts +85 -86
  229. package/src/lib/theme/utils/themeValidation.ts +82 -33
  230. package/src/lib/theme-tools.ts +8 -6
  231. package/src/lib/types/components.ts +180 -73
  232. package/src/lib/types/partProps.ts +1 -1
  233. package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
  234. package/src/lib/utils/__tests__/csv.test.ts +1 -1
  235. package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
  236. package/src/lib/utils/componentUtils.ts +8 -12
  237. package/src/lib/utils/csv.ts +3 -1
  238. package/src/lib/utils/dataTableExport.ts +1 -5
  239. package/src/lib/utils/fontPreloader.ts +10 -19
  240. package/src/lib/utils/icons.ts +4 -1
  241. package/src/lib/utils/index.ts +2 -6
  242. package/src/lib/utils/memoryMonitor.ts +10 -8
  243. package/src/lib/utils/themeNaming.ts +3 -3
  244. package/src/styles/01-settings/_index.scss +0 -1
  245. package/src/styles/01-settings/_settings.colors.scss +8 -8
  246. package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
  247. package/src/styles/01-settings/_settings.navbar.scss +1 -1
  248. package/src/styles/01-settings/_settings.spacing.scss +3 -4
  249. package/src/styles/01-settings/_settings.tooltip.scss +1 -1
  250. package/src/styles/01-settings/_settings.typography.scss +1 -1
  251. package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
  252. package/src/styles/02-tools/_tools.button.scss +51 -21
  253. package/src/styles/02-tools/_tools.utility-api.scss +36 -24
  254. package/src/styles/03-generic/_generic.root.scss +4 -3
  255. package/src/styles/06-components/_components.atomix-glass.scss +13 -9
  256. package/src/styles/06-components/_components.button.scss +16 -4
  257. package/src/styles/06-components/_components.callout.scss +27 -21
  258. package/src/styles/06-components/_components.card.scss +5 -14
  259. package/src/styles/06-components/_components.chart.scss +22 -19
  260. package/src/styles/06-components/_components.checkbox.scss +3 -1
  261. package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
  262. package/src/styles/06-components/_components.edge-panel.scss +9 -2
  263. package/src/styles/06-components/_components.footer.scss +1 -1
  264. package/src/styles/06-components/_components.side-menu.scss +5 -5
  265. package/src/styles/06-components/_components.toggle.scss +18 -0
  266. package/src/styles/06-components/_index.scss +1 -1
  267. package/src/styles/06-components/old.chart.styles.scss +0 -2
  268. package/src/styles/99-utilities/_utilities.border.scss +69 -27
  269. package/src/styles/99-utilities/_utilities.display.scss +1 -1
  270. package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
  271. package/src/styles/99-utilities/_utilities.position.scss +16 -9
  272. package/src/styles/99-utilities/_utilities.scss +1 -1
  273. package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
  274. package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
  275. package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
  276. package/src/styles/99-utilities/_utilities.text.scss +67 -47
@@ -254,7 +254,8 @@ export const UsageExamples: Story = {
254
254
  parameters: {
255
255
  docs: {
256
256
  description: {
257
- story: 'Practical examples demonstrating how badges can be used in real-world scenarios such as navigation menus, product cards, and task lists.',
257
+ story:
258
+ 'Practical examples demonstrating how badges can be used in real-world scenarios such as navigation menus, product cards, and task lists.',
258
259
  },
259
260
  },
260
261
  },
@@ -325,7 +326,8 @@ export const AccessibilityFeatures: Story = {
325
326
  parameters: {
326
327
  docs: {
327
328
  description: {
328
- story: 'Examples of accessible badge implementations with proper ARIA labels and keyboard navigation support.',
329
+ story:
330
+ 'Examples of accessible badge implementations with proper ARIA labels and keyboard navigation support.',
329
331
  },
330
332
  },
331
333
  },
@@ -338,14 +340,11 @@ export const AccessibilityFeatures: Story = {
338
340
  <Badge label="Alert" variant="error" aria-label="Critical notification" />
339
341
  </div>
340
342
  </div>
341
-
343
+
342
344
  <div>
343
345
  <h3 className="u-mt-0 u-mb-2">Interactive Badges</h3>
344
346
  <div className="u-flex u-gap-2">
345
- <Badge
346
- label="Closable Tag"
347
- variant="info"
348
- />
347
+ <Badge label="Closable Tag" variant="info" />
349
348
  </div>
350
349
  </div>
351
350
  </div>
@@ -361,7 +360,13 @@ export const WithGlassEffect: Story = {
361
360
  variant: 'primary',
362
361
  },
363
362
  render: () => (
364
- <div className="u-bg-cover u-bg-center u-rounded-xl u-p-24" style={{backgroundImage: 'url(https://cdn.pixabay.com/photo/2023/07/07/20/42/grasshopper-8113345_1280.jpg)'}}>
363
+ <div
364
+ className="u-bg-cover u-bg-center u-rounded-xl u-p-24"
365
+ style={{
366
+ backgroundImage:
367
+ 'url(https://cdn.pixabay.com/photo/2023/07/07/20/42/grasshopper-8113345_1280.jpg)',
368
+ }}
369
+ >
365
370
  <div className="u-flex u-flex-wrap u-gap-2">
366
371
  {THEME_COLORS.map(color => (
367
372
  <Badge key={color} label={color} variant={color} glass={true} />
@@ -390,7 +395,13 @@ export const WithCustomGlassSettings: Story = {
390
395
  },
391
396
  decorators: [
392
397
  Story => (
393
- <div className="u-bg-cover u-bg-center u-rounded-xl u-p-24" style={{backgroundImage: 'url(https://cdn.pixabay.com/photo/2021/06/14/22/46/milky-way-6337038_1280.jpg)'}}>
398
+ <div
399
+ className="u-bg-cover u-bg-center u-rounded-xl u-p-24"
400
+ style={{
401
+ backgroundImage:
402
+ 'url(https://cdn.pixabay.com/photo/2021/06/14/22/46/milky-way-6337038_1280.jpg)',
403
+ }}
404
+ >
394
405
  <Story />
395
406
  </div>
396
407
  ),
@@ -4,48 +4,48 @@ import { Badge } from './Badge';
4
4
 
5
5
  // Mock AtomixGlass component
6
6
  vi.mock('../AtomixGlass/AtomixGlass', () => ({
7
- AtomixGlass: ({ children, ...props }: any) => (
8
- <div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
9
- {children}
10
- </div>
11
- ),
7
+ AtomixGlass: ({ children, ...props }: any) => (
8
+ <div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
9
+ {children}
10
+ </div>
11
+ ),
12
12
  }));
13
13
 
14
14
  describe('Badge Component', () => {
15
- it('renders correctly', () => {
16
- render(<Badge label="Test Badge" />);
17
- expect(screen.getByText('Test Badge')).toBeInTheDocument();
18
- });
19
-
20
- it('renders with aria-label', () => {
21
- render(<Badge label="Badge" aria-label="Accessible Badge" />);
22
- expect(screen.getByLabelText('Accessible Badge')).toBeInTheDocument();
23
- });
24
-
25
- it('renders close button when onRemove is provided', () => {
26
- const handleRemove = vi.fn();
27
- render(<Badge label="Removable" onRemove={handleRemove} />);
28
-
29
- const closeButton = screen.getByRole('button', { name: 'Remove badge' });
30
- expect(closeButton).toBeInTheDocument();
31
-
32
- fireEvent.click(closeButton);
33
- expect(handleRemove).toHaveBeenCalledTimes(1);
34
- });
35
-
36
- it('does not render close button when onRemove is not provided', () => {
37
- render(<Badge label="Static" />);
38
- expect(screen.queryByRole('button')).not.toBeInTheDocument();
39
- });
40
-
41
- it('disables close button when badge is disabled', () => {
42
- const handleRemove = vi.fn();
43
- render(<Badge label="Disabled" onRemove={handleRemove} disabled />);
44
-
45
- const closeButton = screen.getByRole('button', { name: 'Remove badge' });
46
- expect(closeButton).toBeDisabled();
47
-
48
- fireEvent.click(closeButton);
49
- expect(handleRemove).not.toHaveBeenCalled();
50
- });
15
+ it('renders correctly', () => {
16
+ render(<Badge label="Test Badge" />);
17
+ expect(screen.getByText('Test Badge')).toBeInTheDocument();
18
+ });
19
+
20
+ it('renders with aria-label', () => {
21
+ render(<Badge label="Badge" aria-label="Accessible Badge" />);
22
+ expect(screen.getByLabelText('Accessible Badge')).toBeInTheDocument();
23
+ });
24
+
25
+ it('renders close button when onRemove is provided', () => {
26
+ const handleRemove = vi.fn();
27
+ render(<Badge label="Removable" onRemove={handleRemove} />);
28
+
29
+ const closeButton = screen.getByRole('button', { name: 'Remove badge' });
30
+ expect(closeButton).toBeInTheDocument();
31
+
32
+ fireEvent.click(closeButton);
33
+ expect(handleRemove).toHaveBeenCalledTimes(1);
34
+ });
35
+
36
+ it('does not render close button when onRemove is not provided', () => {
37
+ render(<Badge label="Static" />);
38
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
39
+ });
40
+
41
+ it('disables close button when badge is disabled', () => {
42
+ const handleRemove = vi.fn();
43
+ render(<Badge label="Disabled" onRemove={handleRemove} disabled />);
44
+
45
+ const closeButton = screen.getByRole('button', { name: 'Remove badge' });
46
+ expect(closeButton).toBeDisabled();
47
+
48
+ fireEvent.click(closeButton);
49
+ expect(handleRemove).not.toHaveBeenCalled();
50
+ });
51
51
  });
@@ -4,75 +4,77 @@ import { BADGE } from '../../lib/constants/components';
4
4
  import { BadgeProps } from '../../lib/types/components';
5
5
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
6
6
 
7
- export const Badge: React.FC<BadgeProps> = memo(({
8
- label,
9
- variant = 'primary',
10
- size = 'md',
11
- disabled = false,
12
- icon,
13
- onRemove,
14
- 'aria-label': ariaLabel,
15
- className = '',
16
- glass,
17
- style,
18
- }) => {
19
- const { generateBadgeClass } = useBadge({
20
- variant,
21
- size,
22
- disabled,
23
- });
7
+ export const Badge: React.FC<BadgeProps> = memo(
8
+ ({
9
+ label,
10
+ variant = 'primary',
11
+ size = 'md',
12
+ disabled = false,
13
+ icon,
14
+ onRemove,
15
+ 'aria-label': ariaLabel,
16
+ className = '',
17
+ glass,
18
+ style,
19
+ }) => {
20
+ const { generateBadgeClass } = useBadge({
21
+ variant,
22
+ size,
23
+ disabled,
24
+ });
24
25
 
25
- const ref = useRef<HTMLSpanElement>(null);
26
+ const ref = useRef<HTMLSpanElement>(null);
26
27
 
27
- const badgeClass = generateBadgeClass({
28
- variant,
29
- size,
30
- disabled,
31
- className: `${className} ${glass ? 'c-badge--glass' : ''}`.trim(),
32
- });
28
+ const badgeClass = generateBadgeClass({
29
+ variant,
30
+ size,
31
+ disabled,
32
+ className: `${className} ${glass ? 'c-badge--glass' : ''}`.trim(),
33
+ });
33
34
 
34
- const badgeElement = (
35
- <span
36
- className={badgeClass}
37
- aria-disabled={disabled}
38
- aria-label={ariaLabel}
39
- ref={ref}
40
- style={style}
41
- >
42
- {icon && <span className={BADGE.ICON_CLASS}>{icon}</span>}
43
- <span>{label}</span>
44
- {onRemove && (
45
- <button
46
- type="button"
47
- className="c-badge__close"
48
- onClick={onRemove}
49
- aria-label="Remove badge"
50
- disabled={disabled}
51
- >
52
- ×
53
- </button>
54
- )}
55
- </span>
56
- );
35
+ const badgeElement = (
36
+ <span
37
+ className={badgeClass}
38
+ aria-disabled={disabled}
39
+ aria-label={ariaLabel}
40
+ ref={ref}
41
+ style={style}
42
+ >
43
+ {icon && <span className={BADGE.ICON_CLASS}>{icon}</span>}
44
+ <span>{label}</span>
45
+ {onRemove && (
46
+ <button
47
+ type="button"
48
+ className="c-badge__close"
49
+ onClick={onRemove}
50
+ aria-label="Remove badge"
51
+ disabled={disabled}
52
+ >
53
+ ×
54
+ </button>
55
+ )}
56
+ </span>
57
+ );
57
58
 
58
- if (glass) {
59
- // Default glass settings for badges
60
- const defaultGlassProps = {
61
- displacementScale: 20,
62
- cornerRadius: ref.current?.getBoundingClientRect().width
63
- ? ref.current?.getBoundingClientRect().width / 2
64
- : 16,
65
- className: 'c-badge--glass',
66
- elasticity: 0,
67
- };
59
+ if (glass) {
60
+ // Default glass settings for badges
61
+ const defaultGlassProps = {
62
+ displacementScale: 20,
63
+ cornerRadius: ref.current?.getBoundingClientRect().width
64
+ ? ref.current?.getBoundingClientRect().width / 2
65
+ : 16,
66
+ className: 'c-badge--glass',
67
+ elasticity: 0,
68
+ };
68
69
 
69
- const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
70
+ const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
70
71
 
71
- return <AtomixGlass {...glassProps}>{badgeElement}</AtomixGlass>;
72
- }
72
+ return <AtomixGlass {...glassProps}>{badgeElement}</AtomixGlass>;
73
+ }
73
74
 
74
- return badgeElement;
75
- });
75
+ return badgeElement;
76
+ }
77
+ );
76
78
 
77
79
  Badge.displayName = 'Badge';
78
80
 
@@ -178,7 +178,9 @@ export const ArticleLayout: Story = {
178
178
  <Block spacing="md" container={{ type: 'sm' }} background="secondary">
179
179
  <h3>Ready to get started?</h3>
180
180
  <p>Join thousands of developers building with our design system.</p>
181
- <Button variant="primary" onClick={fn()}>Start Building</Button>
181
+ <Button variant="primary" onClick={fn()}>
182
+ Start Building
183
+ </Button>
182
184
  </Block>
183
185
  </div>
184
186
  ),
@@ -334,14 +336,22 @@ export const ContentPreview: Story = {
334
336
  <Card
335
337
  title="Cards"
336
338
  text="Flexible content containers with multiple options"
337
- actions={<Button variant="primary" onClick={fn()}>View Details</Button>}
339
+ actions={
340
+ <Button variant="primary" onClick={fn()}>
341
+ View Details
342
+ </Button>
343
+ }
338
344
  />
339
345
  </GridCol>
340
346
  <GridCol xs={12} md={6} lg={4}>
341
347
  <Card
342
348
  title="Blocks"
343
349
  text="Layout containers with consistent spacing and backgrounds"
344
- actions={<Button variant="primary" onClick={fn()}>Learn More</Button>}
350
+ actions={
351
+ <Button variant="primary" onClick={fn()}>
352
+ Learn More
353
+ </Button>
354
+ }
345
355
  />
346
356
  </GridCol>
347
357
  </Grid>
@@ -398,4 +408,4 @@ export const ContentPreview: Story = {
398
408
  </Block>
399
409
  </div>
400
410
  ),
401
- };
411
+ };
@@ -181,18 +181,18 @@ export const WithMixedInteractions: Story = {
181
181
  args: {
182
182
  items: [
183
183
  { label: 'Home', href: '/' },
184
- {
185
- label: 'Products',
184
+ {
185
+ label: 'Products',
186
186
  onClick: fn(),
187
187
  // Simulating client-side navigation
188
188
  },
189
- {
190
- label: 'Category',
189
+ {
190
+ label: 'Category',
191
191
  href: '/products/category',
192
192
  icon: <Icon name="Folder" size="sm" />,
193
193
  },
194
- {
195
- label: 'Product Name',
194
+ {
195
+ label: 'Product Name',
196
196
  active: true,
197
197
  icon: <Icon name="Tag" size="sm" />,
198
198
  },
@@ -202,7 +202,8 @@ export const WithMixedInteractions: Story = {
202
202
  parameters: {
203
203
  docs: {
204
204
  description: {
205
- story: 'Breadcrumb combining both traditional link navigation and client-side interactions.',
205
+ story:
206
+ 'Breadcrumb combining both traditional link navigation and client-side interactions.',
206
207
  },
207
208
  },
208
209
  },
@@ -228,4 +229,4 @@ export const LongBreadcrumbPath: Story = {
228
229
  },
229
230
  },
230
231
  },
231
- };
232
+ };
@@ -1,7 +1,8 @@
1
- import React, { ReactNode, memo } from 'react';
1
+ import React, { ReactNode, memo, forwardRef, Children, cloneElement, isValidElement } from 'react';
2
2
  import { BREADCRUMB } from '../../lib/constants/components';
3
3
 
4
- export interface BreadcrumbItem {
4
+ // Legacy Item Interface
5
+ export interface BreadcrumbItemData {
5
6
  /**
6
7
  * Text to display
7
8
  */
@@ -38,11 +39,108 @@ export interface BreadcrumbItem {
38
39
  className?: string;
39
40
  }
40
41
 
42
+ // Export legacy interface as type alias to preserve backward compatibility for type imports
43
+ export type { BreadcrumbItemData as BreadcrumbItem };
44
+
45
+ // Compound Component Props
46
+ export interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement> {
47
+ /**
48
+ * URL for the breadcrumb item
49
+ */
50
+ href?: string;
51
+
52
+ /**
53
+ * Whether this item is active (current page)
54
+ */
55
+ active?: boolean;
56
+
57
+ /**
58
+ * Optional icon to display before the label
59
+ */
60
+ icon?: ReactNode;
61
+
62
+ /**
63
+ * Optional click handler for the link
64
+ */
65
+ onClick?: (event: React.MouseEvent<HTMLAnchorElement | HTMLSpanElement>) => void;
66
+
67
+ /**
68
+ * Optional custom link component
69
+ */
70
+ linkAs?: React.ElementType;
71
+
72
+ /**
73
+ * Link props to pass to the underlying anchor or LinkComponent
74
+ */
75
+ linkProps?: Record<string, any>;
76
+ }
77
+
78
+ export const BreadcrumbItem = forwardRef<HTMLLIElement, BreadcrumbItemProps>(
79
+ (
80
+ {
81
+ children,
82
+ href,
83
+ active,
84
+ icon,
85
+ onClick,
86
+ className = '',
87
+ style,
88
+ linkAs: LinkComponent,
89
+ linkProps = {},
90
+ ...props
91
+ },
92
+ ref
93
+ ) => {
94
+ const itemClasses = [BREADCRUMB.CLASSES.ITEM, active ? BREADCRUMB.CLASSES.ACTIVE : '', className]
95
+ .filter(Boolean)
96
+ .join(' ');
97
+
98
+ const linkContent = (
99
+ <>
100
+ {icon && <span className="c-breadcrumb__icon">{icon}</span>}
101
+ {children}
102
+ </>
103
+ );
104
+
105
+ const commonLinkProps = {
106
+ className: BREADCRUMB.CLASSES.LINK,
107
+ onClick: onClick as any,
108
+ style, // Apply style to the link as per legacy behavior
109
+ ...linkProps,
110
+ };
111
+
112
+ return (
113
+ <li ref={ref} className={itemClasses} style={style} {...props}>
114
+ {href && !active ? (
115
+ LinkComponent ? (
116
+ (() => {
117
+ const Component = LinkComponent;
118
+ return (
119
+ <Component href={href} {...commonLinkProps}>
120
+ {linkContent}
121
+ </Component>
122
+ );
123
+ })()
124
+ ) : (
125
+ <a href={href} {...(commonLinkProps as React.HTMLAttributes<HTMLAnchorElement>)}>
126
+ {linkContent}
127
+ </a>
128
+ )
129
+ ) : (
130
+ <span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
131
+ )}
132
+ </li>
133
+ );
134
+ }
135
+ );
136
+
137
+ BreadcrumbItem.displayName = 'BreadcrumbItem';
138
+
41
139
  export interface BreadcrumbProps {
42
140
  /**
43
- * Array of breadcrumb items
141
+ * Array of breadcrumb items (Legacy)
44
142
  */
45
- items: BreadcrumbItem[];
143
+ items?: BreadcrumbItemData[];
46
144
 
47
145
  /**
48
146
  * Custom divider character or element
@@ -68,69 +166,79 @@ export interface BreadcrumbProps {
68
166
  * Custom style for the breadcrumb
69
167
  */
70
168
  style?: React.CSSProperties;
169
+
170
+ /**
171
+ * Children (Compound)
172
+ */
173
+ children?: ReactNode;
71
174
  }
72
- export const Breadcrumb: React.FC<BreadcrumbProps> = memo(({
73
- items,
74
- divider,
75
- className = '',
76
- 'aria-label': ariaLabel = 'Breadcrumb',
77
- LinkComponent,
78
- style,
79
- }) => {
80
- const breadcrumbClasses = [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ');
81
-
82
- return (
83
- <nav aria-label={ariaLabel} style={style}>
84
- <ol className={breadcrumbClasses}>
85
- {items.map((item, index) => {
86
- const isLast = index === items.length - 1;
87
- const itemClasses = [
88
- BREADCRUMB.CLASSES.ITEM,
89
- item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : '',
90
- ]
91
- .filter(Boolean)
92
- .join(' ');
93
-
94
- const linkContent = (
95
- <>
96
- {item.icon && <span className="c-breadcrumb__icon">{item.icon}</span>}
97
- {item.label}
98
- </>
99
- );
100
-
101
- const linkProps = {
102
- href: item.href,
103
- className: BREADCRUMB.CLASSES.LINK,
104
- onClick: item.onClick,
105
- style: item.style,
106
- };
107
-
108
- return (
109
- <li key={index} className={itemClasses} style={item.style}>
110
- {item.href && !item.active ? (
111
- LinkComponent ? (
112
- (() => {
113
- const Component = LinkComponent as React.ComponentType<any>;
114
- return (
115
- <Component {...(linkProps as React.ComponentProps<React.ElementType>)}>
116
- {linkContent}
117
- </Component>
118
- );
119
- })()
120
- ) : (
121
- <a {...(linkProps as React.ComponentProps<'a'>)}>{linkContent}</a>
122
- )
123
- ) : (
124
- <span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
125
- )}
126
- </li>
127
- );
128
- })}
129
- </ol>
130
- </nav>
131
- );
132
- });
175
+
176
+ type BreadcrumbComponent = React.FC<BreadcrumbProps> & {
177
+ Item: typeof BreadcrumbItem;
178
+ };
179
+
180
+ export const Breadcrumb: BreadcrumbComponent = memo(
181
+ ({
182
+ items,
183
+ divider,
184
+ className = '',
185
+ 'aria-label': ariaLabel = 'Breadcrumb',
186
+ LinkComponent,
187
+ style,
188
+ children,
189
+ }) => {
190
+ const breadcrumbClasses = [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ');
191
+
192
+ let content: ReactNode;
193
+
194
+ if (items && items.length > 0) {
195
+ // Legacy rendering
196
+ content = items.map((item, index) => {
197
+ const isLast = index === items.length - 1;
198
+
199
+ return (
200
+ <BreadcrumbItem
201
+ key={index}
202
+ href={item.href}
203
+ active={item.active || isLast}
204
+ icon={item.icon}
205
+ onClick={item.onClick}
206
+ className={item.className}
207
+ style={item.style}
208
+ linkAs={LinkComponent}
209
+ >
210
+ {item.label}
211
+ </BreadcrumbItem>
212
+ );
213
+ });
214
+ } else {
215
+ // Compound rendering
216
+ const childrenCount = Children.count(children);
217
+ content = Children.map(children, (child, index) => {
218
+ if (isValidElement(child)) {
219
+ const isLast = index === childrenCount - 1;
220
+ const childProps = child.props as any;
221
+
222
+ return cloneElement(child, {
223
+ active: childProps.active ?? (isLast ? true : undefined),
224
+ linkAs: childProps.linkAs ?? LinkComponent,
225
+ } as any);
226
+ }
227
+ return child;
228
+ });
229
+ }
230
+
231
+ return (
232
+ <nav aria-label={ariaLabel} style={style}>
233
+ <ol className={breadcrumbClasses}>
234
+ {content}
235
+ </ol>
236
+ </nav>
237
+ );
238
+ }
239
+ ) as unknown as BreadcrumbComponent;
133
240
 
134
241
  Breadcrumb.displayName = 'Breadcrumb';
242
+ Breadcrumb.Item = BreadcrumbItem;
135
243
 
136
244
  export default Breadcrumb;