@boostdev/design-system-components 2.7.1 → 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 (535) 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-3AY2YKCM.js +223 -0
  15. package/dist/web-components/chunk-3WAV6MDN.js +212 -0
  16. package/dist/web-components/{chunk-CZBIVR4V.js → chunk-47AT5KZD.js} +88 -16
  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-5R6IFELV.js → chunk-AIBOHNDU.js} +11 -1
  23. package/dist/web-components/{chunk-ITHLKR7Q.js → chunk-AP4RDUL2.js} +16 -5
  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-CM3KYZ6Z.js → chunk-JH6LQKLK.js} +78 -6
  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-57TRNCWQ.js → chunk-WFVKU4GX.js} +22 -11
  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-F5E3IT7G.js → chunk-XIZWAWMJ.js} +50 -11
  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 +60 -13
  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 +102 -17
  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 +136 -23
  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 +96 -8
  441. package/src/web-components/interaction/form/bds-slider.spec.ts +83 -0
  442. package/src/web-components/interaction/form/bds-slider.ts +181 -33
  443. package/src/web-components/interaction/form/bds-switch.spec.ts +68 -0
  444. package/src/web-components/interaction/form/bds-switch.ts +142 -41
  445. package/src/web-components/interaction/form/bds-textarea.spec.ts +71 -0
  446. package/src/web-components/interaction/form/bds-textarea.ts +109 -8
  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 +30 -12
  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 +113 -36
  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 +16 -15
  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 +11 -1
  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 +19 -6
  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-5TFWOPLH.js +0 -49
  526. package/dist/web-components/chunk-7FMR53BW.js +0 -127
  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-IYGF6CD5.js +0 -118
  530. package/dist/web-components/chunk-QBB6ZX4R.js +0 -107
  531. package/dist/web-components/chunk-Y3POAKGS.js +0 -133
  532. package/dist/web-components/chunk-Y4MAXTMA.js +0 -80
  533. package/dist/web-components/chunk-Y7V7LM6R.js +0 -98
  534. package/src/web-components/ui/BdsButtonGroup.mdx +0 -69
  535. package/src/web-components/ui/BdsButtonGroup.stories.tsx +0 -66
@@ -61,4 +61,162 @@ describe('bds-field-group', () => {
61
61
  expect(el.style.getPropertyValue('--fieldGroup_field-count')).toBe('3');
62
62
  cleanup(el);
63
63
  });
64
+
65
+ it('renders the auto-collapse probe elements in the shadow DOM', async () => {
66
+ const el = await fixture('<bds-field-group></bds-field-group>');
67
+ expect(el.shadowRoot!.querySelector('.probeContainer')).not.toBeNull();
68
+ expect(el.shadowRoot!.querySelector('.probe')).not.toBeNull();
69
+ cleanup(el);
70
+ });
71
+
72
+ describe('disabled propagation to slotted controls', () => {
73
+ // A shadow-root <fieldset disabled> cannot disable light-DOM children
74
+ // projected through a <slot> — a slotted element's actual DOM parent
75
+ // stays the host, not the shadow fieldset, so the UA's fieldset-disables-
76
+ // descendants algorithm never reaches them. The component sets
77
+ // `.disabled` on each slotted control directly instead.
78
+
79
+ it('propagates disabled to slotted form controls', async () => {
80
+ const el = await fixture(
81
+ '<bds-field-group disabled><input id="a" /><input id="b" /></bds-field-group>',
82
+ ) as BdsFieldGroup;
83
+ const a = el.querySelector('#a') as HTMLInputElement;
84
+ const b = el.querySelector('#b') as HTMLInputElement;
85
+ expect(a.disabled).toBe(true);
86
+ expect(b.disabled).toBe(true);
87
+ cleanup(el);
88
+ });
89
+
90
+ it('re-enables slotted controls when disabled is cleared', async () => {
91
+ const el = await fixture(
92
+ '<bds-field-group disabled><input id="a" /></bds-field-group>',
93
+ ) as BdsFieldGroup;
94
+ el.disabled = false;
95
+ await el.updateComplete;
96
+ const a = el.querySelector('#a') as HTMLInputElement;
97
+ expect(a.disabled).toBe(false);
98
+ cleanup(el);
99
+ });
100
+
101
+ it('propagates disabled to controls slotted in after the group is already disabled', async () => {
102
+ const el = await fixture('<bds-field-group disabled></bds-field-group>') as BdsFieldGroup;
103
+ const input = document.createElement('input');
104
+ input.id = 'late';
105
+ el.appendChild(input);
106
+ // slotchange fires asynchronously; allow a microtask.
107
+ await new Promise(r => requestAnimationFrame(() => r(null)));
108
+ expect(input.disabled).toBe(true);
109
+ cleanup(el);
110
+ });
111
+
112
+ it('does not re-enable a slotted control the consumer had individually disabled', async () => {
113
+ const el = await fixture(
114
+ '<bds-field-group><input id="a" disabled /><input id="b" /></bds-field-group>',
115
+ ) as BdsFieldGroup;
116
+ const a = el.querySelector('#a') as HTMLInputElement;
117
+ const b = el.querySelector('#b') as HTMLInputElement;
118
+
119
+ el.disabled = true;
120
+ await el.updateComplete;
121
+ expect(a.disabled).toBe(true);
122
+ expect(b.disabled).toBe(true);
123
+
124
+ el.disabled = false;
125
+ await el.updateComplete;
126
+ expect(a.disabled).toBe(true); // consumer-set — must stay disabled
127
+ expect(b.disabled).toBe(false); // group-set — re-enabled
128
+ cleanup(el);
129
+ });
130
+ });
131
+
132
+ describe('auto-collapse (mocked ResizeObserver)', () => {
133
+ // jsdom has no ResizeObserver. Install a mock that records each
134
+ // constructed observer and the elements it was told to watch, so tests
135
+ // can drive its callback directly — mirrors the mock used in
136
+ // FieldGroup.spec.tsx for the React counterpart of this same mechanism.
137
+ class MockResizeObserver {
138
+ callback: ResizeObserverCallback;
139
+ observed: Element[] = [];
140
+
141
+ constructor(callback: ResizeObserverCallback) {
142
+ this.callback = callback;
143
+ instances.push(this);
144
+ }
145
+
146
+ observe(el: Element) {
147
+ this.observed.push(el);
148
+ }
149
+
150
+ unobserve() {}
151
+
152
+ disconnect() {}
153
+ }
154
+
155
+ let instances: MockResizeObserver[] = [];
156
+ const originalResizeObserver = globalThis.ResizeObserver;
157
+
158
+ const fireResize = (
159
+ instance: MockResizeObserver,
160
+ fieldsetWidth: number,
161
+ probeWidth: number,
162
+ ) => {
163
+ const [fieldsetEl, probeEl] = instance.observed;
164
+ instance.callback(
165
+ [
166
+ { target: fieldsetEl, contentRect: { width: fieldsetWidth } },
167
+ { target: probeEl, contentRect: { width: probeWidth } },
168
+ ] as unknown as ResizeObserverEntry[],
169
+ instance as unknown as ResizeObserver,
170
+ );
171
+ };
172
+
173
+ beforeEach(() => {
174
+ instances = [];
175
+ (globalThis as unknown as { ResizeObserver: typeof ResizeObserver }).ResizeObserver =
176
+ MockResizeObserver as unknown as typeof ResizeObserver;
177
+ });
178
+
179
+ afterEach(() => {
180
+ if (originalResizeObserver) {
181
+ (globalThis as unknown as { ResizeObserver: typeof ResizeObserver }).ResizeObserver =
182
+ originalResizeObserver;
183
+ } else {
184
+ delete (globalThis as unknown as Record<string, unknown>).ResizeObserver;
185
+ }
186
+ });
187
+
188
+ it('sets data-collapsed when the fieldset is narrower than the derived threshold', async () => {
189
+ const el = await fixture(
190
+ '<bds-field-group><input /><input /></bds-field-group>',
191
+ ) as BdsFieldGroup;
192
+ expect(instances).toHaveLength(1);
193
+
194
+ fireResize(instances[0], 200, 400);
195
+ expect(el.hasAttribute('data-collapsed')).toBe(true);
196
+ cleanup(el);
197
+ });
198
+
199
+ it('clears data-collapsed once the fieldset is wide enough for the row', async () => {
200
+ const el = await fixture(
201
+ '<bds-field-group><input /><input /></bds-field-group>',
202
+ ) as BdsFieldGroup;
203
+
204
+ fireResize(instances[0], 200, 400);
205
+ expect(el.hasAttribute('data-collapsed')).toBe(true);
206
+
207
+ fireResize(instances[0], 600, 400);
208
+ expect(el.hasAttribute('data-collapsed')).toBe(false);
209
+ cleanup(el);
210
+ });
211
+
212
+ it('never collapses the vertical variant', async () => {
213
+ const el = await fixture(
214
+ '<bds-field-group variant="vertical"><input /></bds-field-group>',
215
+ ) as BdsFieldGroup;
216
+
217
+ fireResize(instances[0], 50, 400);
218
+ expect(el.hasAttribute('data-collapsed')).toBe(false);
219
+ cleanup(el);
220
+ });
221
+ });
64
222
  });
@@ -6,14 +6,20 @@ export type FieldGroupVariant = 'horizontal' | 'vertical';
6
6
  * `<bds-field-group>` — framework-agnostic FieldGroup custom element.
7
7
  *
8
8
  * Semantically groups related form fields under a single `<fieldset>` and
9
- * lays them out either horizontally (with a CSS container query that
10
- * collapses to a vertical stack when the FieldGroup itself is too narrow)
11
- * or vertically. Mirrors the React `FieldGroup` component.
9
+ * lays them out either horizontally (auto-collapsing to a vertical stack
10
+ * when the FieldGroup itself is too narrow to fit a full row) or
11
+ * vertically. Mirrors the React `FieldGroup` component.
12
12
  *
13
13
  * Attributes:
14
14
  * variant — "horizontal" (default) | "vertical"
15
15
  * legend — optional legend text rendered above the fields
16
- * disabled — boolean; forwarded to the inner <fieldset>
16
+ * disabled — boolean; forwarded to the inner <fieldset> AND propagated to
17
+ * each directly-slotted form control. A shadow-root `<fieldset disabled>`
18
+ * cannot disable light-DOM children projected through a `<slot>` — its
19
+ * "descendant" is defined over the DOM tree, and a slotted element's
20
+ * actual parent stays the host, not the shadow fieldset — so the WC
21
+ * sets `.disabled` on each slotted control directly (see
22
+ * `_syncDisabledToSlotted`).
17
23
  *
18
24
  * Slots:
19
25
  * (default) — form fields (any element). The slotted-element count is
@@ -21,6 +27,16 @@ export type FieldGroupVariant = 'horizontal' | 'vertical';
21
27
  * `--fieldGroup_field-count` custom property, which the auto-derived
22
28
  * collapse threshold reads.
23
29
  *
30
+ * Auto-collapse mechanism (mirrors FieldGroup.tsx):
31
+ * `@container` query *conditions* cannot reference custom properties — a
32
+ * var() inside a container-query feature value is invalid CSS and is
33
+ * silently dropped, so it can never fire. Instead, an invisible `.probe`
34
+ * element's own inline-size resolves --fieldGroup_collapse-threshold
35
+ * through a normal property value (where var() is valid); a
36
+ * ResizeObserver compares the probe's measured pixel width against the
37
+ * `<fieldset>`'s own width and toggles a `data-collapsed` attribute on
38
+ * the host, which drives the CSS.
39
+ *
24
40
  * @example
25
41
  * <bds-field-group variant="horizontal" legend="Contact details">
26
42
  * <bds-form-input label="First name" name="first"></bds-form-input>
@@ -30,18 +46,25 @@ export type FieldGroupVariant = 'horizontal' | 'vertical';
30
46
  */
31
47
  export class BdsFieldGroup extends LitElement {
32
48
  static styles = css`
49
+ /* Shadow DOM does not inherit the global box-sizing reset, so set it
50
+ here; otherwise elements with an explicit size + padding size as
51
+ content-box and layouts drift from the React (light-DOM) versions. */
52
+ :host,
53
+ *,
54
+ *::before,
55
+ *::after {
56
+ box-sizing: border-box;
57
+ }
58
+
33
59
  :host {
34
60
  display: block;
35
61
 
36
- /* Establish an inline-size containment context so the @container rule
37
- queries the host's own width — independent of the viewport. */
38
- container-type: inline-size;
39
- container-name: field-group;
40
-
41
62
  /* Auto-derived collapse threshold (mirror of the React component).
42
63
  --fieldGroup_field-count is set inline by _onSlotChange; the other
43
64
  tokens use foundation-default fallbacks so the calc rebuilds when
44
- a consumer overrides --fieldGroup_min-field-width or _gap. */
65
+ a consumer overrides --fieldGroup_min-field-width or _gap. Read by
66
+ .probe's inline-size below — see the class doc comment for why a
67
+ direct @container condition can't use this. */
45
68
  --fieldGroup_collapse-threshold:
46
69
  calc(
47
70
  var(--fieldGroup_field-count, 1)
@@ -71,7 +94,8 @@ export class BdsFieldGroup extends LitElement {
71
94
 
72
95
  color:
73
96
  var(--fieldGroup_legend-color, var(--bds-color_on-bg));
74
- font-family: var(--bds-font_family--body);
97
+ font-family:
98
+ var(--fieldGroup_legend-font-family, var(--bds-font_family--body));
75
99
  font-size:
76
100
  var(--fieldGroup_legend-font-size, var(--bds-font_size--heading-3));
77
101
  font-weight:
@@ -80,6 +104,12 @@ export class BdsFieldGroup extends LitElement {
80
104
  var(--fieldGroup_legend-line-height, var(--bds-font_line-height--heading));
81
105
  }
82
106
 
107
+ /* Disabled group: dim the legend. Slotted fields dim via their own
108
+ :host([disabled]) rule. Parity with the React FieldGroup. */
109
+ :host([disabled]) .legend {
110
+ opacity: 0.5;
111
+ }
112
+
83
113
  .fields {
84
114
  display: flex;
85
115
  flex-direction: column;
@@ -95,17 +125,38 @@ export class BdsFieldGroup extends LitElement {
95
125
  min-inline-size: 0;
96
126
  }
97
127
 
98
- /* Auto-collapse: when the host's inline-size can't fit a complete
99
- horizontal row, switch to a single-column stack. Threshold scales
100
- with the actual slotted-element count (see :host calc above). */
101
- @container field-group (max-inline-size: var(--fieldGroup_collapse-threshold)) {
102
- :host([variant='horizontal']) .fields {
103
- flex-direction: column;
104
- }
128
+ /* Zero-size, overflow-clipped box housing .probe. .probe's inline-size
129
+ is intentionally larger than the fieldset whenever the layout should
130
+ collapse, so it must be fully contained here never left to poke
131
+ into the page but clipping only affects paint/scroll, not .probe's
132
+ own resolved box size, so ResizeObserver still reports its true
133
+ width. */
134
+ .probeContainer {
135
+ position: relative;
136
+ inline-size: 0;
137
+ block-size: 0;
138
+ overflow: hidden;
139
+ }
105
140
 
106
- :host([variant='horizontal']) .fields ::slotted(*) {
107
- flex: 1 1 auto;
108
- }
141
+ .probe {
142
+ position: absolute;
143
+ inset-block-start: 0;
144
+ inset-inline-start: 0;
145
+ block-size: 0;
146
+ inline-size: var(--fieldGroup_collapse-threshold);
147
+ visibility: hidden;
148
+ }
149
+
150
+ /* JS (see _onResize/_updateCollapsed) toggles data-collapsed on the host
151
+ once the fieldset's own inline-size can no longer fit a complete
152
+ horizontal row. Threshold scales with the actual slotted-element
153
+ count (see :host calc above). */
154
+ :host([variant='horizontal'][data-collapsed='true']) .fields {
155
+ flex-direction: column;
156
+ }
157
+
158
+ :host([variant='horizontal'][data-collapsed='true']) .fields ::slotted(*) {
159
+ flex: 1 1 auto;
109
160
  }
110
161
  `;
111
162
 
@@ -119,6 +170,17 @@ export class BdsFieldGroup extends LitElement {
119
170
  declare legend: string;
120
171
  declare disabled: boolean;
121
172
 
173
+ private _ro?: ResizeObserver;
174
+ private _fieldsetEl?: HTMLFieldSetElement;
175
+ private _probeEl?: HTMLElement;
176
+ private _fieldsetSize = 0;
177
+ private _thresholdSize = 0;
178
+
179
+ // Slotted controls this instance disabled on the group's behalf, so
180
+ // re-enabling the group never overrides a control the consumer had
181
+ // individually disabled beforehand.
182
+ private _groupDisabledEls = new WeakSet<HTMLElement>();
183
+
122
184
  constructor() {
123
185
  super();
124
186
  this.variant = 'horizontal';
@@ -126,14 +188,93 @@ export class BdsFieldGroup extends LitElement {
126
188
  this.disabled = false;
127
189
  }
128
190
 
129
- // Arrow-bound for stable identity (unused here but matches the
130
- // bds-popover / bds-button conventions for handlers attached in render).
191
+ connectedCallback() {
192
+ super.connectedCallback();
193
+ // disconnectedCallback tears the observer down; firstUpdated() only
194
+ // fires once per element instance, so a reconnect (e.g. the element was
195
+ // moved in the DOM) needs its own re-arm. No-op on first connect — the
196
+ // shadow root hasn't rendered yet, so _setupObserver finds no elements
197
+ // and firstUpdated() performs the initial setup once it has.
198
+ if (this.hasUpdated) this._setupObserver();
199
+ }
200
+
201
+ disconnectedCallback() {
202
+ super.disconnectedCallback();
203
+ this._ro?.disconnect();
204
+ this._ro = undefined;
205
+ }
206
+
207
+ firstUpdated() {
208
+ this._fieldsetEl = this.renderRoot.querySelector('fieldset') ?? undefined;
209
+ this._probeEl = this.renderRoot.querySelector<HTMLElement>('.probe') ?? undefined;
210
+ this._setupObserver();
211
+ }
212
+
213
+ updated(changed: Map<string, unknown>) {
214
+ super.updated(changed);
215
+ if (changed.has('variant')) {
216
+ this._updateCollapsed();
217
+ }
218
+ if (changed.has('disabled')) {
219
+ this._syncDisabledToSlotted();
220
+ }
221
+ }
222
+
223
+ private _setupObserver() {
224
+ if (this._ro || typeof ResizeObserver === 'undefined') return;
225
+ if (!this._fieldsetEl || !this._probeEl) return;
226
+ this._ro = new ResizeObserver(this._onResize);
227
+ this._ro.observe(this._fieldsetEl);
228
+ this._ro.observe(this._probeEl);
229
+ }
230
+
231
+ // Arrow-bound for stable identity across connect/disconnect cycles.
232
+ private _onResize = (entries: ResizeObserverEntry[]) => {
233
+ for (const entry of entries) {
234
+ const size = entry.contentBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
235
+ if (entry.target === this._fieldsetEl) this._fieldsetSize = size;
236
+ else if (entry.target === this._probeEl) this._thresholdSize = size;
237
+ }
238
+ this._updateCollapsed();
239
+ };
240
+
241
+ private _updateCollapsed() {
242
+ const shouldCollapse =
243
+ this.variant === 'horizontal' && this._fieldsetSize < this._thresholdSize;
244
+ this.toggleAttribute('data-collapsed', shouldCollapse);
245
+ }
246
+
247
+ // Arrow-bound for stable identity, matching the bds-popover/bds-button
248
+ // convention for handlers attached in render.
131
249
  private _onSlotChange = (e: Event) => {
132
250
  const slot = e.target as HTMLSlotElement;
133
251
  const count = slot.assignedElements().length;
134
252
  this.style.setProperty('--fieldGroup_field-count', String(count));
253
+ this._syncDisabledToSlotted();
135
254
  };
136
255
 
256
+ // Propagates `disabled` to each directly-slotted form control. Only
257
+ // touches controls this instance disabled itself (tracked in
258
+ // _groupDisabledEls) when re-enabling, so a control the consumer
259
+ // individually disabled stays disabled after the group re-enables.
260
+ private _syncDisabledToSlotted() {
261
+ const slot = this.renderRoot.querySelector('slot');
262
+ if (!slot) return;
263
+ for (const el of slot.assignedElements({ flatten: true })) {
264
+ if (!('disabled' in el)) continue;
265
+ const field = el as HTMLElement & { disabled: boolean };
266
+ if (this.disabled) {
267
+ if (!field.disabled) {
268
+ field.disabled = true;
269
+ this._groupDisabledEls.add(field);
270
+ }
271
+ } else if (this._groupDisabledEls.has(field)) {
272
+ field.disabled = false;
273
+ this._groupDisabledEls.delete(field);
274
+ }
275
+ }
276
+ }
277
+
137
278
  render() {
138
279
  return html`
139
280
  <fieldset ?disabled=${this.disabled}>
@@ -143,6 +284,9 @@ export class BdsFieldGroup extends LitElement {
143
284
  <div class="fields">
144
285
  <slot @slotchange=${this._onSlotChange}></slot>
145
286
  </div>
287
+ <div class="probeContainer">
288
+ <div class="probe"></div>
289
+ </div>
146
290
  </fieldset>
147
291
  `;
148
292
  }
@@ -12,16 +12,30 @@ describe('bds-file-input', () => {
12
12
  cleanup(el);
13
13
  });
14
14
 
15
- it('renders a drop zone with role="button"', async () => {
15
+ it('renders the drop zone as a <label> associated with the shadow input', async () => {
16
16
  const el = await fixture('<bds-file-input name="avatar"></bds-file-input>');
17
- expect(el.shadowRoot!.querySelector('[role="button"]')).not.toBeNull();
17
+ const zone = el.shadowRoot!.querySelector('.zone')!;
18
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input[type="file"]')!;
19
+ expect(zone.tagName).toBe('LABEL');
20
+ expect(zone.getAttribute('for')).toBe(input.id);
18
21
  cleanup(el);
19
22
  });
20
23
 
21
- it('drop zone has aria-label="Upload file"', async () => {
24
+ it('drop zone has no role/tabindex/aria-label overriding the slotted label', async () => {
22
25
  const el = await fixture('<bds-file-input name="avatar"></bds-file-input>');
23
- const zone = el.shadowRoot!.querySelector('[role="button"]')!;
24
- expect(zone.getAttribute('aria-label')).toBe('Upload file');
26
+ const zone = el.shadowRoot!.querySelector('.zone')!;
27
+ expect(zone.hasAttribute('role')).toBe(false);
28
+ expect(zone.hasAttribute('tabindex')).toBe(false);
29
+ expect(zone.hasAttribute('aria-label')).toBe(false);
30
+ cleanup(el);
31
+ });
32
+
33
+ it('shadow input is the only reachable tab stop (not a 0x0 second stop)', async () => {
34
+ const el = await fixture('<bds-file-input name="avatar"></bds-file-input>');
35
+ const zone = el.shadowRoot!.querySelector('.zone')!;
36
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input[type="file"]')!;
37
+ expect(zone.hasAttribute('tabindex')).toBe(false);
38
+ expect(input.tabIndex).toBe(0);
25
39
  cleanup(el);
26
40
  });
27
41
 
@@ -55,4 +69,58 @@ describe('bds-file-input', () => {
55
69
  it('is form-associated', () => {
56
70
  expect(BdsFileInput.formAssociated).toBe(true);
57
71
  });
72
+
73
+ it('filters dropped files by the accept attribute before dispatching bds-change', async () => {
74
+ const el = await fixture('<bds-file-input name="avatar" accept="image/*"></bds-file-input>');
75
+ const zone = el.shadowRoot!.querySelector('.zone')!;
76
+ const acceptedFile = new File(['a'], 'photo.png', { type: 'image/png' });
77
+ const rejectedFile = new File(['b'], 'notes.txt', { type: 'text/plain' });
78
+
79
+ const handler = vi.fn();
80
+ el.addEventListener('bds-change', handler);
81
+
82
+ const dropEvent = new Event('drop', { bubbles: true, cancelable: true });
83
+ Object.defineProperty(dropEvent, 'dataTransfer', { value: { files: [acceptedFile, rejectedFile] } });
84
+ zone.dispatchEvent(dropEvent);
85
+ await el.updateComplete;
86
+
87
+ expect(handler).toHaveBeenCalledOnce();
88
+ const detail = handler.mock.calls[0][0].detail as File[];
89
+ expect(Array.from(detail).map(f => f.name)).toEqual(['photo.png']);
90
+ cleanup(el);
91
+ });
92
+
93
+ it('does not dispatch bds-change when no dropped files match accept', async () => {
94
+ const el = await fixture('<bds-file-input name="avatar" accept="image/*"></bds-file-input>');
95
+ const zone = el.shadowRoot!.querySelector('.zone')!;
96
+ const rejectedFile = new File(['b'], 'notes.txt', { type: 'text/plain' });
97
+
98
+ const handler = vi.fn();
99
+ el.addEventListener('bds-change', handler);
100
+
101
+ const dropEvent = new Event('drop', { bubbles: true, cancelable: true });
102
+ Object.defineProperty(dropEvent, 'dataTransfer', { value: { files: [rejectedFile] } });
103
+ zone.dispatchEvent(dropEvent);
104
+ await el.updateComplete;
105
+
106
+ expect(handler).not.toHaveBeenCalled();
107
+ cleanup(el);
108
+ });
109
+
110
+ it('ignores drops while disabled', async () => {
111
+ const el = await fixture('<bds-file-input name="avatar" disabled></bds-file-input>');
112
+ const zone = el.shadowRoot!.querySelector('.zone')!;
113
+ const file = new File(['a'], 'photo.png', { type: 'image/png' });
114
+
115
+ const handler = vi.fn();
116
+ el.addEventListener('bds-change', handler);
117
+
118
+ const dropEvent = new Event('drop', { bubbles: true, cancelable: true });
119
+ Object.defineProperty(dropEvent, 'dataTransfer', { value: { files: [file] } });
120
+ zone.dispatchEvent(dropEvent);
121
+ await el.updateComplete;
122
+
123
+ expect(handler).not.toHaveBeenCalled();
124
+ cleanup(el);
125
+ });
58
126
  });