@boostdev/design-system-components 1.1.1 → 1.2.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 (320) hide show
  1. package/AGENTS.md +13 -10
  2. package/dist/client.cjs +77 -99
  3. package/dist/client.css +611 -580
  4. package/dist/client.d.cts +13 -16
  5. package/dist/client.d.ts +13 -16
  6. package/dist/client.js +87 -109
  7. package/dist/index.cjs +77 -99
  8. package/dist/index.css +611 -580
  9. package/dist/index.d.cts +13 -16
  10. package/dist/index.d.ts +13 -16
  11. package/dist/index.js +87 -109
  12. package/dist/native/index.cjs +9 -8
  13. package/dist/native/index.d.cts +1 -1
  14. package/dist/native/index.d.ts +1 -1
  15. package/dist/native/index.js +9 -8
  16. package/dist/web-components/chunk-2QA62WCQ.js +48 -0
  17. package/dist/web-components/chunk-3REOIRDW.js +246 -0
  18. package/dist/web-components/chunk-57TRNCWQ.js +93 -0
  19. package/dist/web-components/chunk-5IPHEONG.js +139 -0
  20. package/dist/web-components/chunk-5R6IFELV.js +63 -0
  21. package/dist/web-components/chunk-5TFWOPLH.js +49 -0
  22. package/dist/web-components/chunk-6I3CFC26.js +89 -0
  23. package/dist/web-components/chunk-6MH5UWUD.js +226 -0
  24. package/dist/web-components/chunk-7FMR53BW.js +127 -0
  25. package/dist/web-components/chunk-AGKJTMJI.js +56 -0
  26. package/dist/web-components/chunk-AJSXNDAP.js +151 -0
  27. package/dist/web-components/chunk-BQLZ7I4G.js +56 -0
  28. package/dist/web-components/chunk-BX3IPVOB.js +167 -0
  29. package/dist/web-components/chunk-CR5PGUHV.js +109 -0
  30. package/dist/web-components/chunk-DI46Q2EA.js +278 -0
  31. package/dist/web-components/chunk-DZRSJGPB.js +53 -0
  32. package/dist/web-components/chunk-EBJM3VD4.js +107 -0
  33. package/dist/web-components/chunk-ECW3HHWA.js +185 -0
  34. package/dist/web-components/chunk-HGBNDR22.js +98 -0
  35. package/dist/web-components/chunk-HHHRF2PS.js +118 -0
  36. package/dist/web-components/chunk-ITHLKR7Q.js +105 -0
  37. package/dist/web-components/chunk-JUKB3BUP.js +186 -0
  38. package/dist/web-components/chunk-L3XHOMN6.js +111 -0
  39. package/dist/{web-components.js → web-components/chunk-LGEZYFUU.js} +62 -844
  40. package/dist/web-components/chunk-LY62SAVN.js +64 -0
  41. package/dist/web-components/chunk-N6IMUOY4.js +185 -0
  42. package/dist/web-components/chunk-OHUTLSIQ.js +69 -0
  43. package/dist/web-components/chunk-OQTRTMAB.js +27 -0
  44. package/dist/web-components/chunk-PY62FTRW.js +179 -0
  45. package/dist/web-components/chunk-Q4T5BJT6.js +94 -0
  46. package/dist/web-components/chunk-QDST57JC.js +119 -0
  47. package/dist/web-components/chunk-QLZ2362S.js +279 -0
  48. package/dist/web-components/chunk-QPOKHLMQ.js +45 -0
  49. package/dist/web-components/chunk-SOQQ7RVJ.js +88 -0
  50. package/dist/web-components/chunk-T6AETZRP.js +131 -0
  51. package/dist/web-components/chunk-UFFCKVZE.js +149 -0
  52. package/dist/web-components/chunk-VMMVWLXL.js +53 -0
  53. package/dist/web-components/chunk-X3FKVHLK.js +144 -0
  54. package/dist/web-components/chunk-XNA6WTXG.js +133 -0
  55. package/dist/web-components/chunk-Y4MAXTMA.js +80 -0
  56. package/dist/web-components/chunk-YLVTEY23.js +165 -0
  57. package/dist/web-components/chunk-YRXCVKHV.js +116 -0
  58. package/dist/web-components/chunk-ZTC6GRP7.js +64 -0
  59. package/dist/web-components/globals.d.ts +2 -0
  60. package/dist/web-components/globals.js +1 -0
  61. package/dist/web-components/index.d.ts +41 -0
  62. package/dist/web-components/index.js +165 -0
  63. package/dist/web-components/interaction/bds-accordion-item.d.ts +54 -0
  64. package/dist/web-components/interaction/bds-accordion-item.js +7 -0
  65. package/dist/web-components/interaction/bds-accordion.d.ts +50 -0
  66. package/dist/web-components/interaction/bds-accordion.js +7 -0
  67. package/dist/web-components/interaction/bds-button.d.ts +100 -0
  68. package/dist/web-components/interaction/bds-button.js +7 -0
  69. package/dist/web-components/interaction/bds-collapsible.d.ts +52 -0
  70. package/dist/web-components/interaction/bds-collapsible.js +7 -0
  71. package/dist/web-components/interaction/bds-dialog.d.ts +56 -0
  72. package/dist/web-components/interaction/bds-dialog.js +7 -0
  73. package/dist/web-components/interaction/bds-drawer.d.ts +64 -0
  74. package/dist/web-components/interaction/bds-drawer.js +7 -0
  75. package/dist/web-components/interaction/bds-popover.d.ts +62 -0
  76. package/dist/web-components/interaction/bds-popover.js +7 -0
  77. package/dist/web-components/interaction/bds-skip-link.d.ts +36 -0
  78. package/dist/web-components/interaction/bds-skip-link.js +7 -0
  79. package/dist/web-components/interaction/bds-tab-panel.d.ts +26 -0
  80. package/dist/web-components/interaction/bds-tab-panel.js +7 -0
  81. package/dist/web-components/interaction/bds-tab.d.ts +45 -0
  82. package/dist/web-components/interaction/bds-tab.js +7 -0
  83. package/dist/web-components/interaction/bds-tabs.d.ts +46 -0
  84. package/dist/web-components/interaction/bds-tabs.js +7 -0
  85. package/dist/web-components/interaction/bds-toast-provider.d.ts +57 -0
  86. package/dist/web-components/interaction/bds-toast-provider.js +7 -0
  87. package/dist/web-components/interaction/bds-tooltip.d.ts +50 -0
  88. package/dist/web-components/interaction/bds-tooltip.js +7 -0
  89. package/dist/web-components/interaction/form/bds-checkbox.d.ts +70 -0
  90. package/dist/web-components/interaction/form/bds-checkbox.js +7 -0
  91. package/dist/web-components/interaction/form/bds-combobox.d.ts +84 -0
  92. package/dist/web-components/interaction/form/bds-combobox.js +7 -0
  93. package/dist/web-components/interaction/form/bds-file-input.d.ts +62 -0
  94. package/dist/web-components/interaction/form/bds-file-input.js +7 -0
  95. package/dist/web-components/interaction/form/bds-number-input.d.ts +68 -0
  96. package/dist/web-components/interaction/form/bds-number-input.js +7 -0
  97. package/dist/web-components/interaction/form/bds-radio.d.ts +68 -0
  98. package/dist/web-components/interaction/form/bds-radio.js +7 -0
  99. package/dist/web-components/interaction/form/bds-segmented-control.d.ts +88 -0
  100. package/dist/web-components/interaction/form/bds-segmented-control.js +7 -0
  101. package/dist/web-components/interaction/form/bds-select.d.ts +79 -0
  102. package/dist/web-components/interaction/form/bds-select.js +7 -0
  103. package/dist/web-components/interaction/form/bds-slider.d.ts +70 -0
  104. package/dist/web-components/interaction/form/bds-slider.js +7 -0
  105. package/dist/web-components/interaction/form/bds-switch.d.ts +67 -0
  106. package/dist/web-components/interaction/form/bds-switch.js +7 -0
  107. package/dist/web-components/interaction/form/bds-textarea.d.ts +70 -0
  108. package/dist/web-components/interaction/form/bds-textarea.js +7 -0
  109. package/dist/web-components/ui/bds-alert.d.ts +66 -0
  110. package/dist/web-components/ui/bds-alert.js +7 -0
  111. package/dist/web-components/ui/bds-avatar.d.ts +50 -0
  112. package/dist/web-components/ui/bds-avatar.js +7 -0
  113. package/dist/web-components/ui/bds-badge.d.ts +35 -0
  114. package/dist/web-components/ui/bds-badge.js +7 -0
  115. package/dist/web-components/ui/bds-breadcrumb.d.ts +35 -0
  116. package/dist/web-components/ui/bds-breadcrumb.js +7 -0
  117. package/dist/web-components/ui/bds-card.d.ts +72 -0
  118. package/dist/web-components/ui/bds-card.js +7 -0
  119. package/dist/web-components/ui/bds-description-list.d.ts +42 -0
  120. package/dist/web-components/ui/bds-description-list.js +7 -0
  121. package/dist/web-components/ui/bds-icon-wrapper.d.ts +39 -0
  122. package/dist/web-components/ui/bds-icon-wrapper.js +7 -0
  123. package/dist/web-components/ui/bds-link.d.ts +59 -0
  124. package/dist/web-components/ui/bds-link.js +7 -0
  125. package/dist/web-components/ui/bds-loading.d.ts +32 -0
  126. package/dist/web-components/ui/bds-loading.js +7 -0
  127. package/dist/web-components/ui/bds-notification-banner.d.ts +61 -0
  128. package/dist/web-components/ui/bds-notification-banner.js +7 -0
  129. package/dist/web-components/ui/bds-progress-circle.d.ts +53 -0
  130. package/dist/web-components/ui/bds-progress-circle.js +7 -0
  131. package/dist/web-components/ui/bds-progress.d.ts +54 -0
  132. package/dist/web-components/ui/bds-progress.js +7 -0
  133. package/dist/web-components/ui/bds-rating.d.ts +36 -0
  134. package/dist/web-components/ui/bds-rating.js +7 -0
  135. package/dist/web-components/ui/bds-section-header.d.ts +53 -0
  136. package/dist/web-components/ui/bds-section-header.js +8 -0
  137. package/dist/web-components/ui/bds-separator.d.ts +32 -0
  138. package/dist/web-components/ui/bds-separator.js +7 -0
  139. package/dist/web-components/ui/bds-skeleton.d.ts +22 -0
  140. package/dist/web-components/ui/bds-skeleton.js +7 -0
  141. package/dist/web-components/ui/bds-typography.d.ts +44 -0
  142. package/dist/web-components/ui/bds-typography.js +8 -0
  143. package/package.json +170 -5
  144. package/src/components/interaction/Button/Button.module.css +12 -11
  145. package/src/components/interaction/Button/Button.native.tsx +10 -9
  146. package/src/components/interaction/Button/Button.tsx +1 -1
  147. package/src/components/interaction/Command/Command.module.css +3 -7
  148. package/src/components/interaction/Dialog/Dialog.module.css +7 -5
  149. package/src/components/interaction/Drawer/Drawer.module.css +49 -1
  150. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +1 -3
  151. package/src/components/interaction/Popover/Popover.module.css +1 -3
  152. package/src/components/interaction/form/Checkbox/Checkbox.module.css +1 -3
  153. package/src/components/interaction/form/Combobox/Combobox.module.css +2 -6
  154. package/src/components/interaction/form/FormInput/FormInput.module.css +1 -3
  155. package/src/components/interaction/form/NumberInput/NumberInput.module.css +16 -4
  156. package/src/components/interaction/form/Radio/Radio.module.css +6 -16
  157. package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +70 -25
  158. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +55 -33
  159. package/src/components/interaction/form/SegmentedControl/SegmentedControl.spec.tsx +127 -56
  160. package/src/components/interaction/form/SegmentedControl/SegmentedControl.stories.tsx +89 -72
  161. package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +40 -62
  162. package/src/components/interaction/form/SegmentedControl/index.ts +1 -1
  163. package/src/components/interaction/form/Select/Select.module.css +2 -4
  164. package/src/components/interaction/form/Switch/Switch.module.css +5 -7
  165. package/src/components/interaction/form/Switch/Switch.native.spec.tsx +9 -9
  166. package/src/components/interaction/form/Switch/Switch.native.tsx +2 -2
  167. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -3
  168. package/src/components/interaction/form/atoms/InputContainer.stories.tsx +64 -0
  169. package/src/components/interaction/form/atoms/Label.stories.tsx +33 -0
  170. package/src/components/interaction/form/atoms/Message.stories.tsx +33 -0
  171. package/src/components/layout/Card/Card.module.css +1 -3
  172. package/src/components/ui/Accordion/Accordion.module.css +1 -2
  173. package/src/components/ui/Alert/Alert.module.css +1 -2
  174. package/src/components/ui/Calendar/Calendar.module.css +2 -5
  175. package/src/components/ui/Carousel/Carousel.module.css +1 -3
  176. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +1 -2
  177. package/src/components/ui/Pagination/Pagination.module.css +1 -2
  178. package/src/components/ui/SkipLink/SkipLink.module.css +1 -2
  179. package/src/components/ui/Table/Table.module.css +1 -2
  180. package/src/css/bdc.css +16 -11
  181. package/src/index.ts +1 -1
  182. package/src/web-components/globals.ts +3 -3
  183. package/src/web-components/index.ts +47 -0
  184. package/src/web-components/interaction/BdsAccordion.mdx +51 -0
  185. package/src/web-components/interaction/BdsAccordion.stories.tsx +85 -0
  186. package/src/web-components/interaction/BdsCollapsible.mdx +46 -0
  187. package/src/web-components/interaction/BdsCollapsible.stories.tsx +87 -0
  188. package/src/web-components/interaction/BdsDialog.mdx +49 -0
  189. package/src/web-components/interaction/BdsDialog.stories.tsx +75 -0
  190. package/src/web-components/interaction/BdsDrawer.mdx +50 -0
  191. package/src/web-components/interaction/BdsDrawer.stories.tsx +66 -0
  192. package/src/web-components/interaction/BdsSkipLink.mdx +34 -0
  193. package/src/web-components/interaction/BdsSkipLink.stories.tsx +55 -0
  194. package/src/web-components/interaction/BdsTabs.mdx +47 -0
  195. package/src/web-components/interaction/BdsTabs.stories.tsx +80 -0
  196. package/src/web-components/interaction/BdsTooltip.mdx +38 -0
  197. package/src/web-components/interaction/BdsTooltip.stories.tsx +68 -0
  198. package/src/web-components/interaction/bds-accordion-item.spec.ts +75 -0
  199. package/src/web-components/interaction/bds-accordion-item.ts +181 -0
  200. package/src/web-components/interaction/bds-accordion.spec.ts +73 -0
  201. package/src/web-components/interaction/bds-accordion.ts +98 -0
  202. package/src/web-components/interaction/bds-button.ts +15 -13
  203. package/src/web-components/interaction/bds-collapsible.spec.ts +56 -0
  204. package/src/web-components/interaction/bds-collapsible.ts +170 -0
  205. package/src/web-components/interaction/bds-dialog.spec.ts +119 -0
  206. package/src/web-components/interaction/bds-dialog.ts +221 -0
  207. package/src/web-components/interaction/bds-drawer.spec.ts +113 -0
  208. package/src/web-components/interaction/bds-drawer.ts +315 -0
  209. package/src/web-components/interaction/bds-skip-link.spec.ts +42 -0
  210. package/src/web-components/interaction/bds-skip-link.ts +74 -0
  211. package/src/web-components/interaction/bds-tab-panel.spec.ts +32 -0
  212. package/src/web-components/interaction/bds-tab-panel.ts +38 -0
  213. package/src/web-components/interaction/bds-tab.spec.ts +71 -0
  214. package/src/web-components/interaction/bds-tab.ts +110 -0
  215. package/src/web-components/interaction/bds-tabs.spec.ts +120 -0
  216. package/src/web-components/interaction/bds-tabs.ts +152 -0
  217. package/src/web-components/interaction/bds-tooltip.spec.ts +49 -0
  218. package/src/web-components/interaction/bds-tooltip.ts +127 -0
  219. package/src/web-components/interaction/form/BdsCheckbox.mdx +39 -0
  220. package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +73 -0
  221. package/src/web-components/interaction/form/BdsCombobox.mdx +52 -0
  222. package/src/web-components/interaction/form/BdsCombobox.stories.tsx +91 -0
  223. package/src/web-components/interaction/form/BdsFileInput.mdx +44 -0
  224. package/src/web-components/interaction/form/BdsFileInput.stories.tsx +84 -0
  225. package/src/web-components/interaction/form/BdsNumberInput.mdx +41 -0
  226. package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +90 -0
  227. package/src/web-components/interaction/form/BdsRadio.mdx +41 -0
  228. package/src/web-components/interaction/form/BdsRadio.stories.tsx +90 -0
  229. package/src/web-components/interaction/form/BdsSegmentedControl.mdx +42 -0
  230. package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +92 -0
  231. package/src/web-components/interaction/form/BdsSelect.mdx +38 -0
  232. package/src/web-components/interaction/form/BdsSelect.stories.tsx +93 -0
  233. package/src/web-components/interaction/form/BdsSlider.mdx +49 -0
  234. package/src/web-components/interaction/form/BdsSlider.stories.tsx +89 -0
  235. package/src/web-components/interaction/form/BdsSwitch.mdx +39 -0
  236. package/src/web-components/interaction/form/BdsSwitch.stories.tsx +73 -0
  237. package/src/web-components/interaction/form/BdsTextarea.mdx +41 -0
  238. package/src/web-components/interaction/form/BdsTextarea.stories.tsx +79 -0
  239. package/src/web-components/interaction/form/bds-checkbox.spec.ts +63 -0
  240. package/src/web-components/interaction/form/bds-checkbox.ts +166 -0
  241. package/src/web-components/interaction/form/bds-combobox.spec.ts +117 -0
  242. package/src/web-components/interaction/form/bds-combobox.ts +334 -0
  243. package/src/web-components/interaction/form/bds-file-input.spec.ts +58 -0
  244. package/src/web-components/interaction/form/bds-file-input.ts +209 -0
  245. package/src/web-components/interaction/form/bds-number-input.spec.ts +87 -0
  246. package/src/web-components/interaction/form/bds-number-input.ts +220 -0
  247. package/src/web-components/interaction/form/bds-radio.spec.ts +70 -0
  248. package/src/web-components/interaction/form/bds-radio.ts +151 -0
  249. package/src/web-components/interaction/form/bds-segmented-control.spec.ts +74 -0
  250. package/src/web-components/interaction/form/bds-segmented-control.ts +282 -0
  251. package/src/web-components/interaction/form/bds-select.spec.ts +74 -0
  252. package/src/web-components/interaction/form/bds-select.ts +188 -0
  253. package/src/web-components/interaction/form/bds-slider.spec.ts +41 -0
  254. package/src/web-components/interaction/form/bds-slider.ts +162 -0
  255. package/src/web-components/interaction/form/bds-switch.spec.ts +62 -0
  256. package/src/web-components/interaction/form/bds-switch.ts +165 -0
  257. package/src/web-components/interaction/form/bds-textarea.spec.ts +55 -0
  258. package/src/web-components/interaction/form/bds-textarea.ts +141 -0
  259. package/src/web-components/ui/BdsAvatar.mdx +67 -0
  260. package/src/web-components/ui/BdsAvatar.stories.tsx +62 -0
  261. package/src/web-components/ui/BdsBreadcrumb.mdx +63 -0
  262. package/src/web-components/ui/BdsBreadcrumb.stories.tsx +59 -0
  263. package/src/web-components/ui/BdsCard.mdx +83 -0
  264. package/src/web-components/ui/BdsCard.stories.tsx +90 -0
  265. package/src/web-components/ui/BdsDescriptionList.mdx +67 -0
  266. package/src/web-components/ui/BdsDescriptionList.stories.tsx +79 -0
  267. package/src/web-components/ui/BdsIconWrapper.mdx +70 -0
  268. package/src/web-components/ui/BdsIconWrapper.stories.tsx +76 -0
  269. package/src/web-components/ui/BdsLink.mdx +75 -0
  270. package/src/web-components/ui/BdsLink.stories.tsx +64 -0
  271. package/src/web-components/ui/BdsLoading.mdx +62 -0
  272. package/src/web-components/ui/BdsLoading.stories.tsx +37 -0
  273. package/src/web-components/ui/BdsNotificationBanner.mdx +90 -0
  274. package/src/web-components/ui/BdsNotificationBanner.stories.tsx +93 -0
  275. package/src/web-components/ui/BdsProgress.mdx +63 -0
  276. package/src/web-components/ui/BdsProgress.stories.tsx +70 -0
  277. package/src/web-components/ui/BdsProgressCircle.mdx +64 -0
  278. package/src/web-components/ui/BdsProgressCircle.stories.tsx +69 -0
  279. package/src/web-components/ui/BdsRating.mdx +61 -0
  280. package/src/web-components/ui/BdsRating.stories.tsx +39 -0
  281. package/src/web-components/ui/BdsSectionHeader.mdx +69 -0
  282. package/src/web-components/ui/BdsSectionHeader.stories.tsx +56 -0
  283. package/src/web-components/ui/BdsSeparator.mdx +63 -0
  284. package/src/web-components/ui/BdsSeparator.stories.tsx +47 -0
  285. package/src/web-components/ui/BdsSkeleton.mdx +67 -0
  286. package/src/web-components/ui/BdsSkeleton.stories.tsx +50 -0
  287. package/src/web-components/ui/BdsTypography.mdx +83 -0
  288. package/src/web-components/ui/BdsTypography.stories.tsx +51 -0
  289. package/src/web-components/ui/bds-alert.ts +1 -2
  290. package/src/web-components/ui/bds-avatar.spec.ts +75 -0
  291. package/src/web-components/ui/bds-avatar.ts +122 -0
  292. package/src/web-components/ui/bds-breadcrumb.spec.ts +55 -0
  293. package/src/web-components/ui/bds-breadcrumb.ts +114 -0
  294. package/src/web-components/ui/bds-card.spec.ts +70 -0
  295. package/src/web-components/ui/bds-card.ts +154 -0
  296. package/src/web-components/ui/bds-description-list.spec.ts +36 -0
  297. package/src/web-components/ui/bds-description-list.ts +79 -0
  298. package/src/web-components/ui/bds-icon-wrapper.spec.ts +21 -0
  299. package/src/web-components/ui/bds-icon-wrapper.ts +68 -0
  300. package/src/web-components/ui/bds-link.spec.ts +72 -0
  301. package/src/web-components/ui/bds-link.ts +138 -0
  302. package/src/web-components/ui/bds-loading.spec.ts +38 -0
  303. package/src/web-components/ui/bds-loading.ts +76 -0
  304. package/src/web-components/ui/bds-notification-banner.spec.ts +82 -0
  305. package/src/web-components/ui/bds-notification-banner.ts +184 -0
  306. package/src/web-components/ui/bds-progress-circle.spec.ts +58 -0
  307. package/src/web-components/ui/bds-progress-circle.ts +142 -0
  308. package/src/web-components/ui/bds-progress.spec.ts +66 -0
  309. package/src/web-components/ui/bds-progress.ts +127 -0
  310. package/src/web-components/ui/bds-rating.spec.ts +70 -0
  311. package/src/web-components/ui/bds-rating.ts +80 -0
  312. package/src/web-components/ui/bds-section-header.spec.ts +57 -0
  313. package/src/web-components/ui/bds-section-header.ts +96 -0
  314. package/src/web-components/ui/bds-separator.spec.ts +50 -0
  315. package/src/web-components/ui/bds-separator.ts +66 -0
  316. package/src/web-components/ui/bds-skeleton.spec.ts +22 -0
  317. package/src/web-components/ui/bds-skeleton.ts +55 -0
  318. package/src/web-components/ui/bds-typography.spec.ts +76 -0
  319. package/src/web-components/ui/bds-typography.ts +112 -0
  320. package/dist/web-components.d.ts +0 -304
@@ -0,0 +1,56 @@
1
+ import { fixture, cleanup } from '../test/helpers';
2
+ import { BdsCollapsible } from './bds-collapsible';
3
+
4
+ describe('bds-collapsible', () => {
5
+ it('is registered as a custom element', () => {
6
+ expect(customElements.get('bds-collapsible')).toBe(BdsCollapsible);
7
+ });
8
+
9
+ it('is closed by default', async () => {
10
+ const el = await fixture('<bds-collapsible><span slot="summary">Title</span><p>Content</p></bds-collapsible>') as BdsCollapsible;
11
+ expect(el.open).toBe(false);
12
+ expect(el.hasAttribute('open')).toBe(false);
13
+ cleanup(el);
14
+ });
15
+
16
+ it('renders a native <details> element in shadow DOM', async () => {
17
+ const el = await fixture('<bds-collapsible><span slot="summary">Title</span><p>Content</p></bds-collapsible>');
18
+ expect(el.shadowRoot!.querySelector('details')).not.toBeNull();
19
+ cleanup(el);
20
+ });
21
+
22
+ it('renders a <summary> element', async () => {
23
+ const el = await fixture('<bds-collapsible><span slot="summary">Title</span><p>Content</p></bds-collapsible>');
24
+ expect(el.shadowRoot!.querySelector('summary')).not.toBeNull();
25
+ cleanup(el);
26
+ });
27
+
28
+ it('is open when open attribute is set', async () => {
29
+ const el = await fixture('<bds-collapsible open><span slot="summary">Title</span><p>Content</p></bds-collapsible>') as BdsCollapsible;
30
+ expect(el.open).toBe(true);
31
+ const details = el.shadowRoot!.querySelector('details');
32
+ expect(details!.hasAttribute('open')).toBe(true);
33
+ cleanup(el);
34
+ });
35
+
36
+ it('fires bds-toggle event when toggled', async () => {
37
+ const el = await fixture('<bds-collapsible><span slot="summary">Title</span><p>Content</p></bds-collapsible>') as BdsCollapsible;
38
+ const handler = vi.fn();
39
+ el.addEventListener('bds-toggle', handler);
40
+ // jsdom does not fire the native toggle event on summary click;
41
+ // dispatch it directly on the <details> element instead.
42
+ const details = el.shadowRoot!.querySelector('details')!;
43
+ details.dispatchEvent(new Event('toggle', { bubbles: false }));
44
+ await el.updateComplete;
45
+ expect(handler).toHaveBeenCalledOnce();
46
+ cleanup(el);
47
+ });
48
+
49
+ it('reflects open state change from outside', async () => {
50
+ const el = await fixture('<bds-collapsible><span slot="summary">Title</span><p>Content</p></bds-collapsible>') as BdsCollapsible;
51
+ el.open = true;
52
+ await el.updateComplete;
53
+ expect(el.getAttribute('open')).not.toBeNull();
54
+ cleanup(el);
55
+ });
56
+ });
@@ -0,0 +1,170 @@
1
+ import { LitElement, css, html } from 'lit';
2
+
3
+ /**
4
+ * `<bds-collapsible>` — framework-agnostic Collapsible custom element.
5
+ *
6
+ * Wraps a native `<details>/<summary>` element for built-in toggle behaviour.
7
+ *
8
+ * Attributes:
9
+ * open — boolean; controlled open state
10
+ * name — groups multiple collapsibles so only one is open at a time
11
+ * (native <details name="…"> behaviour)
12
+ *
13
+ * Slots:
14
+ * summary — the always-visible trigger text
15
+ * (default) — the content revealed when expanded
16
+ *
17
+ * Events:
18
+ * bds-toggle — fired (bubbles, composed) when the collapsible opens or closes;
19
+ * event.detail.open reflects the new state
20
+ *
21
+ * @example
22
+ * <bds-collapsible>
23
+ * <span slot="summary">What is this?</span>
24
+ * <p>It's a collapsible panel.</p>
25
+ * </bds-collapsible>
26
+ */
27
+ export class BdsCollapsible extends LitElement {
28
+ static styles = css`
29
+ :host {
30
+ display: block;
31
+ }
32
+
33
+ .collapsible {
34
+ border: var(--collapsible_border-width, 1px) solid var(--collapsible_border-color, var(--bds-color_bg--subtle));
35
+ border-radius: var(--collapsible_border-radius, var(--bds-border_radius--m));
36
+ overflow: hidden;
37
+ }
38
+
39
+ .summary {
40
+ list-style: none;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ gap: var(--bds-space_m);
45
+ padding: var(--bds-space_m);
46
+ cursor: pointer;
47
+ font-weight: var(--bds-font_weight--semibold);
48
+ font-size: var(--bds-font_size--body);
49
+ color: var(--collapsible_color, var(--bds-color_on-bg));
50
+ background-color: var(--collapsible_bg, var(--bds-color_bg));
51
+ user-select: none;
52
+ transition: var(--bds-animation_transition);
53
+ }
54
+
55
+ .summary::-webkit-details-marker {
56
+ display: none;
57
+ }
58
+
59
+ .summary:focus-visible {
60
+ outline: var(--bds-outline_default);
61
+ outline-offset: calc(var(--bds-outline_offset) * -1);
62
+ }
63
+
64
+ @media (hover: hover) and (pointer: fine) {
65
+ .summary:hover {
66
+ background-color: var(--collapsible_bg--hover, var(--bds-color_bg--subtle));
67
+ }
68
+ }
69
+
70
+ .collapsible[open] > .summary {
71
+ border-block-end: var(--collapsible_border-width, 1px) solid var(--collapsible_border-color, var(--bds-color_bg--subtle));
72
+ }
73
+
74
+ .summary-content {
75
+ flex: 1;
76
+ }
77
+
78
+ .icon {
79
+ inline-size: 0.5rem;
80
+ block-size: 0.5rem;
81
+ border-inline-end: 2px solid currentcolor;
82
+ border-block-end: 2px solid currentcolor;
83
+ transform: rotate(45deg);
84
+ flex-shrink: 0;
85
+ transition: transform var(--bds-animation_transition-duration) var(--bds-animation_easing);
86
+ }
87
+
88
+ .collapsible[open] > .summary .icon {
89
+ transform: rotate(-135deg);
90
+ }
91
+
92
+ .content {
93
+ padding: var(--bds-space_m);
94
+ color: var(--collapsible_on-color, var(--bds-color_on-bg));
95
+ font-size: var(--bds-font_size--body);
96
+ line-height: var(--bds-font_line-height--body);
97
+ }
98
+
99
+ @media (prefers-reduced-motion: reduce) {
100
+ .icon {
101
+ transition: none;
102
+ }
103
+ }
104
+ `;
105
+
106
+ static properties = {
107
+ open: { type: Boolean, reflect: true },
108
+ name: { type: String },
109
+ };
110
+
111
+ declare open: boolean;
112
+ declare name: string;
113
+
114
+ constructor() {
115
+ super();
116
+ this.open = false;
117
+ this.name = '';
118
+ }
119
+
120
+ updated(changed: Map<string, unknown>) {
121
+ super.updated(changed);
122
+ // Keep the inner <details> name attribute in sync since it can't be bound directly
123
+ if (changed.has('name')) {
124
+ const details = this.shadowRoot?.querySelector('details');
125
+ if (details) {
126
+ if (this.name) {
127
+ details.setAttribute('name', this.name);
128
+ } else {
129
+ details.removeAttribute('name');
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ private _onToggle(e: Event) {
136
+ const details = e.currentTarget as HTMLDetailsElement;
137
+ this.open = details.open;
138
+ this.dispatchEvent(
139
+ new CustomEvent('bds-toggle', {
140
+ bubbles: true,
141
+ composed: true,
142
+ detail: { open: this.open },
143
+ }),
144
+ );
145
+ }
146
+
147
+ render() {
148
+ return html`
149
+ <details
150
+ class="collapsible"
151
+ ?open=${this.open}
152
+ @toggle=${this._onToggle}
153
+ >
154
+ <summary class="summary">
155
+ <span class="summary-content"><slot name="summary"></slot></span>
156
+ <span class="icon" aria-hidden="true"></span>
157
+ </summary>
158
+ <div class="content"><slot></slot></div>
159
+ </details>
160
+ `;
161
+ }
162
+ }
163
+
164
+ customElements.define('bds-collapsible', BdsCollapsible);
165
+
166
+ declare global {
167
+ interface HTMLElementTagNameMap {
168
+ 'bds-collapsible': BdsCollapsible;
169
+ }
170
+ }
@@ -0,0 +1,119 @@
1
+ import { fixture, cleanup, makeCommandEvent } from '../test/helpers';
2
+ import { BdsDialog } from './bds-dialog';
3
+
4
+ beforeEach(() => {
5
+ // jsdom doesn't implement showModal/close — mock them to simulate the open attribute
6
+ HTMLDialogElement.prototype.showModal = vi.fn(function (this: HTMLDialogElement) {
7
+ this.setAttribute('open', '');
8
+ });
9
+ HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
10
+ this.removeAttribute('open');
11
+ });
12
+ document.body.style.overflow = '';
13
+ });
14
+
15
+ describe('bds-dialog', () => {
16
+ it('is registered as a custom element', () => {
17
+ expect(customElements.get('bds-dialog')).toBe(BdsDialog);
18
+ });
19
+
20
+ it('renders a <dialog> element in shadow DOM', async () => {
21
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>');
22
+ expect(el.shadowRoot!.querySelector('dialog')).not.toBeNull();
23
+ cleanup(el);
24
+ });
25
+
26
+ it('dialog has aria-modal="true"', async () => {
27
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>');
28
+ const dialog = el.shadowRoot!.querySelector('dialog')!;
29
+ expect(dialog.getAttribute('aria-modal')).toBe('true');
30
+ cleanup(el);
31
+ });
32
+
33
+ it('renders a close button with aria-label="Close dialog"', async () => {
34
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>');
35
+ const btn = el.shadowRoot!.querySelector('button.close-button');
36
+ expect(btn).not.toBeNull();
37
+ expect(btn!.getAttribute('aria-label')).toBe('Close dialog');
38
+ cleanup(el);
39
+ });
40
+
41
+ it('calls showModal when open attribute is set', async () => {
42
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>');
43
+ expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalled();
44
+ cleanup(el);
45
+ });
46
+
47
+ it('does not call showModal when closed', async () => {
48
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>');
49
+ expect(HTMLDialogElement.prototype.showModal).not.toHaveBeenCalled();
50
+ cleanup(el);
51
+ });
52
+
53
+ it('sets body overflow to hidden when open', async () => {
54
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>');
55
+ expect(document.body.style.overflow).toBe('hidden');
56
+ cleanup(el);
57
+ });
58
+
59
+ it('calls close() and restores body scroll when open is toggled to false', async () => {
60
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>') as BdsDialog;
61
+ expect(document.body.style.overflow).toBe('hidden');
62
+ el.open = false;
63
+ await el.updateComplete;
64
+ expect(HTMLDialogElement.prototype.close).toHaveBeenCalled();
65
+ expect(document.body.style.overflow).toBe('');
66
+ cleanup(el);
67
+ });
68
+
69
+ it('fires bds-open when opened programmatically', async () => {
70
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>') as BdsDialog;
71
+ const handler = vi.fn();
72
+ el.addEventListener('bds-open', handler);
73
+ el.dispatchEvent(makeCommandEvent('show-modal'));
74
+ expect(handler).toHaveBeenCalledOnce();
75
+ cleanup(el);
76
+ });
77
+
78
+ it('fires bds-close when closed programmatically', async () => {
79
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>') as BdsDialog;
80
+ const handler = vi.fn();
81
+ el.addEventListener('bds-close', handler);
82
+ el.dispatchEvent(makeCommandEvent('close'));
83
+ expect(handler).toHaveBeenCalledOnce();
84
+ cleanup(el);
85
+ });
86
+
87
+ it('responds to show-modal command event', async () => {
88
+ const el = await fixture('<bds-dialog><p>Content</p></bds-dialog>') as BdsDialog;
89
+ expect(el.open).toBeFalsy();
90
+ el.dispatchEvent(makeCommandEvent('show-modal'));
91
+ await el.updateComplete;
92
+ expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalled();
93
+ cleanup(el);
94
+ });
95
+
96
+ it('responds to close command event', async () => {
97
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>') as BdsDialog;
98
+ el.dispatchEvent(makeCommandEvent('close'));
99
+ await el.updateComplete;
100
+ expect(HTMLDialogElement.prototype.close).toHaveBeenCalled();
101
+ cleanup(el);
102
+ });
103
+
104
+ it('close button click fires bds-close', async () => {
105
+ const el = await fixture('<bds-dialog open><p>Content</p></bds-dialog>');
106
+ const handler = vi.fn();
107
+ el.addEventListener('bds-close', handler);
108
+ const btn = el.shadowRoot!.querySelector<HTMLButtonElement>('button.close-button')!;
109
+ btn.click();
110
+ expect(handler).toHaveBeenCalledOnce();
111
+ cleanup(el);
112
+ });
113
+
114
+ it('renders slotted content', async () => {
115
+ const el = await fixture('<bds-dialog><p>Hello world</p></bds-dialog>');
116
+ expect(el.querySelector('p')!.textContent).toBe('Hello world');
117
+ cleanup(el);
118
+ });
119
+ });
@@ -0,0 +1,221 @@
1
+ import { LitElement, css, html } from 'lit';
2
+
3
+ const FOCUSABLE = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
4
+
5
+ /**
6
+ * `<bds-dialog>` — framework-agnostic Dialog custom element.
7
+ *
8
+ * Wraps a native `<dialog>` element with `showModal()` / `close()` lifecycle,
9
+ * backdrop-click to dismiss, Escape key handling, Tab focus trapping, and
10
+ * the Invoker Commands API (`command="show-modal"` / `command="close"`).
11
+ *
12
+ * Attributes:
13
+ * open — boolean; controlled open state (syncs to native showModal/close)
14
+ *
15
+ * Slots:
16
+ * (default) — dialog content
17
+ *
18
+ * Events:
19
+ * bds-open — fired (bubbles, composed) when the dialog opens
20
+ * bds-close — fired (bubbles, composed) when the dialog closes
21
+ *
22
+ * @example
23
+ * <bds-dialog id="my-dialog">
24
+ * <p>Dialog content</p>
25
+ * </bds-dialog>
26
+ * <button commandfor="my-dialog" command="show-modal">Open</button>
27
+ */
28
+ export class BdsDialog extends LitElement {
29
+ static styles = css`
30
+ :host {
31
+ display: contents;
32
+ }
33
+
34
+ .dialog {
35
+ font-size: var(--bds-font_size--body--s);
36
+ border: var(--dialog_border, none);
37
+ --bdc_color: currentcolor;
38
+ --bdc-outline_radius: var(--bds-border_radius--m);
39
+
40
+ border-radius: var(--dialog_radius, var(--bdc-outline_radius));
41
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color), var(--dialog_shadow, var(--bdc-outline_shadow));
42
+ background-color: var(--dialog_color, var(--bds-color_grey--subtle));
43
+ color: var(--dialog_on-color, var(--bds-color_on-grey--subtle));
44
+ padding: var(--bds-space_l);
45
+ max-inline-size: 90svw;
46
+ max-block-size: 90svh;
47
+ overflow: visible;
48
+ position: relative;
49
+ }
50
+
51
+ .dialog-content {
52
+ display: grid;
53
+ gap: var(--bds-grid_gap);
54
+ overflow-block: auto;
55
+ min-block-size: 0;
56
+ flex: 1;
57
+ }
58
+
59
+ .close-button {
60
+ all: unset;
61
+ position: absolute;
62
+ inset-block-start: -1em;
63
+ inset-inline-end: -1em;
64
+ inline-size: 2em;
65
+ block-size: 2em;
66
+ display: flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ border-radius: 50%;
70
+ background-color: var(--bds-color_bg);
71
+ color: currentcolor;
72
+ border: var(--dialog_close-border, none);
73
+ --bdc_color: currentcolor;
74
+
75
+ box-shadow: inset 0 0 0 var(--bdc-outline_width) var(--bdc_color);
76
+ cursor: pointer;
77
+ }
78
+
79
+ .close-button svg {
80
+ inline-size: 1.25em;
81
+ block-size: 1.25em;
82
+ }
83
+
84
+ .dialog::backdrop {
85
+ background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
86
+ backdrop-filter: blur(3px);
87
+ }
88
+ `;
89
+
90
+ static properties = {
91
+ open: { type: Boolean, reflect: true },
92
+ };
93
+
94
+ declare open: boolean;
95
+
96
+ private _triggerEl: Element | null = null;
97
+
98
+ constructor() {
99
+ super();
100
+ this.open = false;
101
+ }
102
+
103
+ connectedCallback() {
104
+ super.connectedCallback();
105
+ this.addEventListener('command', this._onCommand);
106
+ }
107
+
108
+ disconnectedCallback() {
109
+ super.disconnectedCallback();
110
+ this.removeEventListener('command', this._onCommand);
111
+ }
112
+
113
+ updated(changed: Map<string, unknown>) {
114
+ super.updated(changed);
115
+ if (changed.has('open')) {
116
+ const dialog = this._dialog;
117
+ if (!dialog) return;
118
+ if (this.open && !dialog.open) {
119
+ this._triggerEl = document.activeElement;
120
+ dialog.showModal();
121
+ document.body.style.overflow = 'hidden';
122
+ } else if (!this.open && dialog.open) {
123
+ dialog.close();
124
+ document.body.style.overflow = '';
125
+ (this._triggerEl as HTMLElement | null)?.focus();
126
+ this._triggerEl = null;
127
+ }
128
+ }
129
+ }
130
+
131
+ private get _dialog(): HTMLDialogElement | null {
132
+ return this.shadowRoot?.querySelector('dialog') ?? null;
133
+ }
134
+
135
+ private _onCommand = (e: Event) => {
136
+ const command = (e as CommandEvent).command;
137
+ if (command === 'show-modal') this._openDialog();
138
+ else if (command === 'close') this._closeDialog();
139
+ };
140
+
141
+ private _openDialog() {
142
+ if (this.open) return;
143
+ this._triggerEl = document.activeElement;
144
+ this.open = true;
145
+ this.dispatchEvent(new CustomEvent('bds-open', { bubbles: true, composed: true }));
146
+ }
147
+
148
+ private _closeDialog() {
149
+ if (!this.open) return;
150
+ this.open = false;
151
+ this.dispatchEvent(new CustomEvent('bds-close', { bubbles: true, composed: true }));
152
+ }
153
+
154
+ private _handleBackdropClick(e: MouseEvent) {
155
+ if (e.target === this._dialog) this._closeDialog();
156
+ }
157
+
158
+ private _handleCancel(e: Event) {
159
+ e.preventDefault();
160
+ this._closeDialog();
161
+ }
162
+
163
+ private _handleKeydown(e: KeyboardEvent) {
164
+ if (e.key !== 'Tab') return;
165
+ const dialog = this._dialog;
166
+ if (!dialog) return;
167
+ const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE));
168
+ // Also include slotted elements from light DOM via the slot
169
+ const slot = dialog.querySelector('slot');
170
+ const slottedFocusable = slot
171
+ ? (slot as HTMLSlotElement).assignedElements({ flatten: true })
172
+ .flatMap(el => [
173
+ ...(el.matches(FOCUSABLE) ? [el as HTMLElement] : []),
174
+ ...Array.from(el.querySelectorAll<HTMLElement>(FOCUSABLE)),
175
+ ])
176
+ : [];
177
+ const all = [...focusable, ...slottedFocusable];
178
+ if (all.length === 0) return;
179
+ const first = all[0];
180
+ const last = all[all.length - 1];
181
+ if (e.shiftKey) {
182
+ if (document.activeElement === first) { e.preventDefault(); last.focus(); }
183
+ } else {
184
+ if (document.activeElement === last) { e.preventDefault(); first.focus(); }
185
+ }
186
+ }
187
+
188
+ render() {
189
+ return html`
190
+ <dialog
191
+ class="dialog"
192
+ aria-modal="true"
193
+ @click=${this._handleBackdropClick}
194
+ @cancel=${this._handleCancel}
195
+ @keydown=${this._handleKeydown}
196
+ >
197
+ <button
198
+ type="button"
199
+ class="close-button"
200
+ aria-label="Close dialog"
201
+ @click=${this._closeDialog}
202
+ >
203
+ <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
204
+ <path stroke-linecap="round" stroke-linejoin="round" d="M18 6L6 18M6 6l12 12" />
205
+ </svg>
206
+ </button>
207
+ <div class="dialog-content">
208
+ <slot></slot>
209
+ </div>
210
+ </dialog>
211
+ `;
212
+ }
213
+ }
214
+
215
+ customElements.define('bds-dialog', BdsDialog);
216
+
217
+ declare global {
218
+ interface HTMLElementTagNameMap {
219
+ 'bds-dialog': BdsDialog;
220
+ }
221
+ }
@@ -0,0 +1,113 @@
1
+ import { fixture, cleanup, makeCommandEvent } from '../test/helpers';
2
+ import { BdsDrawer } from './bds-drawer';
3
+
4
+ beforeEach(() => {
5
+ HTMLDialogElement.prototype.showModal = vi.fn(function (this: HTMLDialogElement) {
6
+ this.setAttribute('open', '');
7
+ });
8
+ HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
9
+ this.removeAttribute('open');
10
+ });
11
+ document.body.style.overflow = '';
12
+ });
13
+
14
+ describe('bds-drawer', () => {
15
+ it('is registered as a custom element', () => {
16
+ expect(customElements.get('bds-drawer')).toBe(BdsDrawer);
17
+ });
18
+
19
+ it('renders a <dialog> element in shadow DOM', async () => {
20
+ const el = await fixture('<bds-drawer><p>Content</p></bds-drawer>');
21
+ expect(el.shadowRoot!.querySelector('dialog')).not.toBeNull();
22
+ cleanup(el);
23
+ });
24
+
25
+ it('dialog has aria-modal="true"', async () => {
26
+ const el = await fixture('<bds-drawer><p>Content</p></bds-drawer>');
27
+ const dialog = el.shadowRoot!.querySelector('dialog')!;
28
+ expect(dialog.getAttribute('aria-modal')).toBe('true');
29
+ cleanup(el);
30
+ });
31
+
32
+ it('defaults to side="right"', async () => {
33
+ const el = await fixture('<bds-drawer><p>Content</p></bds-drawer>') as BdsDrawer;
34
+ expect(el.side).toBe('right');
35
+ cleanup(el);
36
+ });
37
+
38
+ it('reflects side attribute', async () => {
39
+ const el = await fixture('<bds-drawer side="left"><p>Content</p></bds-drawer>') as BdsDrawer;
40
+ await el.updateComplete;
41
+ expect(el.getAttribute('side')).toBe('left');
42
+ cleanup(el);
43
+ });
44
+
45
+ it('renders a close button with aria-label="Close drawer"', async () => {
46
+ const el = await fixture('<bds-drawer><p>Content</p></bds-drawer>');
47
+ const btn = el.shadowRoot!.querySelector('button.close-button');
48
+ expect(btn).not.toBeNull();
49
+ expect(btn!.getAttribute('aria-label')).toBe('Close drawer');
50
+ cleanup(el);
51
+ });
52
+
53
+ it('calls showModal when open attribute is set', async () => {
54
+ const el = await fixture('<bds-drawer open><p>Content</p></bds-drawer>');
55
+ expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalled();
56
+ cleanup(el);
57
+ });
58
+
59
+ it('sets body overflow to hidden when open', async () => {
60
+ const el = await fixture('<bds-drawer open><p>Content</p></bds-drawer>');
61
+ expect(document.body.style.overflow).toBe('hidden');
62
+ cleanup(el);
63
+ });
64
+
65
+ it('calls close() and restores body scroll when open is set to false', async () => {
66
+ const el = await fixture('<bds-drawer open><p>Content</p></bds-drawer>') as BdsDrawer;
67
+ el.open = false;
68
+ await el.updateComplete;
69
+ expect(HTMLDialogElement.prototype.close).toHaveBeenCalled();
70
+ expect(document.body.style.overflow).toBe('');
71
+ cleanup(el);
72
+ });
73
+
74
+ it('fires bds-open on show-modal command', async () => {
75
+ const el = await fixture('<bds-drawer><p>Content</p></bds-drawer>');
76
+ const handler = vi.fn();
77
+ el.addEventListener('bds-open', handler);
78
+ el.dispatchEvent(makeCommandEvent('show-modal'));
79
+ expect(handler).toHaveBeenCalledOnce();
80
+ cleanup(el);
81
+ });
82
+
83
+ it('fires bds-close on close command', async () => {
84
+ const el = await fixture('<bds-drawer open><p>Content</p></bds-drawer>');
85
+ const handler = vi.fn();
86
+ el.addEventListener('bds-close', handler);
87
+ el.dispatchEvent(makeCommandEvent('close'));
88
+ expect(handler).toHaveBeenCalledOnce();
89
+ cleanup(el);
90
+ });
91
+
92
+ it('close button click fires bds-close', async () => {
93
+ const el = await fixture('<bds-drawer open><p>Content</p></bds-drawer>');
94
+ const handler = vi.fn();
95
+ el.addEventListener('bds-close', handler);
96
+ const btn = el.shadowRoot!.querySelector<HTMLButtonElement>('button.close-button')!;
97
+ btn.click();
98
+ expect(handler).toHaveBeenCalledOnce();
99
+ cleanup(el);
100
+ });
101
+
102
+ it('renders slotted title content', async () => {
103
+ const el = await fixture('<bds-drawer><span slot="title">Settings</span></bds-drawer>');
104
+ expect(el.querySelector('[slot="title"]')!.textContent).toBe('Settings');
105
+ cleanup(el);
106
+ });
107
+
108
+ it('renders slotted body content', async () => {
109
+ const el = await fixture('<bds-drawer><p>Body content</p></bds-drawer>');
110
+ expect(el.querySelector('p')!.textContent).toBe('Body content');
111
+ cleanup(el);
112
+ });
113
+ });