@boostdev/design-system-components 1.1.0 → 1.1.2

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 (269) hide show
  1. package/AGENTS.md +46 -5
  2. package/dist/client.cjs +206 -113
  3. package/dist/client.css +664 -624
  4. package/dist/client.d.cts +9 -3
  5. package/dist/client.d.ts +9 -3
  6. package/dist/client.js +241 -149
  7. package/dist/index.cjs +206 -113
  8. package/dist/index.css +664 -624
  9. package/dist/index.d.cts +9 -3
  10. package/dist/index.d.ts +9 -3
  11. package/dist/index.js +241 -149
  12. package/dist/web-components/chunk-2QA62WCQ.js +48 -0
  13. package/dist/web-components/chunk-4BX6AWXX.js +146 -0
  14. package/dist/web-components/chunk-57TRNCWQ.js +93 -0
  15. package/dist/web-components/chunk-5IPHEONG.js +139 -0
  16. package/dist/web-components/chunk-5R6IFELV.js +63 -0
  17. package/dist/web-components/chunk-5TFWOPLH.js +49 -0
  18. package/dist/web-components/chunk-6I3CFC26.js +89 -0
  19. package/dist/web-components/chunk-6T3RKUKG.js +65 -0
  20. package/dist/web-components/chunk-7FMR53BW.js +127 -0
  21. package/dist/web-components/chunk-A67LCT7C.js +134 -0
  22. package/dist/web-components/chunk-AGKJTMJI.js +56 -0
  23. package/dist/web-components/chunk-BQLZ7I4G.js +56 -0
  24. package/dist/web-components/chunk-CAPI25CU.js +53 -0
  25. package/dist/web-components/chunk-CJ5ZOO42.js +229 -0
  26. package/dist/web-components/chunk-CR5PGUHV.js +109 -0
  27. package/dist/web-components/chunk-ECW3HHWA.js +185 -0
  28. package/dist/web-components/chunk-HGBNDR22.js +98 -0
  29. package/dist/web-components/chunk-IK3PTFZ4.js +133 -0
  30. package/dist/web-components/chunk-IQGLIQSO.js +188 -0
  31. package/dist/web-components/chunk-ITHLKR7Q.js +105 -0
  32. package/dist/web-components/chunk-K5CE6HSB.js +169 -0
  33. package/dist/web-components/chunk-L3XHOMN6.js +111 -0
  34. package/dist/{web-components.js → web-components/chunk-LGEZYFUU.js} +62 -834
  35. package/dist/web-components/chunk-LSC4N56Q.js +168 -0
  36. package/dist/web-components/chunk-LY62SAVN.js +64 -0
  37. package/dist/web-components/chunk-O4UFI2RX.js +244 -0
  38. package/dist/web-components/chunk-O7I63SQX.js +124 -0
  39. package/dist/web-components/chunk-OHUTLSIQ.js +69 -0
  40. package/dist/web-components/chunk-OQTRTMAB.js +27 -0
  41. package/dist/web-components/chunk-OTX4COSV.js +187 -0
  42. package/dist/web-components/chunk-PY62FTRW.js +179 -0
  43. package/dist/web-components/chunk-Q4T5BJT6.js +94 -0
  44. package/dist/web-components/chunk-QDRH46TF.js +281 -0
  45. package/dist/web-components/chunk-QDST57JC.js +119 -0
  46. package/dist/web-components/chunk-QPOKHLMQ.js +45 -0
  47. package/dist/web-components/chunk-SOQQ7RVJ.js +88 -0
  48. package/dist/web-components/chunk-UFFCKVZE.js +149 -0
  49. package/dist/web-components/chunk-VM3YHGQ2.js +152 -0
  50. package/dist/web-components/chunk-VMMVWLXL.js +53 -0
  51. package/dist/web-components/chunk-VSS5ITNE.js +109 -0
  52. package/dist/web-components/chunk-XIENXR7H.js +118 -0
  53. package/dist/web-components/chunk-Y4MAXTMA.js +80 -0
  54. package/dist/web-components/chunk-YLVTEY23.js +165 -0
  55. package/dist/web-components/globals.d.ts +2 -0
  56. package/dist/web-components/globals.js +1 -0
  57. package/dist/web-components/index.d.ts +41 -0
  58. package/dist/web-components/index.js +165 -0
  59. package/dist/web-components/interaction/bds-accordion-item.d.ts +54 -0
  60. package/dist/web-components/interaction/bds-accordion-item.js +7 -0
  61. package/dist/web-components/interaction/bds-accordion.d.ts +50 -0
  62. package/dist/web-components/interaction/bds-accordion.js +7 -0
  63. package/dist/web-components/interaction/bds-button.d.ts +100 -0
  64. package/dist/web-components/interaction/bds-button.js +7 -0
  65. package/dist/web-components/interaction/bds-collapsible.d.ts +52 -0
  66. package/dist/web-components/interaction/bds-collapsible.js +7 -0
  67. package/dist/web-components/interaction/bds-dialog.d.ts +56 -0
  68. package/dist/web-components/interaction/bds-dialog.js +7 -0
  69. package/dist/web-components/interaction/bds-drawer.d.ts +64 -0
  70. package/dist/web-components/interaction/bds-drawer.js +7 -0
  71. package/dist/web-components/interaction/bds-popover.d.ts +62 -0
  72. package/dist/web-components/interaction/bds-popover.js +7 -0
  73. package/dist/web-components/interaction/bds-skip-link.d.ts +36 -0
  74. package/dist/web-components/interaction/bds-skip-link.js +7 -0
  75. package/dist/web-components/interaction/bds-tab-panel.d.ts +26 -0
  76. package/dist/web-components/interaction/bds-tab-panel.js +7 -0
  77. package/dist/web-components/interaction/bds-tab.d.ts +45 -0
  78. package/dist/web-components/interaction/bds-tab.js +7 -0
  79. package/dist/web-components/interaction/bds-tabs.d.ts +46 -0
  80. package/dist/web-components/interaction/bds-tabs.js +7 -0
  81. package/dist/web-components/interaction/bds-toast-provider.d.ts +57 -0
  82. package/dist/web-components/interaction/bds-toast-provider.js +7 -0
  83. package/dist/web-components/interaction/bds-tooltip.d.ts +50 -0
  84. package/dist/web-components/interaction/bds-tooltip.js +7 -0
  85. package/dist/web-components/interaction/form/bds-checkbox.d.ts +70 -0
  86. package/dist/web-components/interaction/form/bds-checkbox.js +7 -0
  87. package/dist/web-components/interaction/form/bds-combobox.d.ts +84 -0
  88. package/dist/web-components/interaction/form/bds-combobox.js +7 -0
  89. package/dist/web-components/interaction/form/bds-file-input.d.ts +62 -0
  90. package/dist/web-components/interaction/form/bds-file-input.js +7 -0
  91. package/dist/web-components/interaction/form/bds-number-input.d.ts +68 -0
  92. package/dist/web-components/interaction/form/bds-number-input.js +7 -0
  93. package/dist/web-components/interaction/form/bds-radio.d.ts +68 -0
  94. package/dist/web-components/interaction/form/bds-radio.js +7 -0
  95. package/dist/web-components/interaction/form/bds-segmented-control.d.ts +71 -0
  96. package/dist/web-components/interaction/form/bds-segmented-control.js +7 -0
  97. package/dist/web-components/interaction/form/bds-select.d.ts +79 -0
  98. package/dist/web-components/interaction/form/bds-select.js +7 -0
  99. package/dist/web-components/interaction/form/bds-slider.d.ts +70 -0
  100. package/dist/web-components/interaction/form/bds-slider.js +7 -0
  101. package/dist/web-components/interaction/form/bds-switch.d.ts +67 -0
  102. package/dist/web-components/interaction/form/bds-switch.js +7 -0
  103. package/dist/web-components/interaction/form/bds-textarea.d.ts +70 -0
  104. package/dist/web-components/interaction/form/bds-textarea.js +7 -0
  105. package/dist/web-components/ui/bds-alert.d.ts +66 -0
  106. package/dist/web-components/ui/bds-alert.js +7 -0
  107. package/dist/web-components/ui/bds-avatar.d.ts +50 -0
  108. package/dist/web-components/ui/bds-avatar.js +7 -0
  109. package/dist/web-components/ui/bds-badge.d.ts +35 -0
  110. package/dist/web-components/ui/bds-badge.js +7 -0
  111. package/dist/web-components/ui/bds-breadcrumb.d.ts +35 -0
  112. package/dist/web-components/ui/bds-breadcrumb.js +7 -0
  113. package/dist/web-components/ui/bds-card.d.ts +72 -0
  114. package/dist/web-components/ui/bds-card.js +7 -0
  115. package/dist/web-components/ui/bds-description-list.d.ts +42 -0
  116. package/dist/web-components/ui/bds-description-list.js +7 -0
  117. package/dist/web-components/ui/bds-icon-wrapper.d.ts +39 -0
  118. package/dist/web-components/ui/bds-icon-wrapper.js +7 -0
  119. package/dist/web-components/ui/bds-link.d.ts +59 -0
  120. package/dist/web-components/ui/bds-link.js +7 -0
  121. package/dist/web-components/ui/bds-loading.d.ts +32 -0
  122. package/dist/web-components/ui/bds-loading.js +7 -0
  123. package/dist/web-components/ui/bds-notification-banner.d.ts +61 -0
  124. package/dist/web-components/ui/bds-notification-banner.js +7 -0
  125. package/dist/web-components/ui/bds-progress-circle.d.ts +53 -0
  126. package/dist/web-components/ui/bds-progress-circle.js +7 -0
  127. package/dist/web-components/ui/bds-progress.d.ts +54 -0
  128. package/dist/web-components/ui/bds-progress.js +7 -0
  129. package/dist/web-components/ui/bds-rating.d.ts +36 -0
  130. package/dist/web-components/ui/bds-rating.js +7 -0
  131. package/dist/web-components/ui/bds-section-header.d.ts +53 -0
  132. package/dist/web-components/ui/bds-section-header.js +8 -0
  133. package/dist/web-components/ui/bds-separator.d.ts +32 -0
  134. package/dist/web-components/ui/bds-separator.js +7 -0
  135. package/dist/web-components/ui/bds-skeleton.d.ts +22 -0
  136. package/dist/web-components/ui/bds-skeleton.js +7 -0
  137. package/dist/web-components/ui/bds-typography.d.ts +44 -0
  138. package/dist/web-components/ui/bds-typography.js +8 -0
  139. package/package.json +169 -4
  140. package/src/components/interaction/Button/Button.module.css +1 -1
  141. package/src/components/interaction/Button/Button.native.mdx +72 -0
  142. package/src/components/interaction/Command/Command.module.css +7 -7
  143. package/src/components/interaction/Command/Command.spec.tsx +37 -0
  144. package/src/components/interaction/Command/Command.tsx +37 -2
  145. package/src/components/interaction/Dialog/Dialog.module.css +10 -13
  146. package/src/components/interaction/Dialog/Dialog.spec.tsx +53 -1
  147. package/src/components/interaction/Dialog/Dialog.stories.tsx +17 -0
  148. package/src/components/interaction/Dialog/Dialog.tsx +50 -16
  149. package/src/components/interaction/Drawer/Drawer.module.css +11 -11
  150. package/src/components/interaction/Drawer/Drawer.spec.tsx +48 -1
  151. package/src/components/interaction/Drawer/Drawer.tsx +38 -4
  152. package/src/components/interaction/DropdownMenu/DropdownMenu.module.css +6 -6
  153. package/src/components/interaction/Popover/Popover.module.css +74 -15
  154. package/src/components/interaction/Popover/Popover.spec.tsx +94 -59
  155. package/src/components/interaction/Popover/Popover.tsx +44 -60
  156. package/src/components/interaction/Toast/Toast.module.css +7 -7
  157. package/src/components/interaction/form/Checkbox/Checkbox.module.css +6 -6
  158. package/src/components/interaction/form/Checkbox/Checkbox.native.mdx +74 -0
  159. package/src/components/interaction/form/Combobox/Combobox.module.css +7 -8
  160. package/src/components/interaction/form/FileInput/FileInput.module.css +4 -4
  161. package/src/components/interaction/form/NumberInput/NumberInput.module.css +1 -1
  162. package/src/components/interaction/form/Radio/Radio.module.css +6 -6
  163. package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +5 -6
  164. package/src/components/interaction/form/Select/Select.module.css +3 -3
  165. package/src/components/interaction/form/Slider/Slider.module.css +6 -6
  166. package/src/components/interaction/form/Switch/Switch.module.css +8 -8
  167. package/src/components/interaction/form/Textarea/Textarea.module.css +1 -1
  168. package/src/components/layout/IconWrapper/IconWrapper.module.css +2 -2
  169. package/src/components/ui/Accordion/Accordion.module.css +5 -5
  170. package/src/components/ui/Alert/Alert.native.mdx +62 -0
  171. package/src/components/ui/Avatar/Avatar.native.mdx +50 -0
  172. package/src/components/ui/Badge/Badge.native.mdx +54 -0
  173. package/src/components/ui/Calendar/Calendar.module.css +7 -7
  174. package/src/components/ui/Carousel/Carousel.module.css +5 -5
  175. package/src/components/ui/Collapsible/Collapsible.module.css +4 -4
  176. package/src/components/ui/Link/Link.module.css +2 -2
  177. package/src/components/ui/Loading/Loading.module.css +7 -7
  178. package/src/components/ui/NotificationBanner/NotificationBanner.module.css +3 -3
  179. package/src/components/ui/Pagination/Pagination.module.css +6 -6
  180. package/src/components/ui/Progress/Progress.module.css +5 -5
  181. package/src/components/ui/Separator/Separator.module.css +3 -3
  182. package/src/components/ui/Skeleton/Skeleton.module.css +1 -1
  183. package/src/components/ui/Table/Table.module.css +9 -9
  184. package/src/components/ui/Tabs/Tabs.module.css +3 -3
  185. package/src/components/ui/Tooltip/Tooltip.module.css +8 -8
  186. package/src/components/ui/Typography/Typography.native.mdx +56 -0
  187. package/src/polyfill-invoker-commands.ts +68 -0
  188. package/src/react-augment.d.ts +19 -0
  189. package/src/stories/ReactNative.mdx +121 -0
  190. package/src/web-components/index.ts +47 -0
  191. package/src/web-components/interaction/BdsButton.mdx +106 -0
  192. package/src/web-components/interaction/BdsPopover.mdx +120 -0
  193. package/src/web-components/interaction/BdsToastProvider.mdx +94 -0
  194. package/src/web-components/interaction/bds-accordion-item.spec.ts +75 -0
  195. package/src/web-components/interaction/bds-accordion-item.ts +181 -0
  196. package/src/web-components/interaction/bds-accordion.spec.ts +73 -0
  197. package/src/web-components/interaction/bds-accordion.ts +99 -0
  198. package/src/web-components/interaction/bds-collapsible.spec.ts +56 -0
  199. package/src/web-components/interaction/bds-collapsible.ts +170 -0
  200. package/src/web-components/interaction/bds-dialog.spec.ts +119 -0
  201. package/src/web-components/interaction/bds-dialog.ts +224 -0
  202. package/src/web-components/interaction/bds-drawer.spec.ts +113 -0
  203. package/src/web-components/interaction/bds-drawer.ts +266 -0
  204. package/src/web-components/interaction/bds-popover.spec.ts +49 -1
  205. package/src/web-components/interaction/bds-popover.ts +12 -0
  206. package/src/web-components/interaction/bds-skip-link.spec.ts +42 -0
  207. package/src/web-components/interaction/bds-skip-link.ts +74 -0
  208. package/src/web-components/interaction/bds-tab-panel.spec.ts +32 -0
  209. package/src/web-components/interaction/bds-tab-panel.ts +38 -0
  210. package/src/web-components/interaction/bds-tab.spec.ts +71 -0
  211. package/src/web-components/interaction/bds-tab.ts +110 -0
  212. package/src/web-components/interaction/bds-tabs.spec.ts +120 -0
  213. package/src/web-components/interaction/bds-tabs.ts +152 -0
  214. package/src/web-components/interaction/bds-tooltip.spec.ts +49 -0
  215. package/src/web-components/interaction/bds-tooltip.ts +127 -0
  216. package/src/web-components/interaction/form/bds-checkbox.spec.ts +63 -0
  217. package/src/web-components/interaction/form/bds-checkbox.ts +168 -0
  218. package/src/web-components/interaction/form/bds-combobox.spec.ts +117 -0
  219. package/src/web-components/interaction/form/bds-combobox.ts +336 -0
  220. package/src/web-components/interaction/form/bds-file-input.spec.ts +58 -0
  221. package/src/web-components/interaction/form/bds-file-input.ts +209 -0
  222. package/src/web-components/interaction/form/bds-number-input.spec.ts +87 -0
  223. package/src/web-components/interaction/form/bds-number-input.ts +221 -0
  224. package/src/web-components/interaction/form/bds-radio.spec.ts +70 -0
  225. package/src/web-components/interaction/form/bds-radio.ts +157 -0
  226. package/src/web-components/interaction/form/bds-segmented-control.spec.ts +74 -0
  227. package/src/web-components/interaction/form/bds-segmented-control.ts +211 -0
  228. package/src/web-components/interaction/form/bds-select.spec.ts +74 -0
  229. package/src/web-components/interaction/form/bds-select.ts +190 -0
  230. package/src/web-components/interaction/form/bds-slider.spec.ts +41 -0
  231. package/src/web-components/interaction/form/bds-slider.ts +162 -0
  232. package/src/web-components/interaction/form/bds-switch.spec.ts +62 -0
  233. package/src/web-components/interaction/form/bds-switch.ts +166 -0
  234. package/src/web-components/interaction/form/bds-textarea.spec.ts +55 -0
  235. package/src/web-components/interaction/form/bds-textarea.ts +143 -0
  236. package/src/web-components/test/helpers.ts +8 -0
  237. package/src/web-components/ui/BdsAlert.mdx +90 -0
  238. package/src/web-components/ui/BdsBadge.mdx +74 -0
  239. package/src/web-components/ui/bds-avatar.spec.ts +75 -0
  240. package/src/web-components/ui/bds-avatar.ts +122 -0
  241. package/src/web-components/ui/bds-breadcrumb.spec.ts +55 -0
  242. package/src/web-components/ui/bds-breadcrumb.ts +114 -0
  243. package/src/web-components/ui/bds-card.spec.ts +70 -0
  244. package/src/web-components/ui/bds-card.ts +156 -0
  245. package/src/web-components/ui/bds-description-list.spec.ts +36 -0
  246. package/src/web-components/ui/bds-description-list.ts +79 -0
  247. package/src/web-components/ui/bds-icon-wrapper.spec.ts +21 -0
  248. package/src/web-components/ui/bds-icon-wrapper.ts +68 -0
  249. package/src/web-components/ui/bds-link.spec.ts +72 -0
  250. package/src/web-components/ui/bds-link.ts +138 -0
  251. package/src/web-components/ui/bds-loading.spec.ts +38 -0
  252. package/src/web-components/ui/bds-loading.ts +76 -0
  253. package/src/web-components/ui/bds-notification-banner.spec.ts +82 -0
  254. package/src/web-components/ui/bds-notification-banner.ts +185 -0
  255. package/src/web-components/ui/bds-progress-circle.spec.ts +58 -0
  256. package/src/web-components/ui/bds-progress-circle.ts +142 -0
  257. package/src/web-components/ui/bds-progress.spec.ts +66 -0
  258. package/src/web-components/ui/bds-progress.ts +127 -0
  259. package/src/web-components/ui/bds-rating.spec.ts +70 -0
  260. package/src/web-components/ui/bds-rating.ts +80 -0
  261. package/src/web-components/ui/bds-section-header.spec.ts +57 -0
  262. package/src/web-components/ui/bds-section-header.ts +96 -0
  263. package/src/web-components/ui/bds-separator.spec.ts +50 -0
  264. package/src/web-components/ui/bds-separator.ts +66 -0
  265. package/src/web-components/ui/bds-skeleton.spec.ts +22 -0
  266. package/src/web-components/ui/bds-skeleton.ts +55 -0
  267. package/src/web-components/ui/bds-typography.spec.ts +76 -0
  268. package/src/web-components/ui/bds-typography.ts +112 -0
  269. package/dist/web-components.d.ts +0 -303
@@ -0,0 +1,41 @@
1
+ import { fixture, cleanup } from '../../test/helpers';
2
+ import { BdsSlider } from './bds-slider';
3
+
4
+ describe('bds-slider', () => {
5
+ it('is registered as a custom element', () => {
6
+ expect(customElements.get('bds-slider')).toBe(BdsSlider);
7
+ });
8
+
9
+ it('renders a range input in shadow DOM', async () => {
10
+ const el = await fixture('<bds-slider name="volume"></bds-slider>');
11
+ expect(el.shadowRoot!.querySelector('input[type="range"]')).not.toBeNull();
12
+ cleanup(el);
13
+ });
14
+
15
+ it('applies --slider_fill inline style matching the value percentage', async () => {
16
+ const el = await fixture('<bds-slider name="volume" value="50" min="0" max="100"></bds-slider>');
17
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
18
+ expect(input.style.getPropertyValue('--slider_fill')).toBe('50%');
19
+ cleanup(el);
20
+ });
21
+
22
+ it('is disabled when disabled attribute is set', async () => {
23
+ const el = await fixture('<bds-slider name="volume" disabled></bds-slider>');
24
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
25
+ expect(input.disabled).toBe(true);
26
+ cleanup(el);
27
+ });
28
+
29
+ it('updates value property on input event', async () => {
30
+ const el = await fixture('<bds-slider name="volume"></bds-slider>') as BdsSlider;
31
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
32
+ input.value = '75';
33
+ input.dispatchEvent(new Event('input', { bubbles: true }));
34
+ expect(el.value).toBe(75);
35
+ cleanup(el);
36
+ });
37
+
38
+ it('is form-associated', () => {
39
+ expect(BdsSlider.formAssociated).toBe(true);
40
+ });
41
+ });
@@ -0,0 +1,162 @@
1
+ import { LitElement, css, html } from 'lit';
2
+
3
+ /**
4
+ * `<bds-slider>` — form-associated Slider / Range Input custom element.
5
+ *
6
+ * Uses a native `<input type="range">` with a CSS fill track driven by
7
+ * the `--slider_fill` custom property.
8
+ *
9
+ * Attributes:
10
+ * name — form field name
11
+ * value — current value (default: 0)
12
+ * min — minimum value (default: 0)
13
+ * max — maximum value (default: 100)
14
+ * step — step increment (default: 1)
15
+ * disabled — boolean
16
+ *
17
+ * Events:
18
+ * input — fired as the user drags
19
+ * change — fired when the user releases
20
+ *
21
+ * @example
22
+ * <bds-slider name="volume" value="50" min="0" max="100" step="5"></bds-slider>
23
+ */
24
+ export class BdsSlider extends LitElement {
25
+ static formAssociated = true;
26
+
27
+ static styles = css`
28
+ :host {
29
+ display: block;
30
+ }
31
+
32
+ :host([disabled]) {
33
+ opacity: 0.5;
34
+ pointer-events: none;
35
+ }
36
+
37
+ .track-wrapper {
38
+ position: relative;
39
+ display: flex;
40
+ align-items: center;
41
+ }
42
+
43
+ .input {
44
+ inline-size: 100%;
45
+ appearance: none;
46
+ block-size: 0.25rem;
47
+ border-radius: 999px;
48
+ background: linear-gradient(
49
+ to right,
50
+ var(--slider_fill-color, var(--bds-color_interactive)) var(--slider_fill, 0%),
51
+ var(--slider_track-color, var(--bds-color_bg--subtle)) var(--slider_fill, 0%)
52
+ );
53
+ outline: none;
54
+ cursor: pointer;
55
+ }
56
+
57
+ .input::-webkit-slider-thumb {
58
+ appearance: none;
59
+ inline-size: 1.25rem;
60
+ block-size: 1.25rem;
61
+ border-radius: 50%;
62
+ background-color: var(--slider_thumb-color, var(--bds-color_interactive));
63
+ box-shadow: var(--bds-shadow_m);
64
+ cursor: pointer;
65
+ transition: var(--bds-animation_transition);
66
+ }
67
+
68
+ .input::-moz-range-thumb {
69
+ inline-size: 1.25rem;
70
+ block-size: 1.25rem;
71
+ border-radius: 50%;
72
+ border: none;
73
+ background-color: var(--slider_thumb-color, var(--bds-color_interactive));
74
+ box-shadow: var(--bds-shadow_m);
75
+ cursor: pointer;
76
+ }
77
+
78
+ .input:focus-visible {
79
+ outline: var(--bds-outline_default);
80
+ outline-offset: var(--bds-outline_offset);
81
+ border-radius: 999px;
82
+ }
83
+ `;
84
+
85
+ static properties = {
86
+ name: { type: String, reflect: true },
87
+ value: { type: Number },
88
+ min: { type: Number },
89
+ max: { type: Number },
90
+ step: { type: Number },
91
+ disabled: { type: Boolean, reflect: true },
92
+ };
93
+
94
+ declare name: string;
95
+ declare value: number;
96
+ declare min: number;
97
+ declare max: number;
98
+ declare step: number;
99
+ declare disabled: boolean;
100
+
101
+ private _internals!: ElementInternals;
102
+
103
+ constructor() {
104
+ super();
105
+ this._internals = this.attachInternals();
106
+ this.name = '';
107
+ this.value = 0;
108
+ this.min = 0;
109
+ this.max = 100;
110
+ this.step = 1;
111
+ this.disabled = false;
112
+ }
113
+
114
+ updated(changed: Map<string, unknown>) {
115
+ super.updated(changed);
116
+ if (changed.has('value')) {
117
+ this._internals.setFormValue?.(String(this.value));
118
+ }
119
+ }
120
+
121
+ formResetCallback() {
122
+ this.value = 0;
123
+ this._internals.setFormValue?.('0');
124
+ }
125
+
126
+ private get _fillPct(): number {
127
+ const range = this.max - this.min;
128
+ if (range === 0) return 0;
129
+ return ((this.value - this.min) / range) * 100;
130
+ }
131
+
132
+ private _handleInput(e: Event) {
133
+ this.value = parseFloat((e.target as HTMLInputElement).value);
134
+ }
135
+
136
+ render() {
137
+ return html`
138
+ <div class="track-wrapper">
139
+ <input
140
+ type="range"
141
+ class="input"
142
+ name=${this.name}
143
+ .value=${String(this.value)}
144
+ min=${this.min}
145
+ max=${this.max}
146
+ step=${this.step}
147
+ ?disabled=${this.disabled}
148
+ style="--slider_fill: ${this._fillPct}%"
149
+ @input=${this._handleInput}
150
+ />
151
+ </div>
152
+ `;
153
+ }
154
+ }
155
+
156
+ customElements.define('bds-slider', BdsSlider);
157
+
158
+ declare global {
159
+ interface HTMLElementTagNameMap {
160
+ 'bds-slider': BdsSlider;
161
+ }
162
+ }
@@ -0,0 +1,62 @@
1
+ import { fixture, cleanup } from '../../test/helpers';
2
+ import { BdsSwitch } from './bds-switch';
3
+
4
+ describe('bds-switch', () => {
5
+ it('is registered as a custom element', () => {
6
+ expect(customElements.get('bds-switch')).toBe(BdsSwitch);
7
+ });
8
+
9
+ it('renders a checkbox input with role="switch"', async () => {
10
+ const el = await fixture('<bds-switch name="notify">Notifications</bds-switch>');
11
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
12
+ expect(input.type).toBe('checkbox');
13
+ expect(input.getAttribute('role')).toBe('switch');
14
+ cleanup(el);
15
+ });
16
+
17
+ it('is off by default', async () => {
18
+ const el = await fixture('<bds-switch name="notify">Notifications</bds-switch>');
19
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
20
+ expect(input.checked).toBe(false);
21
+ cleanup(el);
22
+ });
23
+
24
+ it('is on when checked attribute is set', async () => {
25
+ const el = await fixture('<bds-switch name="notify" checked>Notifications</bds-switch>');
26
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
27
+ expect(input.checked).toBe(true);
28
+ cleanup(el);
29
+ });
30
+
31
+ it('input is disabled when disabled attribute is set', async () => {
32
+ const el = await fixture('<bds-switch name="notify" disabled>Notifications</bds-switch>');
33
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
34
+ expect(input.disabled).toBe(true);
35
+ cleanup(el);
36
+ });
37
+
38
+ it('renders a visual track element', async () => {
39
+ const el = await fixture('<bds-switch name="notify">Notifications</bds-switch>');
40
+ expect(el.shadowRoot!.querySelector('.track')).not.toBeNull();
41
+ cleanup(el);
42
+ });
43
+
44
+ it('renders slotted label text', async () => {
45
+ const el = await fixture('<bds-switch name="notify">Turn on alerts</bds-switch>');
46
+ expect(el.textContent?.trim()).toBe('Turn on alerts');
47
+ cleanup(el);
48
+ });
49
+
50
+ it('updates checked property on change', async () => {
51
+ const el = await fixture('<bds-switch name="notify">Notifications</bds-switch>') as BdsSwitch;
52
+ const input = el.shadowRoot!.querySelector<HTMLInputElement>('input')!;
53
+ input.checked = true;
54
+ input.dispatchEvent(new Event('change', { bubbles: true }));
55
+ expect(el.checked).toBe(true);
56
+ cleanup(el);
57
+ });
58
+
59
+ it('is form-associated', () => {
60
+ expect(BdsSwitch.formAssociated).toBe(true);
61
+ });
62
+ });
@@ -0,0 +1,166 @@
1
+ import { LitElement, css, html } from 'lit';
2
+
3
+ /**
4
+ * `<bds-switch>` — form-associated Toggle Switch custom element.
5
+ *
6
+ * Visually a toggle switch but semantically equivalent to a checkbox
7
+ * (`role="switch"`). Participates in HTML forms via the Form-Associated
8
+ * Custom Elements API.
9
+ *
10
+ * Attributes:
11
+ * name — form field name
12
+ * value — submitted value when on (default: "on")
13
+ * checked — boolean; whether the switch is on
14
+ * required — boolean
15
+ * disabled — boolean
16
+ *
17
+ * Slots:
18
+ * (default) — label text
19
+ *
20
+ * @example
21
+ * <bds-switch name="notifications" value="enabled">Enable notifications</bds-switch>
22
+ */
23
+ export class BdsSwitch extends LitElement {
24
+ static formAssociated = true;
25
+
26
+ static styles = css`
27
+ :host {
28
+ display: inline-flex;
29
+ align-items: center;
30
+ gap: var(--bds-space_xs);
31
+ cursor: pointer;
32
+ font-size: var(--bds-font_size--body);
33
+ font-family: var(--bds-font_family--body);
34
+ color: var(--bds-color_on-bg);
35
+ }
36
+
37
+ :host([disabled]) {
38
+ opacity: 0.5;
39
+ cursor: not-allowed;
40
+ pointer-events: none;
41
+ }
42
+
43
+ .track {
44
+ position: relative;
45
+ inline-size: 2.75rem;
46
+ block-size: 1.5rem;
47
+ border-radius: 999px;
48
+ background-color: var(--switch_color_bg, var(--bds-color_bg--subtle));
49
+ --bdc_color: currentcolor;
50
+
51
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
52
+ outline-offset: var(--bdc-outline_offset);
53
+ transition: background-color 0.2s ease;
54
+ flex-shrink: 0;
55
+ }
56
+
57
+ .thumb {
58
+ position: absolute;
59
+ inset-block-start: 0.2rem;
60
+ inset-inline-start: 0.2rem;
61
+ inline-size: 1.1rem;
62
+ block-size: 1.1rem;
63
+ border-radius: 50%;
64
+ background-color: var(--switch_thumb_color, var(--bds-color_bg));
65
+ transition: inset-inline-start 0.2s ease;
66
+ }
67
+
68
+ .input {
69
+ position: absolute;
70
+ opacity: 0;
71
+ inline-size: 0;
72
+ block-size: 0;
73
+ margin: 0;
74
+ }
75
+
76
+ .input:checked + .track {
77
+ background-color: var(--switch_color-active, var(--bdc-color_active));
78
+ }
79
+
80
+ .input:checked + .track .thumb {
81
+ inset-inline-start: calc(100% - 1.3rem);
82
+ }
83
+
84
+ .input:focus-visible + .track {
85
+ outline: var(--bds-outline_default);
86
+ outline-offset: var(--bds-outline_offset);
87
+ }
88
+
89
+ label {
90
+ cursor: pointer;
91
+ }
92
+ `;
93
+
94
+ static properties = {
95
+ name: { type: String, reflect: true },
96
+ value: { type: String },
97
+ checked: { type: Boolean, reflect: true },
98
+ required: { type: Boolean, reflect: true },
99
+ disabled: { type: Boolean, reflect: true },
100
+ };
101
+
102
+ declare name: string;
103
+ declare value: string;
104
+ declare checked: boolean;
105
+ declare required: boolean;
106
+ declare disabled: boolean;
107
+
108
+ private _internals!: ElementInternals;
109
+ private readonly _uid = Math.random().toString(36).slice(2, 8);
110
+
111
+ constructor() {
112
+ super();
113
+ this._internals = this.attachInternals();
114
+ this.name = '';
115
+ this.value = 'on';
116
+ this.checked = false;
117
+ this.required = false;
118
+ this.disabled = false;
119
+ }
120
+
121
+ updated(changed: Map<string, unknown>) {
122
+ super.updated(changed);
123
+ if (changed.has('checked') || changed.has('value')) {
124
+ this._internals.setFormValue?.(this.checked ? this.value : null);
125
+ }
126
+ }
127
+
128
+ formResetCallback() {
129
+ this.checked = false;
130
+ this._internals.setFormValue?.(null);
131
+ }
132
+
133
+ private _handleChange(e: Event) {
134
+ this.checked = (e.target as HTMLInputElement).checked;
135
+ }
136
+
137
+ render() {
138
+ const id = `bds-switch-${this._uid}`;
139
+ return html`
140
+ <input
141
+ id=${id}
142
+ type="checkbox"
143
+ role="switch"
144
+ class="input"
145
+ .checked=${this.checked}
146
+ ?required=${this.required}
147
+ ?disabled=${this.disabled}
148
+ @change=${this._handleChange}
149
+ />
150
+ <label for=${id}>
151
+ <span class="track">
152
+ <span class="thumb"></span>
153
+ </span>
154
+ <slot></slot>
155
+ </label>
156
+ `;
157
+ }
158
+ }
159
+
160
+ customElements.define('bds-switch', BdsSwitch);
161
+
162
+ declare global {
163
+ interface HTMLElementTagNameMap {
164
+ 'bds-switch': BdsSwitch;
165
+ }
166
+ }
@@ -0,0 +1,55 @@
1
+ import { fixture, cleanup } from '../../test/helpers';
2
+ import { BdsTextarea } from './bds-textarea';
3
+
4
+ describe('bds-textarea', () => {
5
+ it('is registered as a custom element', () => {
6
+ expect(customElements.get('bds-textarea')).toBe(BdsTextarea);
7
+ });
8
+
9
+ it('renders a <textarea> element in shadow DOM', async () => {
10
+ const el = await fixture('<bds-textarea name="message"></bds-textarea>');
11
+ expect(el.shadowRoot!.querySelector('textarea')).not.toBeNull();
12
+ cleanup(el);
13
+ });
14
+
15
+ it('forwards placeholder attribute to inner textarea', async () => {
16
+ const el = await fixture('<bds-textarea name="message" placeholder="Type here…"></bds-textarea>');
17
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
18
+ expect(textarea.placeholder).toBe('Type here…');
19
+ cleanup(el);
20
+ });
21
+
22
+ it('forwards rows attribute to inner textarea', async () => {
23
+ const el = await fixture('<bds-textarea name="message" rows="8"></bds-textarea>');
24
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
25
+ expect(textarea.rows).toBe(8);
26
+ cleanup(el);
27
+ });
28
+
29
+ it('is disabled when disabled attribute is set', async () => {
30
+ const el = await fixture('<bds-textarea name="message" disabled></bds-textarea>');
31
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
32
+ expect(textarea.disabled).toBe(true);
33
+ cleanup(el);
34
+ });
35
+
36
+ it('is required when required attribute is set', async () => {
37
+ const el = await fixture('<bds-textarea name="message" required></bds-textarea>');
38
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
39
+ expect(textarea.required).toBe(true);
40
+ cleanup(el);
41
+ });
42
+
43
+ it('updates value property on input', async () => {
44
+ const el = await fixture('<bds-textarea name="message"></bds-textarea>') as BdsTextarea;
45
+ const textarea = el.shadowRoot!.querySelector<HTMLTextAreaElement>('textarea')!;
46
+ textarea.value = 'Hello world';
47
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
48
+ expect(el.value).toBe('Hello world');
49
+ cleanup(el);
50
+ });
51
+
52
+ it('is form-associated', () => {
53
+ expect(BdsTextarea.formAssociated).toBe(true);
54
+ });
55
+ });
@@ -0,0 +1,143 @@
1
+ import { LitElement, css, html } from 'lit';
2
+
3
+ /**
4
+ * `<bds-textarea>` — form-associated Textarea custom element.
5
+ *
6
+ * Wraps a native `<textarea>` for consistent styling while participating
7
+ * in HTML forms via the Form-Associated Custom Elements API.
8
+ *
9
+ * Attributes:
10
+ * name — form field name
11
+ * value — text value
12
+ * placeholder — placeholder text
13
+ * rows — number of visible rows (default: 4)
14
+ * required — boolean
15
+ * disabled — boolean
16
+ *
17
+ * Events:
18
+ * input — native input event
19
+ * change — native change event
20
+ *
21
+ * @example
22
+ * <bds-textarea name="message" placeholder="Enter message…" rows="6"></bds-textarea>
23
+ */
24
+ export class BdsTextarea extends LitElement {
25
+ static formAssociated = true;
26
+
27
+ static styles = css`
28
+ :host {
29
+ display: block;
30
+ }
31
+
32
+ :host([disabled]) {
33
+ opacity: 0.5;
34
+ pointer-events: none;
35
+ }
36
+
37
+ .textarea {
38
+ inline-size: 100%;
39
+ font-family: var(--bds-font_family--body);
40
+ font-size: var(--bds-font_size--body);
41
+ line-height: var(--bds-font_line-height--body);
42
+ padding: var(--bds-space_s);
43
+ border: none;
44
+ --bdc_color: currentcolor;
45
+ --bdc-outline_radius: var(--bds-border_radius--xs);
46
+
47
+ outline: var(--bdc-outline_width) solid var(--bdc_color);
48
+ outline-offset: var(--bdc-outline_offset);
49
+ border-radius: var(--textarea_radius, var(--bdc-outline_radius));
50
+ box-shadow: var(--textarea_shadow, var(--bdc-outline_shadow));
51
+ background-color: var(--textarea_color_bg, var(--bds-color_bg));
52
+ color: var(--textarea_color, var(--bds-color_on-bg));
53
+ resize: vertical;
54
+ min-block-size: calc(var(--bds-space_m) * 5);
55
+ box-sizing: border-box;
56
+ transition: --bdc_color var(--bds-animation_transition-duration) var(--bds-animation_easing),
57
+ background-color var(--bds-animation_transition-duration) var(--bds-animation_easing);
58
+ }
59
+
60
+ .textarea:focus {
61
+ --bdc_color: var(--bdc_color--focus);
62
+ }
63
+
64
+ .textarea:focus-visible {
65
+ outline: var(--bds-outline_default);
66
+ outline-offset: var(--bds-outline_offset);
67
+ }
68
+
69
+ .textarea:disabled {
70
+ opacity: 0.5;
71
+ cursor: not-allowed;
72
+ resize: none;
73
+ }
74
+ `;
75
+
76
+ static properties = {
77
+ name: { type: String, reflect: true },
78
+ value: { type: String },
79
+ placeholder: { type: String },
80
+ rows: { type: Number },
81
+ required: { type: Boolean, reflect: true },
82
+ disabled: { type: Boolean, reflect: true },
83
+ };
84
+
85
+ declare name: string;
86
+ declare value: string;
87
+ declare placeholder: string;
88
+ declare rows: number;
89
+ declare required: boolean;
90
+ declare disabled: boolean;
91
+
92
+ private _internals!: ElementInternals;
93
+
94
+ constructor() {
95
+ super();
96
+ this._internals = this.attachInternals();
97
+ this.name = '';
98
+ this.value = '';
99
+ this.placeholder = '';
100
+ this.rows = 4;
101
+ this.required = false;
102
+ this.disabled = false;
103
+ }
104
+
105
+ updated(changed: Map<string, unknown>) {
106
+ super.updated(changed);
107
+ if (changed.has('value')) {
108
+ this._internals.setFormValue?.(this.value || null);
109
+ }
110
+ }
111
+
112
+ formResetCallback() {
113
+ this.value = '';
114
+ this._internals.setFormValue?.(null);
115
+ }
116
+
117
+ private _handleInput(e: Event) {
118
+ this.value = (e.target as HTMLTextAreaElement).value;
119
+ }
120
+
121
+ render() {
122
+ return html`
123
+ <textarea
124
+ class="textarea"
125
+ name=${this.name}
126
+ .value=${this.value}
127
+ placeholder=${this.placeholder}
128
+ rows=${this.rows}
129
+ ?required=${this.required}
130
+ ?disabled=${this.disabled}
131
+ @input=${this._handleInput}
132
+ ></textarea>
133
+ `;
134
+ }
135
+ }
136
+
137
+ customElements.define('bds-textarea', BdsTextarea);
138
+
139
+ declare global {
140
+ interface HTMLElementTagNameMap {
141
+ 'bds-textarea': BdsTextarea;
142
+ }
143
+ }
@@ -12,3 +12,11 @@ export async function fixture(html: string): Promise<HTMLElement> {
12
12
  export function cleanup(el: HTMLElement) {
13
13
  el.remove();
14
14
  }
15
+
16
+ /** Create a synthetic Invoker Commands API `command` event for testing. */
17
+ export function makeCommandEvent(command: string): Event {
18
+ const event = new Event('command', { bubbles: false, cancelable: true });
19
+ Object.defineProperty(event, 'command', { value: command, configurable: true });
20
+ Object.defineProperty(event, 'source', { value: null, configurable: true });
21
+ return event;
22
+ }