@boostdev/design-system-components 2.8.0 → 2.11.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 (534) hide show
  1. package/AGENTS.md +54 -46
  2. package/README.md +34 -20
  3. package/dist/client.cjs +1317 -779
  4. package/dist/client.css +1029 -812
  5. package/dist/client.d.cts +88 -26
  6. package/dist/client.d.ts +88 -26
  7. package/dist/client.js +1278 -732
  8. package/dist/index.cjs +1317 -779
  9. package/dist/index.css +1029 -812
  10. package/dist/index.d.cts +88 -26
  11. package/dist/index.d.ts +88 -26
  12. package/dist/index.js +1278 -732
  13. package/dist/web-components/chunk-2OLVQUBU.js +193 -0
  14. package/dist/web-components/{chunk-OF6FFGOT.js → chunk-3AY2YKCM.js} +77 -21
  15. package/dist/web-components/chunk-3WAV6MDN.js +212 -0
  16. package/dist/web-components/{chunk-UUKSDCYS.js → chunk-47AT5KZD.js} +87 -15
  17. package/dist/web-components/chunk-4A2YYTYN.js +93 -0
  18. package/dist/web-components/{chunk-Q4T5BJT6.js → chunk-62NK3ZSC.js} +18 -3
  19. package/dist/web-components/chunk-66M6ETWD.js +77 -0
  20. package/dist/web-components/{chunk-NCPQ7AF3.js → chunk-6G22I2CK.js} +18 -3
  21. package/dist/web-components/{chunk-QDST57JC.js → chunk-7ZOJ7N4E.js} +14 -4
  22. package/dist/web-components/{chunk-QLT6MTZF.js → chunk-AIBOHNDU.js} +10 -0
  23. package/dist/web-components/{chunk-X3NQMBAQ.js → chunk-AP4RDUL2.js} +14 -3
  24. package/dist/web-components/{chunk-QWPACK5L.js → chunk-BPYKZVDK.js} +11 -1
  25. package/dist/web-components/{chunk-YLVTEY23.js → chunk-BTCMJQKB.js} +53 -21
  26. package/dist/web-components/chunk-CUKQ2L5B.js +245 -0
  27. package/dist/web-components/chunk-DIZDCL7Z.js +14 -0
  28. package/dist/web-components/{chunk-PC3IOQAH.js → chunk-DV5T43ZC.js} +27 -7
  29. package/dist/web-components/{chunk-2QA62WCQ.js → chunk-EF7IVYTY.js} +10 -0
  30. package/dist/web-components/{chunk-L3XHOMN6.js → chunk-F3ZDNHZZ.js} +26 -1
  31. package/dist/web-components/chunk-FEDABOR5.js +248 -0
  32. package/dist/web-components/{chunk-6I2DBFQ7.js → chunk-HGFCJXOQ.js} +36 -5
  33. package/dist/web-components/{chunk-SOQQ7RVJ.js → chunk-IPGHUCJT.js} +23 -8
  34. package/dist/web-components/chunk-ITD2MV5K.js +392 -0
  35. package/dist/web-components/{chunk-ARMKC4BQ.js → chunk-JH6LQKLK.js} +77 -5
  36. package/dist/web-components/chunk-JMCXAAFK.js +120 -0
  37. package/dist/web-components/{chunk-KUI55GFB.js → chunk-JUPCZVVW.js} +50 -18
  38. package/dist/web-components/chunk-JV5QT2OU.js +146 -0
  39. package/dist/web-components/{chunk-3GFWLSLS.js → chunk-KA3VPKMY.js} +29 -6
  40. package/dist/web-components/{chunk-CR5PGUHV.js → chunk-KJGRFDFI.js} +26 -11
  41. package/dist/web-components/{chunk-PY62FTRW.js → chunk-KZPRFY5P.js} +68 -33
  42. package/dist/web-components/{chunk-X46KL5BK.js → chunk-LDIPCTRN.js} +18 -1
  43. package/dist/web-components/chunk-M3ECULRH.js +174 -0
  44. package/dist/web-components/chunk-NBN2ENWJ.js +90 -0
  45. package/dist/web-components/chunk-NIN3JO6Q.js +189 -0
  46. package/dist/web-components/{chunk-2FGATTGT.js → chunk-NTQ4TCUT.js} +76 -55
  47. package/dist/web-components/chunk-OY74D4X6.js +302 -0
  48. package/dist/web-components/{chunk-3S42DZ7W.js → chunk-P24JYC5V.js} +7 -0
  49. package/dist/web-components/{chunk-BKIHQYJR.js → chunk-PXPPKCLT.js} +17 -2
  50. package/dist/web-components/{chunk-TQ7IQ4GZ.js → chunk-QEF2IRID.js} +71 -29
  51. package/dist/web-components/chunk-QZAIRJ75.js +111 -0
  52. package/dist/web-components/{chunk-6I3CFC26.js → chunk-RSZ3BWBK.js} +20 -5
  53. package/dist/web-components/chunk-RVF7W653.js +101 -0
  54. package/dist/web-components/chunk-SEO2F5KJ.js +234 -0
  55. package/dist/web-components/{chunk-VMMVWLXL.js → chunk-SFZ6Q4BP.js} +10 -0
  56. package/dist/web-components/{chunk-LY62SAVN.js → chunk-SYNLFKD3.js} +12 -2
  57. package/dist/web-components/chunk-TMWXRHGF.js +192 -0
  58. package/dist/web-components/{chunk-65JXQOBA.js → chunk-U3YZE6R6.js} +51 -7
  59. package/dist/web-components/chunk-UO7ROSHD.js +424 -0
  60. package/dist/web-components/chunk-VANAKB6D.js +152 -0
  61. package/dist/web-components/{chunk-PXZWYO6Z.js → chunk-VED2DFJ6.js} +108 -29
  62. package/dist/web-components/chunk-W6H5ID2U.js +161 -0
  63. package/dist/web-components/{chunk-FSZRP43R.js → chunk-WFVKU4GX.js} +21 -10
  64. package/dist/web-components/{chunk-OQTRTMAB.js → chunk-WIBAN7KP.js} +10 -0
  65. package/dist/web-components/chunk-XI7KRAH7.js +54 -0
  66. package/dist/web-components/{chunk-ORC56MDY.js → chunk-XIZWAWMJ.js} +45 -6
  67. package/dist/web-components/{chunk-FXURM6D5.js → chunk-YFAXGRPR.js} +10 -0
  68. package/dist/web-components/globals.js +1 -1
  69. package/dist/web-components/index.d.ts +77 -707
  70. package/dist/web-components/index.js +339 -2066
  71. package/dist/web-components/interaction/bds-accordion-item.js +1 -1
  72. package/dist/web-components/interaction/bds-accordion.js +1 -1
  73. package/dist/web-components/interaction/bds-button.d.ts +10 -2
  74. package/dist/web-components/interaction/bds-button.js +1 -1
  75. package/dist/web-components/interaction/bds-collapsible.d.ts +7 -2
  76. package/dist/web-components/interaction/bds-collapsible.js +1 -1
  77. package/dist/web-components/interaction/bds-dialog.d.ts +11 -3
  78. package/dist/web-components/interaction/bds-dialog.js +1 -1
  79. package/dist/web-components/interaction/bds-drawer.d.ts +7 -1
  80. package/dist/web-components/interaction/bds-drawer.js +1 -1
  81. package/dist/web-components/interaction/bds-dropdown-menu-item.d.ts +51 -0
  82. package/dist/web-components/interaction/bds-dropdown-menu-item.js +7 -0
  83. package/dist/web-components/interaction/bds-dropdown-menu.d.ts +70 -0
  84. package/dist/web-components/interaction/bds-dropdown-menu.js +7 -0
  85. package/dist/web-components/interaction/bds-popover.d.ts +14 -3
  86. package/dist/web-components/interaction/bds-popover.js +1 -1
  87. package/dist/web-components/interaction/bds-skip-link.js +1 -1
  88. package/dist/web-components/interaction/bds-tab-panel.js +1 -1
  89. package/dist/web-components/interaction/bds-tab.js +1 -1
  90. package/dist/web-components/interaction/bds-tabs.d.ts +1 -0
  91. package/dist/web-components/interaction/bds-tabs.js +1 -1
  92. package/dist/web-components/interaction/bds-toast-provider.js +1 -1
  93. package/dist/web-components/interaction/bds-tooltip.d.ts +19 -3
  94. package/dist/web-components/interaction/bds-tooltip.js +1 -1
  95. package/dist/web-components/interaction/form/bds-checkbox-group.d.ts +83 -0
  96. package/dist/web-components/interaction/form/bds-checkbox-group.js +7 -0
  97. package/dist/web-components/interaction/form/bds-checkbox.d.ts +6 -0
  98. package/dist/web-components/interaction/form/bds-checkbox.js +1 -1
  99. package/dist/web-components/interaction/form/bds-combobox.d.ts +18 -0
  100. package/dist/web-components/interaction/form/bds-combobox.js +1 -1
  101. package/dist/web-components/interaction/form/bds-field-group.d.ts +89 -0
  102. package/dist/web-components/interaction/form/bds-field-group.js +7 -0
  103. package/dist/web-components/interaction/form/bds-file-input.d.ts +1 -1
  104. package/dist/web-components/interaction/form/bds-file-input.js +1 -1
  105. package/dist/web-components/interaction/form/bds-form-input.d.ts +92 -0
  106. package/dist/web-components/interaction/form/bds-form-input.js +7 -0
  107. package/dist/web-components/interaction/form/bds-number-input.d.ts +18 -1
  108. package/dist/web-components/interaction/form/bds-number-input.js +1 -1
  109. package/dist/web-components/interaction/form/bds-radio-group.d.ts +81 -0
  110. package/dist/web-components/interaction/form/bds-radio-group.js +7 -0
  111. package/dist/web-components/interaction/form/bds-radio.d.ts +34 -6
  112. package/dist/web-components/interaction/form/bds-radio.js +1 -1
  113. package/dist/web-components/interaction/form/bds-segmented-control.d.ts +4 -4
  114. package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
  115. package/dist/web-components/interaction/form/bds-select.d.ts +22 -1
  116. package/dist/web-components/interaction/form/bds-select.js +1 -1
  117. package/dist/web-components/interaction/form/bds-slider.d.ts +42 -10
  118. package/dist/web-components/interaction/form/bds-slider.js +1 -1
  119. package/dist/web-components/interaction/form/bds-switch.d.ts +20 -0
  120. package/dist/web-components/interaction/form/bds-switch.js +1 -1
  121. package/dist/web-components/interaction/form/bds-textarea.d.ts +23 -3
  122. package/dist/web-components/interaction/form/bds-textarea.js +1 -1
  123. package/dist/web-components/layout/bds-grid-item.d.ts +63 -0
  124. package/dist/web-components/layout/bds-grid-item.js +7 -0
  125. package/dist/web-components/layout/bds-grid.d.ts +108 -0
  126. package/dist/web-components/layout/bds-grid.js +7 -0
  127. package/dist/web-components/ui/bds-alert.d.ts +16 -2
  128. package/dist/web-components/ui/bds-alert.js +1 -1
  129. package/dist/web-components/ui/bds-avatar.d.ts +2 -1
  130. package/dist/web-components/ui/bds-avatar.js +1 -1
  131. package/dist/web-components/ui/bds-badge.js +1 -1
  132. package/dist/web-components/ui/bds-breadcrumb.d.ts +3 -1
  133. package/dist/web-components/ui/bds-breadcrumb.js +1 -1
  134. package/dist/web-components/ui/bds-button-container.d.ts +52 -0
  135. package/dist/web-components/ui/bds-button-container.js +7 -0
  136. package/dist/web-components/ui/bds-button-group.d.ts +21 -0
  137. package/dist/web-components/ui/bds-button-group.js +8 -0
  138. package/dist/web-components/ui/bds-calendar.d.ts +121 -0
  139. package/dist/web-components/ui/bds-calendar.js +7 -0
  140. package/dist/web-components/ui/bds-card.d.ts +12 -3
  141. package/dist/web-components/ui/bds-card.js +1 -1
  142. package/dist/web-components/ui/bds-carousel.d.ts +98 -0
  143. package/dist/web-components/ui/bds-carousel.js +7 -0
  144. package/dist/web-components/ui/bds-description-list.js +1 -1
  145. package/dist/web-components/ui/bds-icon-wrapper.d.ts +0 -9
  146. package/dist/web-components/ui/bds-icon-wrapper.js +1 -1
  147. package/dist/web-components/ui/bds-link.js +1 -1
  148. package/dist/web-components/ui/bds-loading.js +1 -1
  149. package/dist/web-components/ui/bds-notification-banner.js +1 -1
  150. package/dist/web-components/ui/bds-pagination.d.ts +47 -0
  151. package/dist/web-components/ui/bds-pagination.js +7 -0
  152. package/dist/web-components/ui/bds-progress-circle.d.ts +3 -1
  153. package/dist/web-components/ui/bds-progress-circle.js +1 -1
  154. package/dist/web-components/ui/bds-progress.d.ts +3 -1
  155. package/dist/web-components/ui/bds-progress.js +1 -1
  156. package/dist/web-components/ui/bds-rating.js +1 -1
  157. package/dist/web-components/ui/bds-section-header.d.ts +18 -4
  158. package/dist/web-components/ui/bds-section-header.js +1 -1
  159. package/dist/web-components/ui/bds-separator.js +1 -1
  160. package/dist/web-components/ui/bds-skeleton.js +1 -1
  161. package/dist/web-components/ui/bds-table.d.ts +52 -0
  162. package/dist/web-components/ui/bds-table.js +7 -0
  163. package/dist/web-components/ui/bds-typography.d.ts +1 -0
  164. package/dist/web-components/ui/bds-typography.js +1 -1
  165. package/package.json +60 -3
  166. package/src/components/interaction/Button/Button.module.css +23 -14
  167. package/src/components/interaction/Button/Button.spec.tsx +20 -0
  168. package/src/components/interaction/Button/Button.tsx +2 -2
  169. package/src/components/interaction/Command/Command.mdx +5 -2
  170. package/src/components/interaction/Command/Command.module.css +23 -7
  171. package/src/components/interaction/Command/Command.spec.tsx +35 -0
  172. package/src/components/interaction/Command/Command.tsx +15 -4
  173. package/src/components/interaction/Command/index.ts +1 -1
  174. package/src/components/interaction/Dialog/Dialog.mdx +4 -4
  175. package/src/components/interaction/Dialog/Dialog.module.css +20 -0
  176. package/src/components/interaction/Dialog/Dialog.spec.tsx +20 -0
  177. package/src/components/interaction/Dialog/Dialog.tsx +8 -3
  178. package/src/components/interaction/Dialog/index.ts +1 -0
  179. package/src/components/interaction/Drawer/Drawer.mdx +5 -2
  180. package/src/components/interaction/Drawer/Drawer.module.css +34 -3
  181. package/src/components/interaction/Drawer/Drawer.spec.tsx +67 -1
  182. package/src/components/interaction/Drawer/Drawer.tsx +30 -12
  183. package/src/components/interaction/Drawer/index.ts +1 -0
  184. package/src/components/interaction/DropdownMenu/DropdownMenu.mdx +2 -0
  185. package/src/components/interaction/DropdownMenu/DropdownMenu.spec.tsx +34 -0
  186. package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +15 -5
  187. package/src/components/interaction/DropdownMenu/index.ts +1 -0
  188. package/src/components/interaction/Popover/Popover.mdx +6 -2
  189. package/src/components/interaction/Popover/Popover.module.css +26 -18
  190. package/src/components/interaction/Popover/Popover.spec.tsx +38 -0
  191. package/src/components/interaction/Popover/Popover.tsx +19 -5
  192. package/src/components/interaction/Popover/index.ts +1 -0
  193. package/src/components/interaction/Rating/Rating.mdx +1 -1
  194. package/src/components/interaction/Rating/Rating.module.css +2 -2
  195. package/src/components/interaction/Rating/Rating.tsx +1 -1
  196. package/src/components/interaction/Rating/index.ts +1 -0
  197. package/src/components/interaction/Toast/Toast.mdx +3 -0
  198. package/src/components/interaction/Toast/Toast.module.css +35 -5
  199. package/src/components/interaction/Toast/Toast.spec.tsx +69 -1
  200. package/src/components/interaction/Toast/Toast.tsx +15 -9
  201. package/src/components/interaction/form/Checkbox/Checkbox.mdx +3 -0
  202. package/src/components/interaction/form/Checkbox/Checkbox.module.css +13 -6
  203. package/src/components/interaction/form/Checkbox/Checkbox.spec.tsx +6 -0
  204. package/src/components/interaction/form/Checkbox/Checkbox.tsx +1 -1
  205. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.mdx +16 -1
  206. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.module.css +24 -8
  207. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.spec.tsx +32 -2
  208. package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +14 -2
  209. package/src/components/interaction/form/Combobox/Combobox.mdx +11 -4
  210. package/src/components/interaction/form/Combobox/Combobox.module.css +6 -6
  211. package/src/components/interaction/form/Combobox/Combobox.spec.tsx +47 -0
  212. package/src/components/interaction/form/Combobox/Combobox.tsx +10 -5
  213. package/src/components/interaction/form/FieldGroup/FieldGroup.mdx +9 -10
  214. package/src/components/interaction/form/FieldGroup/FieldGroup.module.css +57 -23
  215. package/src/components/interaction/form/FieldGroup/FieldGroup.spec.tsx +129 -7
  216. package/src/components/interaction/form/FieldGroup/FieldGroup.tsx +72 -12
  217. package/src/components/interaction/form/FileInput/FileInput.mdx +9 -1
  218. package/src/components/interaction/form/FileInput/FileInput.module.css +8 -8
  219. package/src/components/interaction/form/FileInput/FileInput.spec.tsx +89 -1
  220. package/src/components/interaction/form/FileInput/FileInput.tsx +6 -2
  221. package/src/components/interaction/form/FormInput/FormInput.mdx +9 -5
  222. package/src/components/interaction/form/FormInput/FormInput.module.css +17 -8
  223. package/src/components/interaction/form/FormInput/FormInput.spec.tsx +20 -0
  224. package/src/components/interaction/form/FormInput/FormInput.tsx +7 -2
  225. package/src/components/interaction/form/NumberInput/NumberInput.mdx +5 -1
  226. package/src/components/interaction/form/NumberInput/NumberInput.module.css +6 -6
  227. package/src/components/interaction/form/NumberInput/NumberInput.spec.tsx +51 -0
  228. package/src/components/interaction/form/NumberInput/NumberInput.tsx +36 -8
  229. package/src/components/interaction/form/Radio/Radio.mdx +8 -1
  230. package/src/components/interaction/form/Radio/Radio.module.css +20 -7
  231. package/src/components/interaction/form/Radio/Radio.spec.tsx +15 -0
  232. package/src/components/interaction/form/Radio/Radio.tsx +10 -4
  233. package/src/components/interaction/form/RadioGroup/RadioGroup.mdx +16 -1
  234. package/src/components/interaction/form/RadioGroup/RadioGroup.module.css +24 -8
  235. package/src/components/interaction/form/RadioGroup/RadioGroup.spec.tsx +32 -2
  236. package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +14 -2
  237. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +9 -3
  238. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +25 -12
  239. package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +86 -5
  240. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +44 -4
  241. package/src/components/interaction/form/Select/Select.mdx +10 -4
  242. package/src/components/interaction/form/Select/Select.module.css +4 -4
  243. package/src/components/interaction/form/Select/Select.spec.tsx +34 -0
  244. package/src/components/interaction/form/Select/Select.tsx +21 -9
  245. package/src/components/interaction/form/Slider/Slider.mdx +4 -1
  246. package/src/components/interaction/form/Slider/Slider.module.css +32 -8
  247. package/src/components/interaction/form/Slider/Slider.spec.tsx +29 -0
  248. package/src/components/interaction/form/Slider/Slider.tsx +9 -7
  249. package/src/components/interaction/form/Switch/Switch.mdx +2 -2
  250. package/src/components/interaction/form/Switch/Switch.module.css +12 -13
  251. package/src/components/interaction/form/Switch/Switch.spec.tsx +20 -0
  252. package/src/components/interaction/form/Switch/Switch.tsx +2 -1
  253. package/src/components/interaction/form/Textarea/Textarea.mdx +5 -0
  254. package/src/components/interaction/form/Textarea/Textarea.module.css +5 -5
  255. package/src/components/interaction/form/Textarea/Textarea.spec.tsx +13 -0
  256. package/src/components/interaction/form/Textarea/Textarea.tsx +3 -1
  257. package/src/components/interaction/form/atoms/Message.module.css +1 -1
  258. package/src/components/interaction/form/atoms/Message.spec.tsx +29 -0
  259. package/src/components/interaction/form/atoms/Message.tsx +1 -1
  260. package/src/components/layout/ButtonGroup/ButtonGroup.mdx +11 -0
  261. package/src/components/layout/ButtonGroup/ButtonGroup.module.css +1 -5
  262. package/src/components/layout/ButtonGroup/ButtonGroup.stories.tsx +14 -0
  263. package/src/components/layout/Card/Card.mdx +6 -1
  264. package/src/components/layout/Card/Card.module.css +26 -5
  265. package/src/components/layout/Card/Card.spec.tsx +7 -0
  266. package/src/components/layout/Card/Card.tsx +4 -3
  267. package/src/components/layout/Grid/Grid.spec.tsx +44 -0
  268. package/src/components/layout/Grid/Grid.tsx +5 -5
  269. package/src/components/layout/Grid/GridItem.tsx +6 -6
  270. package/src/components/layout/Grid/masonry.ts +31 -5
  271. package/src/components/layout/Grid/useIsomorphicLayoutEffect.ts +11 -0
  272. package/src/components/layout/IconWrapper/IconWrapper.mdx +8 -2
  273. package/src/components/layout/IconWrapper/IconWrapper.module.css +9 -6
  274. package/src/components/layout/SectionHeader/SectionHeader.mdx +4 -1
  275. package/src/components/layout/SectionHeader/SectionHeader.module.css +10 -9
  276. package/src/components/ui/Accordion/Accordion.mdx +4 -1
  277. package/src/components/ui/Accordion/Accordion.module.css +2 -1
  278. package/src/components/ui/Alert/Alert.mdx +21 -4
  279. package/src/components/ui/Alert/Alert.module.css +9 -4
  280. package/src/components/ui/Alert/Alert.spec.tsx +20 -0
  281. package/src/components/ui/Avatar/Avatar.mdx +8 -3
  282. package/src/components/ui/Avatar/Avatar.module.css +13 -8
  283. package/src/components/ui/Badge/Badge.mdx +3 -1
  284. package/src/components/ui/Breadcrumb/Breadcrumb.mdx +3 -0
  285. package/src/components/ui/Breadcrumb/Breadcrumb.module.css +4 -0
  286. package/src/components/ui/Breadcrumb/Breadcrumb.spec.tsx +12 -0
  287. package/src/components/ui/Breadcrumb/Breadcrumb.tsx +11 -3
  288. package/src/components/ui/Calendar/Calendar.mdx +6 -0
  289. package/src/components/ui/Calendar/Calendar.module.css +68 -19
  290. package/src/components/ui/Calendar/Calendar.spec.tsx +139 -24
  291. package/src/components/ui/Calendar/Calendar.stories.tsx +14 -0
  292. package/src/components/ui/Calendar/Calendar.tsx +143 -154
  293. package/src/components/ui/Calendar/CalendarRange.mdx +39 -0
  294. package/src/components/ui/Calendar/CalendarRange.spec.tsx +119 -0
  295. package/src/components/ui/Calendar/CalendarRange.stories.tsx +51 -0
  296. package/src/components/ui/Calendar/CalendarRange.tsx +213 -0
  297. package/src/components/ui/Calendar/MonthGrid.tsx +141 -0
  298. package/src/components/ui/Calendar/calendar-core.spec.ts +176 -0
  299. package/src/components/ui/Calendar/calendar-core.ts +164 -0
  300. package/src/components/ui/Calendar/index.ts +1 -0
  301. package/src/components/ui/Carousel/Carousel.mdx +8 -3
  302. package/src/components/ui/Carousel/Carousel.module.css +7 -5
  303. package/src/components/ui/Carousel/Carousel.spec.tsx +67 -0
  304. package/src/components/ui/Carousel/Carousel.tsx +19 -7
  305. package/src/components/ui/Collapsible/Collapsible.mdx +17 -6
  306. package/src/components/ui/Collapsible/Collapsible.module.css +7 -2
  307. package/src/components/ui/Collapsible/Collapsible.spec.tsx +40 -0
  308. package/src/components/ui/Collapsible/Collapsible.tsx +19 -2
  309. package/src/components/ui/DescriptionList/DescriptionList.mdx +2 -0
  310. package/src/components/ui/Link/Link.mdx +4 -2
  311. package/src/components/ui/Loading/Loading.mdx +6 -3
  312. package/src/components/ui/Loading/Loading.module.css +7 -7
  313. package/src/components/ui/Loading/Loading.spec.tsx +12 -0
  314. package/src/components/ui/Loading/Loading.tsx +8 -3
  315. package/src/components/ui/NotificationBanner/NotificationBanner.mdx +4 -0
  316. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +5 -0
  317. package/src/components/ui/NotificationBanner/NotificationBanner.spec.tsx +20 -0
  318. package/src/components/ui/Pagination/Pagination.mdx +29 -7
  319. package/src/components/ui/Pagination/Pagination.module.css +15 -14
  320. package/src/components/ui/Progress/Progress.mdx +6 -2
  321. package/src/components/ui/Progress/Progress.module.css +1 -1
  322. package/src/components/ui/Progress/Progress.spec.tsx +17 -0
  323. package/src/components/ui/Progress/Progress.tsx +2 -2
  324. package/src/components/ui/ProgressCircle/ProgressCircle.mdx +8 -4
  325. package/src/components/ui/ProgressCircle/ProgressCircle.module.css +2 -2
  326. package/src/components/ui/ProgressCircle/ProgressCircle.spec.tsx +11 -1
  327. package/src/components/ui/ProgressCircle/ProgressCircle.tsx +1 -1
  328. package/src/components/ui/Separator/Separator.mdx +7 -0
  329. package/src/components/ui/Separator/Separator.module.css +1 -0
  330. package/src/components/ui/Skeleton/Skeleton.mdx +17 -0
  331. package/src/components/ui/Skeleton/Skeleton.module.css +12 -5
  332. package/src/components/ui/SkipLink/SkipLink.mdx +19 -0
  333. package/src/components/ui/SkipLink/SkipLink.module.css +8 -7
  334. package/src/components/ui/SkipLink/SkipLink.spec.tsx +28 -0
  335. package/src/components/ui/Table/Table.mdx +14 -0
  336. package/src/components/ui/Table/Table.module.css +13 -8
  337. package/src/components/ui/Table/Table.spec.tsx +56 -0
  338. package/src/components/ui/Table/Table.tsx +44 -6
  339. package/src/components/ui/Tabs/Tabs.mdx +16 -0
  340. package/src/components/ui/Tabs/Tabs.module.css +3 -3
  341. package/src/components/ui/Tabs/Tabs.spec.tsx +20 -0
  342. package/src/components/ui/Tabs/Tabs.tsx +5 -1
  343. package/src/components/ui/Tooltip/Tooltip.mdx +9 -0
  344. package/src/components/ui/Tooltip/Tooltip.module.css +32 -12
  345. package/src/components/ui/Tooltip/Tooltip.spec.tsx +56 -0
  346. package/src/components/ui/Tooltip/Tooltip.tsx +26 -7
  347. package/src/components/ui/Typography/Typography.mdx +7 -0
  348. package/src/components/ui/Typography/Typography.module.css +5 -5
  349. package/src/index.ts +7 -3
  350. package/src/stories/DesignSystem/Borders.mdx +14 -9
  351. package/src/stories/DesignSystem/Colors.mdx +43 -41
  352. package/src/stories/DesignSystem/Overview.mdx +4 -3
  353. package/src/stories/Introduction.mdx +5 -5
  354. package/src/web-components/globals.ts +7 -0
  355. package/src/web-components/index.ts +2 -0
  356. package/src/web-components/interaction/BdsAccordion.mdx +10 -1
  357. package/src/web-components/interaction/BdsButton.mdx +15 -4
  358. package/src/web-components/interaction/BdsButton.stories.tsx +1 -1
  359. package/src/web-components/interaction/BdsCollapsible.mdx +3 -3
  360. package/src/web-components/interaction/BdsDialog.mdx +12 -7
  361. package/src/web-components/interaction/BdsDialog.stories.tsx +2 -2
  362. package/src/web-components/interaction/BdsDrawer.mdx +9 -6
  363. package/src/web-components/interaction/BdsDropdownMenu.mdx +20 -1
  364. package/src/web-components/interaction/BdsPopover.mdx +7 -3
  365. package/src/web-components/interaction/BdsSkipLink.mdx +14 -1
  366. package/src/web-components/interaction/BdsTabs.mdx +18 -1
  367. package/src/web-components/interaction/BdsToastProvider.mdx +21 -3
  368. package/src/web-components/interaction/BdsTooltip.mdx +4 -4
  369. package/src/web-components/interaction/bds-accordion-item.ts +10 -0
  370. package/src/web-components/interaction/bds-accordion.spec.ts +41 -0
  371. package/src/web-components/interaction/bds-accordion.ts +28 -2
  372. package/src/web-components/interaction/bds-button.spec.ts +81 -0
  373. package/src/web-components/interaction/bds-button.ts +55 -8
  374. package/src/web-components/interaction/bds-collapsible.spec.ts +93 -2
  375. package/src/web-components/interaction/bds-collapsible.ts +49 -7
  376. package/src/web-components/interaction/bds-dialog.spec.ts +49 -0
  377. package/src/web-components/interaction/bds-dialog.ts +68 -11
  378. package/src/web-components/interaction/bds-drawer.spec.ts +93 -4
  379. package/src/web-components/interaction/bds-drawer.ts +54 -21
  380. package/src/web-components/interaction/bds-dropdown-menu-item.ts +12 -1
  381. package/src/web-components/interaction/bds-dropdown-menu.spec.ts +63 -0
  382. package/src/web-components/interaction/bds-dropdown-menu.ts +45 -7
  383. package/src/web-components/interaction/bds-popover.spec.ts +80 -6
  384. package/src/web-components/interaction/bds-popover.ts +83 -32
  385. package/src/web-components/interaction/bds-skip-link.spec.ts +18 -0
  386. package/src/web-components/interaction/bds-skip-link.ts +29 -8
  387. package/src/web-components/interaction/bds-tab-panel.ts +10 -0
  388. package/src/web-components/interaction/bds-tab.spec.ts +25 -0
  389. package/src/web-components/interaction/bds-tab.ts +20 -5
  390. package/src/web-components/interaction/bds-tabs.spec.ts +92 -5
  391. package/src/web-components/interaction/bds-tabs.ts +42 -1
  392. package/src/web-components/interaction/bds-toast-provider.spec.ts +43 -0
  393. package/src/web-components/interaction/bds-toast-provider.ts +50 -21
  394. package/src/web-components/interaction/bds-tooltip.spec.ts +71 -0
  395. package/src/web-components/interaction/bds-tooltip.ts +100 -5
  396. package/src/web-components/interaction/form/BdsCheckbox.mdx +20 -1
  397. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +8 -0
  398. package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +4 -3
  399. package/src/web-components/interaction/form/BdsCombobox.mdx +39 -2
  400. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +31 -0
  401. package/src/web-components/interaction/form/BdsFieldGroup.mdx +8 -3
  402. package/src/web-components/interaction/form/BdsFieldGroup.stories.tsx +1 -1
  403. package/src/web-components/interaction/form/BdsFileInput.mdx +26 -5
  404. package/src/web-components/interaction/form/BdsFormInput.mdx +16 -1
  405. package/src/web-components/interaction/form/BdsNumberInput.mdx +34 -2
  406. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +27 -3
  407. package/src/web-components/interaction/form/BdsRadio.mdx +39 -12
  408. package/src/web-components/interaction/form/BdsRadio.stories.tsx +26 -0
  409. package/src/web-components/interaction/form/BdsRadioGroup.mdx +9 -4
  410. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +37 -11
  411. package/src/web-components/interaction/form/BdsSelect.mdx +22 -4
  412. package/src/web-components/interaction/form/BdsSelect.stories.tsx +33 -0
  413. package/src/web-components/interaction/form/BdsSlider.mdx +36 -19
  414. package/src/web-components/interaction/form/BdsSlider.stories.tsx +24 -0
  415. package/src/web-components/interaction/form/BdsSwitch.mdx +38 -11
  416. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +16 -0
  417. package/src/web-components/interaction/form/BdsTextarea.mdx +42 -16
  418. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +21 -0
  419. package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +99 -0
  420. package/src/web-components/interaction/form/bds-checkbox-group.ts +95 -9
  421. package/src/web-components/interaction/form/bds-checkbox.spec.ts +55 -0
  422. package/src/web-components/interaction/form/bds-checkbox.ts +30 -5
  423. package/src/web-components/interaction/form/bds-combobox.spec.ts +79 -0
  424. package/src/web-components/interaction/form/bds-combobox.ts +101 -16
  425. package/src/web-components/interaction/form/bds-field-group.spec.ts +158 -0
  426. package/src/web-components/interaction/form/bds-field-group.ts +167 -23
  427. package/src/web-components/interaction/form/bds-file-input.spec.ts +73 -5
  428. package/src/web-components/interaction/form/bds-file-input.ts +71 -34
  429. package/src/web-components/interaction/form/bds-form-input.spec.ts +22 -0
  430. package/src/web-components/interaction/form/bds-form-input.ts +27 -16
  431. package/src/web-components/interaction/form/bds-number-input.spec.ts +49 -0
  432. package/src/web-components/interaction/form/bds-number-input.ts +118 -29
  433. package/src/web-components/interaction/form/bds-radio-group.spec.ts +57 -0
  434. package/src/web-components/interaction/form/bds-radio-group.ts +60 -12
  435. package/src/web-components/interaction/form/bds-radio.spec.ts +123 -0
  436. package/src/web-components/interaction/form/bds-radio.ts +135 -22
  437. package/src/web-components/interaction/form/bds-segmented-control.spec.ts +55 -0
  438. package/src/web-components/interaction/form/bds-segmented-control.ts +81 -59
  439. package/src/web-components/interaction/form/bds-select.spec.ts +57 -0
  440. package/src/web-components/interaction/form/bds-select.ts +95 -7
  441. package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
  442. package/src/web-components/interaction/form/bds-slider.ts +154 -26
  443. package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
  444. package/src/web-components/interaction/form/bds-switch.ts +87 -20
  445. package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
  446. package/src/web-components/interaction/form/bds-textarea.ts +108 -7
  447. package/src/web-components/layout/BdsGrid.mdx +6 -2
  448. package/src/web-components/layout/BdsGrid.stories.tsx +4 -0
  449. package/src/web-components/layout/bds-grid-item.spec.ts +21 -0
  450. package/src/web-components/layout/bds-grid-item.ts +17 -3
  451. package/src/web-components/layout/bds-grid.spec.ts +98 -0
  452. package/src/web-components/layout/bds-grid.ts +139 -19
  453. package/src/web-components/ui/BdsAlert.mdx +6 -5
  454. package/src/web-components/ui/BdsAlert.stories.tsx +6 -6
  455. package/src/web-components/ui/BdsAvatar.mdx +4 -2
  456. package/src/web-components/ui/BdsBreadcrumb.mdx +3 -2
  457. package/src/web-components/ui/BdsButtonContainer.mdx +64 -0
  458. package/src/web-components/ui/BdsButtonContainer.stories.tsx +84 -0
  459. package/src/web-components/ui/BdsCalendar.mdx +29 -4
  460. package/src/web-components/ui/BdsCalendar.stories.tsx +16 -18
  461. package/src/web-components/ui/BdsCalendarRange.mdx +71 -0
  462. package/src/web-components/ui/BdsCalendarRange.stories.tsx +31 -0
  463. package/src/web-components/ui/BdsCard.mdx +7 -3
  464. package/src/web-components/ui/BdsCard.stories.tsx +17 -1
  465. package/src/web-components/ui/BdsCarousel.mdx +40 -12
  466. package/src/web-components/ui/BdsDescriptionList.mdx +6 -1
  467. package/src/web-components/ui/BdsIconWrapper.mdx +3 -2
  468. package/src/web-components/ui/BdsLink.mdx +14 -2
  469. package/src/web-components/ui/BdsNotificationBanner.mdx +18 -0
  470. package/src/web-components/ui/BdsPagination.mdx +40 -1
  471. package/src/web-components/ui/BdsProgress.mdx +8 -2
  472. package/src/web-components/ui/BdsProgressCircle.mdx +5 -2
  473. package/src/web-components/ui/BdsRating.mdx +7 -0
  474. package/src/web-components/ui/BdsSectionHeader.mdx +22 -3
  475. package/src/web-components/ui/BdsSectionHeader.stories.tsx +15 -13
  476. package/src/web-components/ui/BdsTable.mdx +23 -2
  477. package/src/web-components/ui/BdsTable.stories.tsx +8 -2
  478. package/src/web-components/ui/BdsTypography.mdx +17 -1
  479. package/src/web-components/ui/bds-alert.spec.ts +17 -2
  480. package/src/web-components/ui/bds-alert.ts +31 -5
  481. package/src/web-components/ui/bds-avatar.spec.ts +29 -0
  482. package/src/web-components/ui/bds-avatar.ts +29 -11
  483. package/src/web-components/ui/bds-badge.spec.ts +5 -0
  484. package/src/web-components/ui/bds-badge.ts +11 -1
  485. package/src/web-components/ui/bds-breadcrumb.spec.ts +30 -0
  486. package/src/web-components/ui/bds-breadcrumb.ts +26 -4
  487. package/src/web-components/ui/bds-button-container.spec.ts +39 -0
  488. package/src/web-components/ui/bds-button-container.ts +123 -0
  489. package/src/web-components/ui/bds-button-group.spec.ts +14 -20
  490. package/src/web-components/ui/bds-button-group.ts +13 -64
  491. package/src/web-components/ui/bds-calendar-range.spec.ts +123 -0
  492. package/src/web-components/ui/bds-calendar-range.ts +307 -0
  493. package/src/web-components/ui/bds-calendar.spec.ts +176 -0
  494. package/src/web-components/ui/bds-calendar.ts +186 -344
  495. package/src/web-components/ui/bds-card.spec.ts +32 -0
  496. package/src/web-components/ui/bds-card.ts +34 -9
  497. package/src/web-components/ui/bds-carousel.spec.ts +147 -0
  498. package/src/web-components/ui/bds-carousel.ts +109 -32
  499. package/src/web-components/ui/bds-description-list.spec.ts +15 -0
  500. package/src/web-components/ui/bds-description-list.ts +37 -3
  501. package/src/web-components/ui/bds-icon-wrapper.spec.ts +12 -0
  502. package/src/web-components/ui/bds-icon-wrapper.ts +15 -14
  503. package/src/web-components/ui/bds-link.spec.ts +17 -0
  504. package/src/web-components/ui/bds-link.ts +26 -11
  505. package/src/web-components/ui/bds-loading.ts +10 -0
  506. package/src/web-components/ui/bds-notification-banner.spec.ts +13 -0
  507. package/src/web-components/ui/bds-notification-banner.ts +18 -1
  508. package/src/web-components/ui/bds-pagination.spec.ts +44 -0
  509. package/src/web-components/ui/bds-pagination.ts +39 -19
  510. package/src/web-components/ui/bds-progress-circle.spec.ts +21 -0
  511. package/src/web-components/ui/bds-progress-circle.ts +17 -5
  512. package/src/web-components/ui/bds-progress.spec.ts +21 -0
  513. package/src/web-components/ui/bds-progress.ts +17 -4
  514. package/src/web-components/ui/bds-rating.spec.ts +6 -0
  515. package/src/web-components/ui/bds-rating.ts +12 -2
  516. package/src/web-components/ui/bds-section-header.spec.ts +39 -8
  517. package/src/web-components/ui/bds-section-header.ts +47 -13
  518. package/src/web-components/ui/bds-separator.ts +10 -0
  519. package/src/web-components/ui/bds-skeleton.ts +10 -0
  520. package/src/web-components/ui/bds-table.spec.ts +63 -0
  521. package/src/web-components/ui/bds-table.ts +88 -28
  522. package/src/web-components/ui/bds-typography.spec.ts +47 -0
  523. package/src/web-components/ui/bds-typography.ts +28 -10
  524. package/src/web-components/ui/calendar-styles.ts +94 -0
  525. package/dist/web-components/chunk-7YURXHTS.js +0 -147
  526. package/dist/web-components/chunk-A3RLT3AS.js +0 -49
  527. package/dist/web-components/chunk-AGKJTMJI.js +0 -56
  528. package/dist/web-components/chunk-BQLZ7I4G.js +0 -56
  529. package/dist/web-components/chunk-DZOEPVFT.js +0 -107
  530. package/dist/web-components/chunk-JYYRJS2D.js +0 -118
  531. package/dist/web-components/chunk-Y4MAXTMA.js +0 -80
  532. package/dist/web-components/chunk-Y7V7LM6R.js +0 -98
  533. package/src/web-components/ui/BdsButtonGroup.mdx +0 -69
  534. package/src/web-components/ui/BdsButtonGroup.stories.tsx +0 -66
@@ -1,21 +1,31 @@
1
- import { LitElement, css, html } from 'lit';
1
+ import { LitElement, css, html, nothing } from 'lit';
2
2
 
3
3
  /**
4
4
  * `<bds-radio>` — form-associated Radio custom element.
5
5
  *
6
6
  * Participates in HTML forms via the Form-Associated Custom Elements API.
7
- * Use multiple `<bds-radio>` elements with the same `name` to form a group.
7
+ * Use multiple `<bds-radio>` elements with the same `name` to form a group
8
+ * checking one automatically unchecks the others sharing that `name`
9
+ * (searched within the same document/shadow root), and Arrow Up/Down/Left/Right
10
+ * move selection between them, mirroring native `<input type="radio">` semantics.
8
11
  *
9
12
  * Attributes:
10
- * name — form field name (groups radio buttons)
11
- * value — submitted value when selected
12
- * checked — boolean; whether this option is selected
13
- * required — boolean
14
- * disabled — boolean
13
+ * name — form field name (groups radio buttons)
14
+ * value — submitted value when selected
15
+ * checked — boolean; whether this option is selected
16
+ * required — boolean
17
+ * disabled — boolean
18
+ * error — boolean; visual + aria-invalid error state
19
+ * description — optional supporting text rendered below the label
15
20
  *
16
21
  * Slots:
17
22
  * (default) — label text
18
23
  *
24
+ * Events:
25
+ * bds-change — fired when this radio becomes checked via user interaction;
26
+ * `detail: { value: string, checked: true }`. Composed so it
27
+ * crosses the shadow boundary (unlike the native `change` event).
28
+ *
19
29
  * @example
20
30
  * <bds-radio name="size" value="s">Small</bds-radio>
21
31
  * <bds-radio name="size" value="m" checked>Medium</bds-radio>
@@ -25,6 +35,16 @@ export class BdsRadio extends LitElement {
25
35
  static formAssociated = true;
26
36
 
27
37
  static styles = css`
38
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
39
+ here; otherwise elements with an explicit size + padding size as
40
+ content-box and layouts drift from the React (light-DOM) versions. */
41
+ :host,
42
+ *,
43
+ *::before,
44
+ *::after {
45
+ box-sizing: border-box;
46
+ }
47
+
28
48
  :host {
29
49
  display: inline-flex;
30
50
  align-items: flex-start;
@@ -42,22 +62,25 @@ export class BdsRadio extends LitElement {
42
62
  }
43
63
 
44
64
  .input {
65
+ box-sizing: border-box;
45
66
  --inputSize: calc(var(--bds-space_s) * 2);
46
- margin-block-start: 0.25em;
67
+ margin-block-start: var(--radio_offset, 0.2em);
47
68
  inline-size: var(--inputSize);
48
69
  block-size: var(--inputSize);
49
70
  flex-shrink: 0;
50
- --bdc_color: var(--radio_border-color, currentcolor);
71
+ color: var(--radio_color, var(--radio_border-color, var(--bds-color_on-bg)));
72
+ border: var(--radio_border, none);
73
+ --bdc_color: currentcolor;
51
74
  --bdc-outline_radius: var(--bds-border_radius--circle);
52
75
 
53
- border-radius: var(--radio_radius, var(--bds-border_radius--circle));
76
+ border-radius: var(--radio_radius, var(--bdc-outline_radius));
54
77
  box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--radio_shadow, var(--bdc-outline_shadow));
55
- border: none;
56
78
  appearance: none;
57
79
  background-color: var(--radio_color_bg, var(--bds-color_bg));
58
80
  cursor: pointer;
59
81
  position: relative;
60
- transition: --bdc_color 0.3s ease, background-color 0.3s ease;
82
+ transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
83
+ background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
61
84
  }
62
85
 
63
86
  .input:checked {
@@ -69,23 +92,45 @@ export class BdsRadio extends LitElement {
69
92
  --bdc_color: var(--radio_color-active, var(--bdc-color_active));
70
93
  }
71
94
 
95
+ .input:focus {
96
+ --bdc_color: var(--bdc_color--focus);
97
+ }
98
+
72
99
  .input:focus-visible {
73
100
  outline: var(--bds-outline_default);
74
101
  outline-offset: var(--bds-outline_offset);
75
102
  }
76
103
 
104
+ :host([error]) .input {
105
+ --bdc_color: var(--bdc_color--error);
106
+ }
107
+
108
+ .textWrapper {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: var(--bds-space_xxxs);
112
+ flex: 1;
113
+ }
114
+
77
115
  label {
78
116
  cursor: pointer;
79
- flex: 1;
117
+ }
118
+
119
+ .description {
120
+ font-size: var(--bds-font_size--body--s);
121
+ color: var(--radio_description-color, var(--bds-color_on-bg--subtle));
122
+ line-height: var(--bds-font_line-height--body);
80
123
  }
81
124
  `;
82
125
 
83
126
  static properties = {
84
- name: { type: String, reflect: true },
85
- value: { type: String },
86
- checked: { type: Boolean, reflect: true },
87
- required: { type: Boolean, reflect: true },
88
- disabled: { type: Boolean, reflect: true },
127
+ name: { type: String, reflect: true },
128
+ value: { type: String },
129
+ checked: { type: Boolean, reflect: true },
130
+ required: { type: Boolean, reflect: true },
131
+ disabled: { type: Boolean, reflect: true },
132
+ error: { type: Boolean, reflect: true },
133
+ description: { type: String },
89
134
  };
90
135
 
91
136
  declare name: string;
@@ -93,9 +138,12 @@ export class BdsRadio extends LitElement {
93
138
  declare checked: boolean;
94
139
  declare required: boolean;
95
140
  declare disabled: boolean;
141
+ declare error: boolean;
142
+ declare description: string | undefined;
96
143
 
97
144
  private _internals!: ElementInternals;
98
145
  private readonly _uid = Math.random().toString(36).slice(2, 8);
146
+ private _initialChecked = false;
99
147
 
100
148
  constructor() {
101
149
  super();
@@ -105,6 +153,12 @@ export class BdsRadio extends LitElement {
105
153
  this.checked = false;
106
154
  this.required = false;
107
155
  this.disabled = false;
156
+ this.error = false;
157
+ }
158
+
159
+ connectedCallback() {
160
+ super.connectedCallback();
161
+ this._initialChecked = this.hasAttribute('checked');
108
162
  }
109
163
 
110
164
  updated(changed: Map<string, unknown>) {
@@ -115,16 +169,68 @@ export class BdsRadio extends LitElement {
115
169
  }
116
170
 
117
171
  formResetCallback() {
118
- this.checked = false;
119
- this._internals.setFormValue?.(null);
172
+ this.checked = this._initialChecked;
173
+ this._internals.setFormValue?.(this._initialChecked ? this.value : null);
174
+ }
175
+
176
+ /** All `<bds-radio>` elements sharing this radio's `name`, in document order (including this one). */
177
+ private _getGroupMembers(): BdsRadio[] {
178
+ if (!this.name) return [this];
179
+ const root = this.getRootNode() as Document | ShadowRoot;
180
+ return Array.from(root.querySelectorAll('bds-radio')).filter(radio => radio.name === this.name);
181
+ }
182
+
183
+ /** Marks `radio` as the sole checked member of its group and notifies listeners. */
184
+ private _selectRadio(radio: BdsRadio) {
185
+ this._getGroupMembers().forEach(member => {
186
+ if (member !== radio) member.checked = false;
187
+ });
188
+ radio.checked = true;
189
+ radio.dispatchEvent(new CustomEvent('bds-change', {
190
+ detail: { value: radio.value, checked: true },
191
+ bubbles: true,
192
+ composed: true,
193
+ }));
120
194
  }
121
195
 
122
196
  private _handleChange(e: Event) {
123
- this.checked = (e.target as HTMLInputElement).checked;
197
+ const isChecked = (e.target as HTMLInputElement).checked;
198
+ if (isChecked) {
199
+ this._selectRadio(this);
200
+ } else {
201
+ this.checked = false;
202
+ }
203
+ }
204
+
205
+ private _handleKeydown = (e: KeyboardEvent) => {
206
+ if (this.disabled) return;
207
+ if (!['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft'].includes(e.key)) return;
208
+
209
+ const group = this._getGroupMembers().filter(member => !member.disabled);
210
+ if (group.length < 2) return;
211
+
212
+ e.preventDefault();
213
+ const currentIndex = group.indexOf(this);
214
+ const direction = e.key === 'ArrowDown' || e.key === 'ArrowRight' ? 1 : -1;
215
+ const next = group[(currentIndex + direction + group.length) % group.length];
216
+
217
+ this._selectRadio(next);
218
+ void next.updateComplete.then(() => {
219
+ next.shadowRoot?.querySelector<HTMLInputElement>('input')?.focus();
220
+ });
221
+ };
222
+
223
+ /** Roving tabindex: only the checked member (or the first member if none is checked) is tabbable. */
224
+ private _isTabbable(): boolean {
225
+ const group = this._getGroupMembers();
226
+ if (group.length <= 1) return true;
227
+ const checkedMember = group.find(member => member.checked);
228
+ return checkedMember ? checkedMember === this : group[0] === this;
124
229
  }
125
230
 
126
231
  render() {
127
232
  const id = `bds-radio-${this._uid}`;
233
+ const descriptionId = `${id}-description`;
128
234
  return html`
129
235
  <input
130
236
  id=${id}
@@ -135,9 +241,16 @@ export class BdsRadio extends LitElement {
135
241
  .checked=${this.checked}
136
242
  ?required=${this.required}
137
243
  ?disabled=${this.disabled}
244
+ tabindex=${this._isTabbable() ? 0 : -1}
245
+ aria-invalid=${this.error ? 'true' : 'false'}
246
+ aria-describedby=${this.description ? descriptionId : nothing}
138
247
  @change=${this._handleChange}
248
+ @keydown=${this._handleKeydown}
139
249
  />
140
- <label for=${id}><slot></slot></label>
250
+ <div class="textWrapper">
251
+ <label for=${id}><slot></slot></label>
252
+ ${this.description ? html`<span id=${descriptionId} class="description">${this.description}</span>` : ''}
253
+ </div>
141
254
  `;
142
255
  }
143
256
  }
@@ -71,4 +71,59 @@ describe('bds-segmented-control', () => {
71
71
  it('is form-associated', () => {
72
72
  expect(BdsSegmentedControl.formAssociated).toBe(true);
73
73
  });
74
+
75
+ it('fires a bds-change event with the selected value in detail', async () => {
76
+ const el = await fixture(`<bds-segmented-control name="view" options='${OPTIONS}'></bds-segmented-control>`);
77
+ const handler = vi.fn();
78
+ el.addEventListener('bds-change', handler);
79
+ const radios = [...el.shadowRoot!.querySelectorAll<HTMLInputElement>('input[type="radio"]')];
80
+ radios[0].dispatchEvent(new Event('change', { bubbles: true }));
81
+ expect(handler).toHaveBeenCalledOnce();
82
+ expect((handler.mock.calls[0][0] as CustomEvent).detail).toEqual({ value: 'list' });
83
+ cleanup(el);
84
+ });
85
+
86
+ it('sets a string aria-disabled value on disabled link-mode options', async () => {
87
+ const navOptions = JSON.stringify([
88
+ { value: 'home', label: 'Home', href: '/' },
89
+ { value: 'about', label: 'About', href: '/about', disabled: true },
90
+ ]);
91
+ const el = await fixture(`<bds-segmented-control value="home" options='${navOptions}'></bds-segmented-control>`);
92
+ const links = [...el.shadowRoot!.querySelectorAll('a')];
93
+ const disabledLink = links.find(a => a.textContent?.trim() === 'About')!;
94
+ const enabledLink = links.find(a => a.textContent?.trim() === 'Home')!;
95
+ expect(disabledLink.getAttribute('aria-disabled')).toBe('true');
96
+ expect(enabledLink.hasAttribute('aria-disabled')).toBe(false);
97
+ cleanup(el);
98
+ });
99
+
100
+ it('restores the initial value attribute on form reset', async () => {
101
+ const el = await fixture(`<bds-segmented-control name="view" value="grid" options='${OPTIONS}'></bds-segmented-control>`) as BdsSegmentedControl;
102
+ el.value = 'list';
103
+ el.formResetCallback();
104
+ expect(el.value).toBe('grid');
105
+ cleanup(el);
106
+ });
107
+
108
+ it('falls back to the first option on form reset when no initial value was set', async () => {
109
+ const el = await fixture(`<bds-segmented-control name="view" options='${OPTIONS}'></bds-segmented-control>`) as BdsSegmentedControl;
110
+ el.value = 'grid';
111
+ el.formResetCallback();
112
+ expect(el.value).toBe('list');
113
+ cleanup(el);
114
+ });
115
+
116
+ it('exposes --control_count and --control_selected-index on the control for CSS-driven positioning', async () => {
117
+ const el = await fixture(`<bds-segmented-control name="view" value="grid" options='${OPTIONS}'></bds-segmented-control>`);
118
+ const control = el.shadowRoot!.querySelector<HTMLElement>('.control')!;
119
+ expect(control.style.getPropertyValue('--control_count')).toBe('3');
120
+ expect(control.style.getPropertyValue('--control_selected-index')).toBe('1');
121
+ cleanup(el);
122
+ });
123
+
124
+ it('uses a focus-visible selector that can actually match (label wrapping the radio)', () => {
125
+ const cssText = BdsSegmentedControl.styles.toString();
126
+ expect(cssText).not.toContain('.item-label');
127
+ expect(cssText).toContain(':has(> .radio:focus-visible)');
128
+ });
74
129
  });
@@ -23,7 +23,8 @@ export interface SegmentedOption {
23
23
  * variant — "outline" (inset border on active item, default) | "filled" (sliding thumb)
24
24
  *
25
25
  * Events:
26
- * change — fired when a new segment is selected (radio mode only)
26
+ * bds-change — fired when a new segment is selected (radio mode only); detail: { value }
27
+ * change — deprecated alias for bds-change, kept for backward compatibility (radio mode only)
27
28
  *
28
29
  * @example
29
30
  * <bds-segmented-control
@@ -43,6 +44,16 @@ export class BdsSegmentedControl extends LitElement {
43
44
  static formAssociated = true;
44
45
 
45
46
  static styles = css`
47
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
48
+ here; otherwise elements with an explicit size + padding size as
49
+ content-box and layouts drift from the React (light-DOM) versions. */
50
+ :host,
51
+ *,
52
+ *::before,
53
+ *::after {
54
+ box-sizing: border-box;
55
+ }
56
+
46
57
  :host {
47
58
  display: inline-flex;
48
59
  }
@@ -53,25 +64,35 @@ export class BdsSegmentedControl extends LitElement {
53
64
  }
54
65
 
55
66
  .control {
56
- display: inline-flex;
57
67
  position: relative;
58
- border-radius: var(--bds-border_radius--s);
59
- background-color: var(--segmented_color_bg, var(--bds-color_bg--subtle));
60
- padding: 0.25rem;
68
+ display: inline-grid;
69
+ grid-auto-flow: column;
70
+ grid-auto-columns: 1fr;
71
+ padding: var(--control_track-pad, var(--bds-space_xxs));
72
+ background-color: var(--control_track-bg, var(--segmented_color_bg, var(--bds-color_bg--subtle)));
73
+ border-radius: var(--control_radius, var(--bds-border_radius--full));
61
74
  gap: 0;
75
+ --control_dir: 1;
76
+ }
77
+
78
+ :host(:dir(rtl)) .control {
79
+ --control_dir: -1;
62
80
  }
63
81
 
64
- /* Sliding thumb */
82
+ /* Sliding thumb (filled variant) — positioned entirely via CSS custom properties
83
+ (--control_count / --control_selected-index, set inline from render()) so it
84
+ stays correct across RTL and resize without any JS measurement. */
65
85
  .thumb {
66
86
  position: absolute;
67
- inset-block: 0.25rem;
68
- border-radius: calc(var(--bds-border_radius--s) - 2px);
69
- background-color: var(--segmented_thumb-color, var(--bds-color_interactive));
70
- box-shadow: var(--bds-shadow_m);
71
- transition: inset-inline-start var(--bds-animation_transition-duration) var(--bds-animation_easing),
72
- inline-size var(--bds-animation_transition-duration) var(--bds-animation_easing);
87
+ inset-block: var(--control_track-pad, var(--bds-space_xxs));
88
+ inset-inline-start: var(--control_track-pad, var(--bds-space_xxs));
89
+ inline-size: calc((100% - 2 * var(--control_track-pad, var(--bds-space_xxs))) / var(--control_count, 1));
90
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
91
+ background-color: var(--control_thumb-bg, var(--segmented_thumb-color, var(--bds-color_interactive)));
92
+ box-shadow: var(--control_thumb-shadow, var(--bds-shadow_s));
73
93
  pointer-events: none;
74
- /* Position set via JS in updated() */
94
+ transform: translateX(calc(var(--control_selected-index, 0) * var(--control_dir, 1) * 100%));
95
+ transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
75
96
  }
76
97
 
77
98
  .radio {
@@ -84,6 +105,7 @@ export class BdsSegmentedControl extends LitElement {
84
105
 
85
106
  .item {
86
107
  position: relative;
108
+ z-index: 1;
87
109
  display: inline-flex;
88
110
  align-items: center;
89
111
  justify-content: center;
@@ -91,9 +113,9 @@ export class BdsSegmentedControl extends LitElement {
91
113
  font-size: var(--bds-font_size--body--s);
92
114
  font-family: var(--bds-font_family--body);
93
115
  font-weight: var(--bds-font_weight--semibold);
94
- color: var(--segmented_color, var(--bds-color_on-bg));
116
+ color: var(--control_label-color, var(--segmented_color, var(--bds-color_on-bg)));
95
117
  cursor: pointer;
96
- border-radius: calc(var(--bds-border_radius--s) - 2px);
118
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
97
119
  transition: color var(--bds-animation_transition-duration) var(--bds-animation_easing);
98
120
  user-select: none;
99
121
  white-space: nowrap;
@@ -101,7 +123,7 @@ export class BdsSegmentedControl extends LitElement {
101
123
  }
102
124
 
103
125
  .item.--active {
104
- color: var(--segmented_color--active, var(--bds-color_on-interactive));
126
+ color: var(--control_label-color--active, var(--segmented_color--active, var(--bds-color_on-interactive)));
105
127
  }
106
128
 
107
129
  .item.--disabled {
@@ -109,21 +131,34 @@ export class BdsSegmentedControl extends LitElement {
109
131
  cursor: not-allowed;
110
132
  }
111
133
 
112
- .radio:focus-visible + .item-label {
134
+ /* Radio-mode items aren't directly focusable (the input is visually hidden inside
135
+ the label), so match focus-visible on the input itself via :has(); link-mode
136
+ items are focusable directly. */
137
+ .item:focus-visible,
138
+ .item:has(> .radio:focus-visible) {
113
139
  outline: var(--bds-outline_default);
114
- outline-offset: var(--bds-outline_offset);
140
+ outline-offset: calc(var(--bds-outline_offset) * -1);
141
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
115
142
  }
116
143
 
117
- /* Outline variant: sliding inset-border indicator (positioned via JS like .thumb) */
144
+ @media (hover: hover) and (pointer: fine) {
145
+ .item:not(.--active, .--disabled):hover {
146
+ color: var(--control_label-color--hover, var(--bds-color_on-bg));
147
+ }
148
+ }
149
+
150
+ /* Outline variant: sliding inset-border indicator (positioned like .thumb) */
118
151
  .indicator {
119
152
  position: absolute;
120
- inset-block: 0.25rem;
121
- border-radius: calc(var(--bds-border_radius--s) - 2px);
153
+ inset-block: var(--control_track-pad, var(--bds-space_xxs));
154
+ inset-inline-start: var(--control_track-pad, var(--bds-space_xxs));
155
+ inline-size: calc((100% - 2 * var(--control_track-pad, var(--bds-space_xxs))) / var(--control_count, 1));
156
+ border-radius: var(--control_thumb-radius, var(--bds-border_radius--full));
122
157
  pointer-events: none;
123
158
  z-index: 2;
124
159
  display: none;
125
- transition: inset-inline-start var(--bds-animation_transition-duration) var(--bds-animation_easing),
126
- inline-size var(--bds-animation_transition-duration) var(--bds-animation_easing);
160
+ transform: translateX(calc(var(--control_selected-index, 0) * var(--control_dir, 1) * 100%));
161
+ transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
127
162
  }
128
163
 
129
164
  /* Outline variant — no thumb, sliding inset-border indicator, interactive active color */
@@ -132,17 +167,17 @@ export class BdsSegmentedControl extends LitElement {
132
167
  }
133
168
 
134
169
  :host([variant='outline']) .item {
135
- background-color: var(--segmented_item-bg, transparent);
170
+ background-color: var(--control_item-bg, var(--segmented_item-bg, transparent));
136
171
  }
137
172
 
138
173
  :host([variant='outline']) .item.--active {
139
- color: var(--segmented_color--active, var(--bds-color_interactive));
140
- background-color: var(--segmented_item-bg--active, var(--bds-color_bg));
174
+ color: var(--control_label-color--active, var(--segmented_color--active, var(--bds-color_interactive)));
175
+ background-color: var(--control_item-bg--active, var(--segmented_item-bg--active, var(--bds-color_bg)));
141
176
  }
142
177
 
143
178
  :host([variant='outline']) .indicator {
144
179
  display: block;
145
- box-shadow: inset 0 0 0 2px var(--bds-color_interactive);
180
+ box-shadow: inset 0 0 0 var(--control_indicator-width, 2px) var(--control_indicator-color, var(--bds-color_interactive));
146
181
  }
147
182
 
148
183
  @media (prefers-reduced-motion: reduce) {
@@ -169,6 +204,7 @@ export class BdsSegmentedControl extends LitElement {
169
204
 
170
205
  private _internals!: ElementInternals;
171
206
  private readonly _uid = Math.random().toString(36).slice(2, 8);
207
+ private _initialValue = '';
172
208
 
173
209
  constructor() {
174
210
  super();
@@ -180,6 +216,11 @@ export class BdsSegmentedControl extends LitElement {
180
216
  this.variant = 'outline';
181
217
  }
182
218
 
219
+ connectedCallback() {
220
+ super.connectedCallback();
221
+ this._initialValue = this.value;
222
+ }
223
+
183
224
  private get _options(): SegmentedOption[] {
184
225
  try {
185
226
  return JSON.parse(this.options) as SegmentedOption[];
@@ -192,52 +233,33 @@ export class BdsSegmentedControl extends LitElement {
192
233
  super.updated(changed);
193
234
  if (changed.has('value') || changed.has('options')) {
194
235
  this._internals.setFormValue?.(this.value || null);
195
- if (this.variant === 'filled') this._updateThumb();
196
- if (this.variant === 'outline') this._updateIndicator();
197
- }
198
- if (changed.has('variant')) {
199
- if (this.variant === 'filled') this._updateThumb();
200
- if (this.variant === 'outline') this._updateIndicator();
201
236
  }
202
237
  }
203
238
 
204
- private _positionSlider(el: HTMLElement | null | undefined, items: NodeListOf<HTMLElement>, selectedIndex: number) {
205
- if (!el || items.length === 0) return;
206
- const item = items[selectedIndex];
207
- if (!item) return;
208
- const controlRect = (item.parentElement as HTMLElement).getBoundingClientRect();
209
- const itemRect = item.getBoundingClientRect();
210
- el.style.insetInlineStart = `${itemRect.left - controlRect.left}px`;
211
- el.style.inlineSize = `${itemRect.width}px`;
212
- }
213
-
214
- private _updateThumb() {
215
- const selectedIndex = Math.max(0, this._options.findIndex(o => o.value === this.value));
216
- const items = this.shadowRoot?.querySelectorAll<HTMLElement>('.item');
217
- this._positionSlider(this.shadowRoot?.querySelector<HTMLElement>('.thumb'), items!, selectedIndex);
218
- }
219
-
220
- private _updateIndicator() {
221
- const selectedIndex = Math.max(0, this._options.findIndex(o => o.value === this.value));
222
- const items = this.shadowRoot?.querySelectorAll<HTMLElement>('.item');
223
- this._positionSlider(this.shadowRoot?.querySelector<HTMLElement>('.indicator'), items!, selectedIndex);
224
- }
225
-
226
239
  formResetCallback() {
227
- const firstOption = this._options[0];
228
- this.value = firstOption?.value ?? '';
240
+ const fallback = this._options[0]?.value ?? '';
241
+ this.value = this._initialValue || fallback;
229
242
  this._internals.setFormValue?.(this.value || null);
230
243
  }
231
244
 
232
245
  private _handleChange(optionValue: string) {
233
246
  this.value = optionValue;
247
+ this.dispatchEvent(new CustomEvent('bds-change', {
248
+ detail: { value: optionValue },
249
+ bubbles: true,
250
+ composed: true,
251
+ }));
252
+ // Deprecated: kept for backward compatibility, prefer 'bds-change'.
234
253
  this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
235
254
  }
236
255
 
237
256
  render() {
238
257
  const opts = this._options;
258
+ const selectedIndex = Math.max(0, opts.findIndex(o => o.value === this.value));
259
+ const controlStyle = `--control_count: ${Math.max(opts.length, 1)}; --control_selected-index: ${selectedIndex}`;
260
+
239
261
  return html`
240
- <div class="control" role="group">
262
+ <div class="control" role="group" style=${controlStyle}>
241
263
  <span class="thumb" aria-hidden="true"></span>
242
264
  <span class="indicator" aria-hidden="true"></span>
243
265
  ${opts.map((opt, i) => {
@@ -251,7 +273,7 @@ export class BdsSegmentedControl extends LitElement {
251
273
  class=${itemClass}
252
274
  href=${isDisabled ? nothing : opt.href}
253
275
  aria-current=${isActive ? 'page' : nothing}
254
- ?aria-disabled=${isDisabled}
276
+ aria-disabled=${isDisabled ? 'true' : nothing}
255
277
  >${opt.label}</a>
256
278
  `;
257
279
  }
@@ -71,4 +71,61 @@ describe('bds-select', () => {
71
71
  it('is form-associated', () => {
72
72
  expect(BdsSelect.formAssociated).toBe(true);
73
73
  });
74
+
75
+ it('fires a composed bds-change event that reaches a host-level listener', async () => {
76
+ const el = await fixture(`<bds-select name="country" options='${OPTIONS}'></bds-select>`);
77
+ const values: string[] = [];
78
+ let composed = false;
79
+ el.addEventListener('bds-change', (e: Event) => {
80
+ composed = (e as CustomEvent).composed;
81
+ values.push((e as CustomEvent).detail.value);
82
+ });
83
+ const select = el.shadowRoot!.querySelector<HTMLSelectElement>('select')!;
84
+ select.value = 'be';
85
+ select.dispatchEvent(new Event('change', { bubbles: true }));
86
+ expect(values).toEqual(['be']);
87
+ expect(composed).toBe(true);
88
+ cleanup(el);
89
+ });
90
+
91
+ it('renders a label when label attribute is set, associated via for/id', async () => {
92
+ const el = await fixture(`<bds-select name="country" label="Country" options='${OPTIONS}'></bds-select>`);
93
+ const label = el.shadowRoot!.querySelector('label')!;
94
+ const select = el.shadowRoot!.querySelector('select')!;
95
+ expect(label.textContent?.trim()).toContain('Country');
96
+ expect(select.id).toBeTruthy();
97
+ expect(label.getAttribute('for')).toBe(select.id);
98
+ cleanup(el);
99
+ });
100
+
101
+ it('forwards a host aria-label to the inner select when no label attribute is set', async () => {
102
+ const el = await fixture(`<bds-select name="country" aria-label="Country" options='${OPTIONS}'></bds-select>`);
103
+ const select = el.shadowRoot!.querySelector('select')!;
104
+ expect(select.getAttribute('aria-label')).toBe('Country');
105
+ cleanup(el);
106
+ });
107
+
108
+ it('does not set aria-label on the inner select when a label attribute is present', async () => {
109
+ const el = await fixture(`<bds-select name="country" label="Country" aria-label="Ignored" options='${OPTIONS}'></bds-select>`);
110
+ const select = el.shadowRoot!.querySelector('select')!;
111
+ expect(select.hasAttribute('aria-label')).toBe(false);
112
+ cleanup(el);
113
+ });
114
+
115
+ it('renders error message and sets aria-invalid/aria-describedby when error is set', async () => {
116
+ const el = await fixture(`<bds-select name="country" error="Please choose a country" options='${OPTIONS}'></bds-select>`);
117
+ const select = el.shadowRoot!.querySelector('select')!;
118
+ const errorMsg = el.shadowRoot!.querySelector('.message--error');
119
+ expect(select.getAttribute('aria-invalid')).toBe('true');
120
+ expect(errorMsg?.textContent?.trim()).toBe('Please choose a country');
121
+ expect(select.getAttribute('aria-describedby')).toContain('-error');
122
+ cleanup(el);
123
+ });
124
+
125
+ it('renders hint message when hint is set (no error)', async () => {
126
+ const el = await fixture(`<bds-select name="country" hint="Pick your country" options='${OPTIONS}'></bds-select>`);
127
+ const hint = el.shadowRoot!.querySelector('.message--hint');
128
+ expect(hint?.textContent?.trim()).toBe('Pick your country');
129
+ cleanup(el);
130
+ });
74
131
  });