@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
@@ -149,143 +149,145 @@ export const Footer = forwardRef<HTMLElement, FooterProps>(
149
149
 
150
150
  const footerContent = (
151
151
  <div className={containerClass}>
152
- {/* Main Footer Content */}
153
- <Grid
154
- className={sectionsClass}
155
- alignItems="start"
156
- justifyContent={layout === 'centered' ? 'center' : undefined}
157
- >
158
- {/* Brand Section */}
159
- {(brand || brandLogo || brandDescription) && (
160
- <GridCol {...(getResponsiveColumnProps('brand') as any)} className={brandClass}>
161
- {brandLogo && (
162
- <div className="c-footer__brand-logo">
163
- {typeof brandLogo === 'string' ? (
164
- <img src={brandLogo} alt={'Brand Logo'} />
165
- ) : (
166
- brandLogo
167
- )}
168
- </div>
169
- )}
170
- {brand && (
171
- <div className="c-footer__brand-name">
172
- {typeof brand === 'string' ? <h3>{brand}</h3> : brand}
173
- </div>
174
- )}
175
- {brandDescription && (
176
- <div className="c-footer__brand-description">{brandDescription}</div>
177
- )}
178
- {socialLinks.length > 0 && (
179
- <div className="c-footer__social" data-testid="footer-social-links">
180
- {socialLinks.map((link, index) => (
181
- <FooterSocialLink
182
- key={`${link.platform}-${index}`}
183
- platform={link.platform}
184
- url={link.url}
185
- icon={link.icon}
186
- label={link.label}
187
- size={size}
188
- />
189
- ))}
190
- </div>
191
- )}
192
- </GridCol>
193
- )}
194
-
195
- {/* Footer Sections */}
196
- {children && (
197
- <GridCol
198
- {...(getResponsiveColumnProps('content') as any)}
199
- className="c-footer__content"
200
- >
201
- <Grid
202
- className="c-footer__sections"
203
- alignItems={layout === 'centered' || layout === 'stacked' ? 'center' : undefined}
204
- >
205
- {React.Children.map(children, child => {
206
- // Check if the child is a valid React element
207
- if (React.isValidElement(child)) {
208
- // Clone the element and pass the showNewsletter prop
209
- return React.cloneElement(child, { showNewsletter } as any);
210
- }
211
- return child;
212
- })}
213
- </Grid>
214
- </GridCol>
215
- )}
152
+ {/* Main Footer Content */}
153
+ <Grid
154
+ className={sectionsClass}
155
+ alignItems="start"
156
+ justifyContent={layout === 'centered' ? 'center' : undefined}
157
+ >
158
+ {/* Brand Section */}
159
+ {(brand || brandLogo || brandDescription) && (
160
+ <GridCol {...(getResponsiveColumnProps('brand') as any)} className={brandClass}>
161
+ {brandLogo && (
162
+ <div className="c-footer__brand-logo">
163
+ {typeof brandLogo === 'string' ? (
164
+ <img src={brandLogo} alt={'Brand Logo'} />
165
+ ) : (
166
+ brandLogo
167
+ )}
168
+ </div>
169
+ )}
170
+ {brand && (
171
+ <div className="c-footer__brand-name">
172
+ {typeof brand === 'string' ? <h3>{brand}</h3> : brand}
173
+ </div>
174
+ )}
175
+ {brandDescription && (
176
+ <div className="c-footer__brand-description">{brandDescription}</div>
177
+ )}
178
+ {socialLinks.length > 0 && (
179
+ <div className="c-footer__social" data-testid="footer-social-links">
180
+ {socialLinks.map((link, index) => (
181
+ <FooterSocialLink
182
+ key={`${link.platform}-${index}`}
183
+ platform={link.platform}
184
+ url={link.url}
185
+ icon={link.icon}
186
+ label={link.label}
187
+ size={size}
188
+ />
189
+ ))}
190
+ </div>
191
+ )}
192
+ </GridCol>
193
+ )}
216
194
 
217
- {/* Newsletter Section */}
218
- {showNewsletter && (
219
- <GridCol
220
- {...(getResponsiveColumnProps('newsletter') as any)}
221
- className="c-footer__newsletter"
195
+ {/* Footer Sections */}
196
+ {children && (
197
+ <GridCol
198
+ {...(getResponsiveColumnProps('content') as any)}
199
+ className="c-footer__content"
200
+ >
201
+ <Grid
202
+ className="c-footer__sections"
203
+ alignItems={layout === 'centered' || layout === 'stacked' ? 'center' : undefined}
222
204
  >
223
- <h4 className="c-footer__newsletter-title">{newsletterTitle}</h4>
224
- {newsletterDescription && (
225
- <p className="c-footer__newsletter-description">{newsletterDescription}</p>
226
- )}
227
- <Form
228
- className="c-footer__newsletter-form"
229
- onSubmit={e => {
230
- e.preventDefault();
231
- const formData = new FormData(e.currentTarget);
232
- const email = formData.get('email') as string;
233
- if (email) handleNewsletterSubmit(email);
234
- }}
235
- >
236
- <div className="c-footer__newsletter-input-group">
237
- <Input
238
- type="email"
239
- name="email"
240
- className="c-footer__newsletter-input"
241
- placeholder={newsletterPlaceholder}
242
- required
243
- />
244
- <Button type="submit" className="c-footer__newsletter-button">
245
- {newsletterButtonText}
246
- </Button>
247
- </div>
248
- </Form>
249
- </GridCol>
250
- )}
251
- </Grid>
205
+ {React.Children.map(children, child => {
206
+ // Check if the child is a valid React element
207
+ if (React.isValidElement(child)) {
208
+ // Clone the element and pass the showNewsletter prop
209
+ return React.cloneElement(child, { showNewsletter } as any);
210
+ }
211
+ return child;
212
+ })}
213
+ </Grid>
214
+ </GridCol>
215
+ )}
252
216
 
253
- {(copyright || showBackToTop) && (
254
- <div className={bottomClass}>
255
- {copyright && <div className="c-footer__copyright">{copyright}</div>}
256
- {showBackToTop && (
257
- <Button
258
- variant="ghost"
259
- className="c-footer__back-to-top"
260
- onClick={handleBackToTop}
261
- disabled={disabled}
262
- aria-label={backToTopText}
263
- >
264
- <span className="c-footer__back-to-top-icon">↑</span>
265
- <span className="c-footer__back-to-top-text">{backToTopText}</span>
266
- </Button>
217
+ {/* Newsletter Section */}
218
+ {showNewsletter && (
219
+ <GridCol
220
+ {...(getResponsiveColumnProps('newsletter') as any)}
221
+ className="c-footer__newsletter"
222
+ >
223
+ <h4 className="c-footer__newsletter-title">{newsletterTitle}</h4>
224
+ {newsletterDescription && (
225
+ <p className="c-footer__newsletter-description">{newsletterDescription}</p>
267
226
  )}
268
- </div>
227
+ <Form
228
+ className="c-footer__newsletter-form"
229
+ onSubmit={e => {
230
+ e.preventDefault();
231
+ const formData = new FormData(e.currentTarget);
232
+ const email = formData.get('email') as string;
233
+ if (email) handleNewsletterSubmit(email);
234
+ }}
235
+ >
236
+ <div className="c-footer__newsletter-input-group">
237
+ <Input
238
+ type="email"
239
+ name="email"
240
+ className="c-footer__newsletter-input"
241
+ placeholder={newsletterPlaceholder}
242
+ required
243
+ />
244
+ <Button type="submit" className="c-footer__newsletter-button">
245
+ {newsletterButtonText}
246
+ </Button>
247
+ </div>
248
+ </Form>
249
+ </GridCol>
269
250
  )}
270
- </div>
251
+ </Grid>
252
+
253
+ {(copyright || showBackToTop) && (
254
+ <div className={bottomClass}>
255
+ {copyright && <div className="c-footer__copyright">{copyright}</div>}
256
+ {showBackToTop && (
257
+ <Button
258
+ variant="ghost"
259
+ className="c-footer__back-to-top"
260
+ onClick={handleBackToTop}
261
+ disabled={disabled}
262
+ aria-label={backToTopText}
263
+ >
264
+ <span className="c-footer__back-to-top-icon">↑</span>
265
+ <span className="c-footer__back-to-top-text">{backToTopText}</span>
266
+ </Button>
267
+ )}
268
+ </div>
269
+ )}
270
+ </div>
271
271
  );
272
272
 
273
273
  return (
274
- <footer ref={ref} className={footerClass + ` c-footer ${glass ? 'c-footer--glass' : ''}`} {...props}>
275
- {glass ? (
276
- <AtomixGlass {...(glass as unknown as AtomixGlassProps)} elasticity={0}>
277
- <div className="c-footer__glass">
278
- {footerContent}
279
- </div>
280
- </AtomixGlass>
281
- ) : (
282
- footerContent
283
- )}
284
- </footer>
285
- );
286
- }
274
+ <footer
275
+ ref={ref}
276
+ className={footerClass + ` c-footer ${glass ? 'c-footer--glass' : ''}`}
277
+ {...props}
278
+ >
279
+ {glass ? (
280
+ <AtomixGlass {...(glass as unknown as AtomixGlassProps)} elasticity={0}>
281
+ <div className="c-footer__glass">{footerContent}</div>
282
+ </AtomixGlass>
283
+ ) : (
284
+ footerContent
285
+ )}
286
+ </footer>
287
+ );
288
+ }
287
289
  );
288
290
 
289
291
  Footer.displayName = 'Footer';
290
292
 
291
- export default Footer;
293
+ export default Footer;
@@ -55,7 +55,7 @@ export const FooterLink = forwardRef<HTMLAnchorElement, FooterLinkProps>(
55
55
  ...(href && !disabled ? { to: href } : {}),
56
56
  ...linkProps,
57
57
  };
58
-
58
+
59
59
  return (
60
60
  <Component {...componentProps}>
61
61
  {icon && <span className="c-footer__link-icon">{icon}</span>}
@@ -77,4 +77,4 @@ export const FooterLink = forwardRef<HTMLAnchorElement, FooterLinkProps>(
77
77
 
78
78
  FooterLink.displayName = 'FooterLink';
79
79
 
80
- export default FooterLink;
80
+ export default FooterLink;
@@ -8,56 +8,56 @@ expect.extend(toHaveNoViolations);
8
8
 
9
9
  // Mock AtomixGlass
10
10
  vi.mock('../AtomixGlass/AtomixGlass', () => ({
11
- AtomixGlass: ({ children }: any) => <div>{children}</div>,
11
+ AtomixGlass: ({ children }: any) => <div>{children}</div>,
12
12
  }));
13
13
 
14
14
  describe('Checkbox Component', () => {
15
- it('renders correctly with label', () => {
16
- render(<Checkbox label="Accept Terms" />);
17
- // In current implementation, if no ID is provided, htmlFor is undefined, so label is not associated.
18
- // screen.getByLabelText might fail or might not find the input.
19
- // Let's see.
20
- expect(screen.getByText('Accept Terms')).toBeInTheDocument();
21
- });
22
-
23
- it('associates label with input when ID is provided', () => {
24
- render(<Checkbox label="Subscribe" id="subscribe-check" />);
25
- expect(screen.getByLabelText('Subscribe')).toBeInTheDocument();
26
- });
27
-
28
- it('associates label with input WITHOUT ID', () => {
29
- // This tests my proposed improvement: wrapping input in label or auto-ID
30
- render(<Checkbox label="No ID Checkbox" />);
31
- // If not associated, this throws
32
- expect(screen.getByLabelText('No ID Checkbox')).toBeInTheDocument();
33
- });
34
-
35
- it('handles checked state', () => {
36
- const handleChange = vi.fn();
37
- render(<Checkbox checked onChange={handleChange} label="Checked" id="checked-id" />);
38
- const input = screen.getByLabelText('Checked');
39
- expect(input).toBeChecked();
40
-
41
- fireEvent.click(input);
42
- expect(handleChange).toHaveBeenCalledTimes(1);
43
- });
44
-
45
- it('forwards ref', () => {
46
- const ref = React.createRef<HTMLInputElement>();
47
- render(<Checkbox ref={ref} label="Ref Checkbox" />);
48
- expect(ref.current).toBeInstanceOf(HTMLInputElement);
49
- });
50
-
51
- it('handles indeterminate state', () => {
52
- // This might need manual DOM check as indeterminate is a property, not attribute
53
- const { getByRole } = render(<Checkbox indeterminate label="Indeterminate" id="indet" />);
54
- const input = getByRole('checkbox') as HTMLInputElement;
55
- expect(input.indeterminate).toBe(true);
56
- });
57
-
58
- it('should have no accessibility violations', async () => {
59
- const { container } = render(<Checkbox label="Accessible Checkbox" id="a11y-check" />);
60
- const results = await axe(container);
61
- expect(results).toHaveNoViolations();
62
- });
15
+ it('renders correctly with label', () => {
16
+ render(<Checkbox label="Accept Terms" />);
17
+ // In current implementation, if no ID is provided, htmlFor is undefined, so label is not associated.
18
+ // screen.getByLabelText might fail or might not find the input.
19
+ // Let's see.
20
+ expect(screen.getByText('Accept Terms')).toBeInTheDocument();
21
+ });
22
+
23
+ it('associates label with input when ID is provided', () => {
24
+ render(<Checkbox label="Subscribe" id="subscribe-check" />);
25
+ expect(screen.getByLabelText('Subscribe')).toBeInTheDocument();
26
+ });
27
+
28
+ it('associates label with input WITHOUT ID', () => {
29
+ // This tests my proposed improvement: wrapping input in label or auto-ID
30
+ render(<Checkbox label="No ID Checkbox" />);
31
+ // If not associated, this throws
32
+ expect(screen.getByLabelText('No ID Checkbox')).toBeInTheDocument();
33
+ });
34
+
35
+ it('handles checked state', () => {
36
+ const handleChange = vi.fn();
37
+ render(<Checkbox checked onChange={handleChange} label="Checked" id="checked-id" />);
38
+ const input = screen.getByLabelText('Checked');
39
+ expect(input).toBeChecked();
40
+
41
+ fireEvent.click(input);
42
+ expect(handleChange).toHaveBeenCalledTimes(1);
43
+ });
44
+
45
+ it('forwards ref', () => {
46
+ const ref = React.createRef<HTMLInputElement>();
47
+ render(<Checkbox ref={ref} label="Ref Checkbox" />);
48
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
49
+ });
50
+
51
+ it('handles indeterminate state', () => {
52
+ // This might need manual DOM check as indeterminate is a property, not attribute
53
+ const { getByRole } = render(<Checkbox indeterminate label="Indeterminate" id="indet" />);
54
+ const input = getByRole('checkbox') as HTMLInputElement;
55
+ expect(input.indeterminate).toBe(true);
56
+ });
57
+
58
+ it('should have no accessibility violations', async () => {
59
+ const { container } = render(<Checkbox label="Accessible Checkbox" id="a11y-check" />);
60
+ const results = await axe(container);
61
+ expect(results).toHaveNoViolations();
62
+ });
63
63
  });
@@ -10,116 +10,124 @@ const CHECKBOX_CLASSES = {
10
10
  MIXED: 'c-checkbox--mixed',
11
11
  };
12
12
 
13
- export const Checkbox = React.memo(forwardRef<HTMLInputElement, CheckboxProps>(({
14
- label,
15
- checked,
16
- onChange,
17
- className = '',
18
- style,
19
- disabled = false,
20
- required = false,
21
- id,
22
- name,
23
- value,
24
- invalid = false,
25
- valid = false,
26
- indeterminate = false,
27
- 'aria-label': ariaLabel,
28
- 'aria-describedby': ariaDescribedBy,
29
- onClick,
30
- glass,
31
- ...props
32
- }, ref) => {
33
- // Local ref to handle indeterminate state
34
- const localRef = useRef<HTMLInputElement>(null);
13
+ export const Checkbox = React.memo(
14
+ forwardRef<HTMLInputElement, CheckboxProps>(
15
+ (
16
+ {
17
+ label,
18
+ checked,
19
+ onChange,
20
+ className = '',
21
+ style,
22
+ disabled = false,
23
+ required = false,
24
+ id,
25
+ name,
26
+ value,
27
+ invalid = false,
28
+ valid = false,
29
+ indeterminate = false,
30
+ 'aria-label': ariaLabel,
31
+ 'aria-describedby': ariaDescribedBy,
32
+ onClick,
33
+ glass,
34
+ ...props
35
+ },
36
+ ref
37
+ ) => {
38
+ // Local ref to handle indeterminate state
39
+ const localRef = useRef<HTMLInputElement>(null);
35
40
 
36
- // Merge refs
37
- useImperativeHandle(ref, () => localRef.current as HTMLInputElement);
41
+ // Merge refs
42
+ useImperativeHandle(ref, () => localRef.current as HTMLInputElement);
38
43
 
39
- // Handle indeterminate
40
- useEffect(() => {
41
- if (localRef.current) {
42
- localRef.current.indeterminate = Boolean(indeterminate);
43
- }
44
- }, [indeterminate]);
44
+ // Handle indeterminate
45
+ useEffect(() => {
46
+ if (localRef.current) {
47
+ localRef.current.indeterminate = Boolean(indeterminate);
48
+ }
49
+ }, [indeterminate]);
45
50
 
46
- // Generate classes
47
- let validationClass = '';
48
- if (invalid) {
49
- validationClass = CHECKBOX_CLASSES.INVALID;
50
- } else if (valid) {
51
- validationClass = CHECKBOX_CLASSES.VALID;
52
- }
51
+ // Generate classes
52
+ let validationClass = '';
53
+ if (invalid) {
54
+ validationClass = CHECKBOX_CLASSES.INVALID;
55
+ } else if (valid) {
56
+ validationClass = CHECKBOX_CLASSES.VALID;
57
+ }
53
58
 
54
- const disabledClass = disabled ? CHECKBOX_CLASSES.DISABLED : '';
55
- const indeterminateClass = indeterminate ? CHECKBOX_CLASSES.MIXED : '';
56
- const glassClass = glass ? 'c-checkbox--glass' : '';
59
+ const disabledClass = disabled ? CHECKBOX_CLASSES.DISABLED : '';
60
+ const indeterminateClass = indeterminate ? CHECKBOX_CLASSES.MIXED : '';
61
+ const glassClass = glass ? 'c-checkbox--glass' : '';
57
62
 
58
- const checkboxClass = `${CHECKBOX_CLASSES.BASE} ${validationClass} ${disabledClass} ${indeterminateClass} ${glassClass} ${className}`.trim();
63
+ const checkboxClass =
64
+ `${CHECKBOX_CLASSES.BASE} ${validationClass} ${disabledClass} ${indeterminateClass} ${glassClass} ${className}`.trim();
59
65
 
60
- const inputElement = (
61
- <input
62
- ref={localRef}
63
- type="checkbox"
64
- className="c-checkbox__input"
65
- checked={checked}
66
- onChange={onChange}
67
- onClick={onClick}
68
- disabled={disabled}
69
- required={required}
70
- id={id}
71
- name={name}
72
- value={value}
73
- aria-label={!label ? ariaLabel : undefined}
74
- aria-describedby={ariaDescribedBy}
75
- aria-invalid={invalid}
76
- {...props}
77
- />
78
- );
66
+ const inputElement = (
67
+ <input
68
+ ref={localRef}
69
+ type="checkbox"
70
+ className="c-checkbox__input"
71
+ checked={checked}
72
+ onChange={onChange}
73
+ onClick={onClick}
74
+ disabled={disabled}
75
+ required={required}
76
+ id={id}
77
+ name={name}
78
+ value={value}
79
+ aria-label={!label ? ariaLabel : undefined}
80
+ aria-describedby={ariaDescribedBy}
81
+ aria-invalid={invalid}
82
+ {...props}
83
+ />
84
+ );
79
85
 
80
- let content: React.ReactNode;
86
+ let content: React.ReactNode;
81
87
 
82
- if (id && label) {
83
- content = (
84
- <div className={checkboxClass} style={style}>
85
- {inputElement}
86
- <label className="c-checkbox__label" htmlFor={id}>
87
- {label}
88
- </label>
89
- </div>
90
- );
91
- } else if (label) {
92
- // Wrap input in label for accessibility when no ID is provided
93
- content = (
94
- <label className={checkboxClass} style={style}>
95
- {inputElement}
96
- <span className="c-checkbox__label">{label}</span>
97
- </label>
98
- );
99
- } else {
100
- // No label
101
- content = (
102
- <div className={checkboxClass} style={style}>
103
- {inputElement}
104
- </div>
105
- );
106
- }
88
+ if (id && label) {
89
+ content = (
90
+ <div className={checkboxClass} style={style}>
91
+ {inputElement}
92
+ <label className="c-checkbox__label" htmlFor={id}>
93
+ {label}
94
+ </label>
95
+ </div>
96
+ );
97
+ } else if (label) {
98
+ // Wrap input in label for accessibility when no ID is provided
99
+ content = (
100
+ <label className={checkboxClass} style={style}>
101
+ {inputElement}
102
+ <span className="c-checkbox__label">{label}</span>
103
+ </label>
104
+ );
105
+ } else {
106
+ // No label
107
+ content = (
108
+ <div className={checkboxClass} style={style}>
109
+ {inputElement}
110
+ </div>
111
+ );
112
+ }
107
113
 
108
- if (glass) {
109
- const defaultGlassProps = {
110
- displacementScale: 40,
111
- blurAmount: 1,
112
- saturation: 160,
113
- aberrationIntensity: 0.3,
114
- cornerRadius: 6,
115
- mode: 'shader' as const,
116
- };
117
- const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
118
- return <AtomixGlass {...glassProps}>{content}</AtomixGlass>;
119
- }
114
+ if (glass) {
115
+ const defaultGlassProps = {
116
+ displacementScale: 40,
117
+ blurAmount: 1,
118
+ saturation: 160,
119
+ aberrationIntensity: 0.3,
120
+ cornerRadius: 6,
121
+ mode: 'shader' as const,
122
+ };
123
+ const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
124
+ return <AtomixGlass {...glassProps}>{content}</AtomixGlass>;
125
+ }
120
126
 
121
- return content;
122
- }));
127
+ return content;
128
+ }
129
+ )
130
+ );
123
131
 
124
132
  Checkbox.displayName = 'Checkbox';
125
133
 
@@ -211,19 +211,11 @@ export const CompleteForm: Story = {
211
211
  </FormGroup>
212
212
 
213
213
  <FormGroup label="Bio" htmlFor="bio">
214
- <Textarea
215
- id="bio"
216
- name="bio"
217
- placeholder="Tell us about yourself"
218
- rows={4}
219
- />
214
+ <Textarea id="bio" name="bio" placeholder="Tell us about yourself" rows={4} />
220
215
  </FormGroup>
221
216
 
222
217
  <FormGroup label="Subscribe to newsletter">
223
- <Checkbox
224
- name="newsletter"
225
- label="Yes, I would like to receive updates"
226
- />
218
+ <Checkbox name="newsletter" label="Yes, I would like to receive updates" />
227
219
  </FormGroup>
228
220
 
229
221
  <div className="u-flex u-gap-3 u-mt-4">