@brandsync/wc 1.8.0 → 1.10.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 (280) hide show
  1. package/custom-elements.json +4493 -3458
  2. package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
  3. package/dist/brandsync-wc/{p-c4de155c.entry.js → p-0132a2d1.entry.js} +1 -1
  4. package/dist/brandsync-wc/{p-051d3064.entry.js → p-041776c0.entry.js} +1 -1
  5. package/dist/brandsync-wc/{p-a09e157f.entry.js → p-08c99a60.entry.js} +1 -1
  6. package/dist/brandsync-wc/{p-70f64b25.entry.js → p-18c3c22c.entry.js} +1 -1
  7. package/dist/brandsync-wc/p-1c3a43c7.entry.js +1 -0
  8. package/dist/brandsync-wc/{p-fddc7bd1.entry.js → p-1c427ffb.entry.js} +1 -1
  9. package/dist/brandsync-wc/p-2181378f.entry.js +1 -0
  10. package/dist/brandsync-wc/{p-29027559.entry.js → p-2e958566.entry.js} +1 -1
  11. package/dist/brandsync-wc/{p-235ae012.entry.js → p-301b2455.entry.js} +1 -1
  12. package/dist/brandsync-wc/{p-8ee0153b.entry.js → p-32cd69f1.entry.js} +1 -1
  13. package/dist/brandsync-wc/p-4f2c4929.entry.js +1 -0
  14. package/dist/brandsync-wc/{p-a0d5a620.entry.js → p-50a251a4.entry.js} +1 -1
  15. package/dist/brandsync-wc/{p-0e39b9cc.entry.js → p-5f3cd501.entry.js} +1 -1
  16. package/dist/brandsync-wc/p-701ebf4a.entry.js +1 -0
  17. package/dist/brandsync-wc/p-763876f5.entry.js +1 -0
  18. package/dist/brandsync-wc/{p-f7d47c17.entry.js → p-76e1c295.entry.js} +1 -1
  19. package/dist/brandsync-wc/p-790db8b3.entry.js +1 -0
  20. package/dist/brandsync-wc/{p-4e5dcaff.entry.js → p-796a1860.entry.js} +1 -1
  21. package/dist/brandsync-wc/p-806a5347.entry.js +1 -0
  22. package/dist/brandsync-wc/{p-6331de9c.entry.js → p-8eba7f90.entry.js} +1 -1
  23. package/dist/brandsync-wc/p-96ed9386.entry.js +1 -0
  24. package/dist/brandsync-wc/{p-cfcf3f0a.entry.js → p-9e88f19c.entry.js} +1 -1
  25. package/dist/brandsync-wc/p-9f6fe37d.entry.js +1 -0
  26. package/dist/brandsync-wc/{p-de7c946d.entry.js → p-a71d2811.entry.js} +1 -1
  27. package/dist/brandsync-wc/{p-33e44094.entry.js → p-a8e2a829.entry.js} +1 -1
  28. package/dist/brandsync-wc/p-addfa0a0.entry.js +1 -0
  29. package/dist/brandsync-wc/{p-4574a707.entry.js → p-af2bf957.entry.js} +1 -1
  30. package/dist/brandsync-wc/{p-6bf2f116.entry.js → p-b2074a32.entry.js} +1 -1
  31. package/dist/brandsync-wc/{p-03b08508.entry.js → p-c3f72584.entry.js} +1 -1
  32. package/dist/brandsync-wc/{p-7c94e6ae.entry.js → p-c99b6a66.entry.js} +1 -1
  33. package/dist/brandsync-wc/{p-2891d40b.entry.js → p-c9a72416.entry.js} +1 -1
  34. package/dist/brandsync-wc/{p-9f65f998.entry.js → p-d3f44cb7.entry.js} +1 -1
  35. package/dist/brandsync-wc/p-d42b1c2b.entry.js +1 -0
  36. package/dist/brandsync-wc/{p-3ec74d35.entry.js → p-daab6b22.entry.js} +1 -1
  37. package/dist/brandsync-wc/{p-ee7da23d.entry.js → p-dbcbe682.entry.js} +1 -1
  38. package/dist/brandsync-wc/{p-3176d1d3.entry.js → p-e8a5b8eb.entry.js} +1 -1
  39. package/dist/brandsync-wc/{p-062cfa34.entry.js → p-ea4e20ef.entry.js} +1 -1
  40. package/dist/brandsync-wc/p-f370b8fa.entry.js +1 -0
  41. package/dist/brandsync-wc/{p-05c7de28.entry.js → p-f89de457.entry.js} +1 -1
  42. package/dist/brandsync-wc/{p-114d2fd8.entry.js → p-fd254972.entry.js} +1 -1
  43. package/dist/cjs/brandsync-wc.cjs.js +1 -1
  44. package/dist/cjs/bs-ai-disclaimer.cjs.entry.js +1 -1
  45. package/dist/cjs/bs-ai-greeting.cjs.entry.js +1 -1
  46. package/dist/cjs/bs-ai-thinking.cjs.entry.js +1 -1
  47. package/dist/cjs/bs-attachment-list.cjs.entry.js +1 -1
  48. package/dist/cjs/bs-breadcrumb-overflow.cjs.entry.js +13 -1
  49. package/dist/cjs/bs-breadcrumb.cjs.entry.js +15 -1
  50. package/dist/cjs/bs-breadcrumbs.cjs.entry.js +1 -1
  51. package/dist/cjs/bs-button-skeleton.cjs.entry.js +1 -1
  52. package/dist/cjs/bs-chatbot-feedback.cjs.entry.js +1 -1
  53. package/dist/cjs/bs-chatbot-header.cjs.entry.js +1 -1
  54. package/dist/cjs/bs-chatbot-response-action.cjs.entry.js +1 -1
  55. package/dist/cjs/bs-chatbot-sources-drawer.cjs.entry.js +1 -1
  56. package/dist/cjs/bs-chatbot-suggestion-button.cjs.entry.js +1 -1
  57. package/dist/cjs/bs-checkbox-skeleton.cjs.entry.js +1 -1
  58. package/dist/cjs/bs-data-table.cjs.entry.js +18 -4
  59. package/dist/cjs/bs-datepicker.cjs.entry.js +410 -0
  60. package/dist/cjs/bs-file-upload-item.cjs.entry.js +1 -1
  61. package/dist/cjs/bs-file-upload.cjs.entry.js +1 -1
  62. package/dist/cjs/bs-icon-button.cjs.entry.js +2 -2
  63. package/dist/cjs/bs-inline-tab.cjs.entry.js +5 -1
  64. package/dist/cjs/{bs-input.cjs.entry.js → bs-input_2.cjs.entry.js} +37 -4
  65. package/dist/cjs/bs-menu.cjs.entry.js +1 -1
  66. package/dist/cjs/bs-navigation-drawer-item.cjs.entry.js +1 -1
  67. package/dist/cjs/bs-navigation-drawer.cjs.entry.js +118 -5
  68. package/dist/cjs/bs-navigation-header.cjs.entry.js +2 -2
  69. package/dist/cjs/bs-pagination.cjs.entry.js +1 -1
  70. package/dist/cjs/bs-progress-linear.cjs.entry.js +2 -2
  71. package/dist/cjs/bs-progress.cjs.entry.js +2 -2
  72. package/dist/cjs/bs-radio.cjs.entry.js +1 -1
  73. package/dist/cjs/bs-slider.cjs.entry.js +1 -1
  74. package/dist/cjs/bs-snackbar.cjs.entry.js +1 -1
  75. package/dist/cjs/bs-source-link.cjs.entry.js +2 -2
  76. package/dist/cjs/bs-stepper-step.cjs.entry.js +1 -1
  77. package/dist/cjs/bs-stepper.cjs.entry.js +1 -1
  78. package/dist/cjs/bs-switch.cjs.entry.js +1 -1
  79. package/dist/cjs/bs-tab.cjs.entry.js +5 -1
  80. package/dist/cjs/bs-tabs.cjs.entry.js +47 -2
  81. package/dist/cjs/bs-timepicker.cjs.entry.js +209 -0
  82. package/dist/cjs/bs-toast.cjs.entry.js +1 -1
  83. package/dist/cjs/bs-tooltip.cjs.entry.js +1 -1
  84. package/dist/cjs/loader.cjs.js +1 -1
  85. package/dist/collection/collection-manifest.json +2 -0
  86. package/dist/collection/components/bs-a11y.cmp.test.js +4 -0
  87. package/dist/collection/components/bs-ai-disclaimer/bs-ai-disclaimer.js +1 -1
  88. package/dist/collection/components/bs-ai-greeting/bs-ai-greeting.js +1 -1
  89. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.js +1 -1
  90. package/dist/collection/components/bs-attachment-list/bs-attachment-list.js +1 -1
  91. package/dist/collection/components/bs-breadcrumb/bs-breadcrumb/bs-breadcrumb.cmp.test.js +63 -0
  92. package/dist/collection/components/bs-breadcrumb/bs-breadcrumb/bs-breadcrumb.js +32 -1
  93. package/dist/collection/components/bs-breadcrumb/bs-breadcrumb-overflow/bs-breadcrumb-overflow.cmp.test.js +45 -0
  94. package/dist/collection/components/bs-breadcrumb/bs-breadcrumb-overflow/bs-breadcrumb-overflow.js +30 -1
  95. package/dist/collection/components/bs-breadcrumb/bs-breadcrumbs/bs-breadcrumbs.js +1 -1
  96. package/dist/collection/components/bs-button/bs-icon-button/bs-icon-button.cmp.test.js +20 -0
  97. package/dist/collection/components/bs-button/bs-icon-button/bs-icon-button.css +21 -4
  98. package/dist/collection/components/bs-button/bs-icon-button/bs-icon-button.js +3 -2
  99. package/dist/collection/components/bs-button/bs-icon-button/bs-icon-button.stories.js +10 -0
  100. package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.js +1 -1
  101. package/dist/collection/components/bs-chatbot-feedback/bs-chatbot-feedback.js +1 -1
  102. package/dist/collection/components/bs-chatbot-header/bs-chatbot-header.js +1 -1
  103. package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.js +1 -1
  104. package/dist/collection/components/bs-chatbot-sources-drawer/bs-chatbot-sources-drawer.js +1 -1
  105. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js +1 -1
  106. package/dist/collection/components/bs-checkbox/bs-checkbox-skeleton/bs-checkbox-skeleton.js +1 -1
  107. package/dist/collection/components/bs-data-table/bs-data-table.cmp.test.js +19 -0
  108. package/dist/collection/components/bs-data-table/bs-data-table.js +19 -5
  109. package/dist/collection/components/bs-data-table/bs-data-table.stories.js +8 -1
  110. package/dist/collection/components/bs-datepicker/bs-datepicker.cmp.test.js +182 -0
  111. package/dist/collection/components/bs-datepicker/bs-datepicker.css +315 -0
  112. package/dist/collection/components/bs-datepicker/bs-datepicker.js +782 -0
  113. package/dist/collection/components/bs-datepicker/bs-datepicker.stories.js +63 -0
  114. package/dist/collection/components/bs-file-upload/bs-file-upload.js +1 -1
  115. package/dist/collection/components/bs-file-upload-item/bs-file-upload-item.js +1 -1
  116. package/dist/collection/components/bs-input/bs-input.js +7 -4
  117. package/dist/collection/components/bs-logo/bs-logo.cmp.test.js +52 -3
  118. package/dist/collection/components/bs-menu/bs-menu.js +1 -1
  119. package/dist/collection/components/bs-menu/bs-menu.stories.js +54 -0
  120. package/dist/collection/components/bs-menu-item/bs-menu-item.js +1 -1
  121. package/dist/collection/components/bs-pagination/bs-pagination.js +1 -1
  122. package/dist/collection/components/bs-progress/bs-progress.js +2 -2
  123. package/dist/collection/components/bs-progress-linear/bs-progress-linear.js +2 -2
  124. package/dist/collection/components/bs-radio/bs-radio.js +1 -1
  125. package/dist/collection/components/bs-slider/bs-slider.js +1 -1
  126. package/dist/collection/components/bs-snackbar/bs-snackbar.js +1 -1
  127. package/dist/collection/components/bs-source-link/bs-source-link.js +2 -2
  128. package/dist/collection/components/bs-stepper/bs-stepper/bs-stepper.js +1 -1
  129. package/dist/collection/components/bs-stepper/bs-stepper-step/bs-stepper-step.js +1 -1
  130. package/dist/collection/components/bs-switch/bs-switch.js +1 -1
  131. package/dist/collection/components/bs-tab/bs-inline-tab/bs-inline-tab.js +24 -1
  132. package/dist/collection/components/bs-tab/bs-tab/bs-tab.js +24 -1
  133. package/dist/collection/components/bs-tab/bs-tabs/bs-tabs.cmp.test.js +88 -0
  134. package/dist/collection/components/bs-tab/bs-tabs/bs-tabs.js +83 -2
  135. package/dist/collection/components/bs-tab/bs-tabs/bs-tabs.stories.js +24 -0
  136. package/dist/collection/components/bs-timepicker/bs-timepicker.cmp.test.js +141 -0
  137. package/dist/collection/components/bs-timepicker/bs-timepicker.css +200 -0
  138. package/dist/collection/components/bs-timepicker/bs-timepicker.js +509 -0
  139. package/dist/collection/components/bs-timepicker/bs-timepicker.stories.js +44 -0
  140. package/dist/collection/components/bs-toast/bs-toast.js +1 -1
  141. package/dist/collection/components/bs-tooltip/bs-tooltip.js +1 -1
  142. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.cmp.test.js +173 -5
  143. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.css +106 -34
  144. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.js +181 -5
  145. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.stories.js +48 -0
  146. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer-item/bs-navigation-drawer-item.js +1 -1
  147. package/dist/collection/components/ui-shell/bs-navigation-header/bs-navigation-header/bs-navigation-header.cmp.test.js +68 -0
  148. package/dist/collection/components/ui-shell/bs-navigation-header/bs-navigation-header/bs-navigation-header.css +31 -17
  149. package/dist/collection/components/ui-shell/bs-navigation-header/bs-navigation-header/bs-navigation-header.js +1 -1
  150. package/dist/components/bs-ai-disclaimer.js +1 -1
  151. package/dist/components/bs-ai-greeting.js +1 -1
  152. package/dist/components/bs-ai-thinking.js +1 -1
  153. package/dist/components/bs-attachment-list.js +1 -1
  154. package/dist/components/bs-attachment.js +1 -1
  155. package/dist/components/bs-breadcrumb-overflow.js +1 -1
  156. package/dist/components/bs-breadcrumb.js +1 -1
  157. package/dist/components/bs-breadcrumbs.js +1 -1
  158. package/dist/components/bs-button-skeleton.js +1 -1
  159. package/dist/components/bs-carousel.js +1 -1
  160. package/dist/components/bs-chatbot-feedback.js +1 -1
  161. package/dist/components/bs-chatbot-header.js +1 -1
  162. package/dist/components/bs-chatbot-response-action.js +1 -1
  163. package/dist/components/bs-chatbot-sources-drawer.js +1 -1
  164. package/dist/components/bs-chatbot-suggestion-button.js +1 -1
  165. package/dist/components/bs-checkbox-skeleton.js +1 -1
  166. package/dist/components/bs-data-table.js +1 -1
  167. package/dist/components/bs-datepicker.d.ts +11 -0
  168. package/dist/components/bs-datepicker.js +1 -0
  169. package/dist/components/bs-dialog.js +1 -1
  170. package/dist/components/bs-file-upload-item.js +1 -1
  171. package/dist/components/bs-file-upload.js +1 -1
  172. package/dist/components/bs-icon-button.js +1 -1
  173. package/dist/components/bs-inline-tab.js +1 -1
  174. package/dist/components/bs-input.js +1 -1
  175. package/dist/components/bs-menu-item.js +1 -1
  176. package/dist/components/bs-menu.js +1 -1
  177. package/dist/components/bs-navigation-drawer-item.js +1 -1
  178. package/dist/components/bs-navigation-drawer.js +1 -1
  179. package/dist/components/bs-navigation-header.js +1 -1
  180. package/dist/components/bs-pagination.js +1 -1
  181. package/dist/components/bs-progress-linear.js +1 -1
  182. package/dist/components/bs-progress.js +1 -1
  183. package/dist/components/bs-radio.js +1 -1
  184. package/dist/components/bs-slider.js +1 -1
  185. package/dist/components/bs-snackbar.js +1 -1
  186. package/dist/components/bs-source-link.js +1 -1
  187. package/dist/components/bs-stepper-step.js +1 -1
  188. package/dist/components/bs-stepper.js +1 -1
  189. package/dist/components/bs-switch.js +1 -1
  190. package/dist/components/bs-tab.js +1 -1
  191. package/dist/components/bs-tabs.js +1 -1
  192. package/dist/components/bs-timepicker.d.ts +11 -0
  193. package/dist/components/bs-timepicker.js +1 -0
  194. package/dist/components/bs-toast.js +1 -1
  195. package/dist/components/bs-tooltip.js +1 -1
  196. package/dist/components/{p-BA3c9Rom.js → p-B7Icv1ho.js} +1 -1
  197. package/dist/components/{p-wei0Ija2.js → p-BTvme8CE.js} +1 -1
  198. package/dist/components/p-BYR_ZC8v.js +1 -0
  199. package/dist/components/p-Ba860ej1.js +1 -0
  200. package/dist/components/p-CPvqZNO8.js +1 -0
  201. package/dist/components/{p-EtXc94lJ.js → p-DkIVyFOP.js} +1 -1
  202. package/dist/components/{p-BfEyD6Ku.js → p-ZK8p5zax.js} +1 -1
  203. package/dist/components/p-jDO25SfF.js +1 -0
  204. package/dist/esm/brandsync-wc.js +1 -1
  205. package/dist/esm/bs-ai-disclaimer.entry.js +1 -1
  206. package/dist/esm/bs-ai-greeting.entry.js +1 -1
  207. package/dist/esm/bs-ai-thinking.entry.js +1 -1
  208. package/dist/esm/bs-attachment-list.entry.js +1 -1
  209. package/dist/esm/bs-breadcrumb-overflow.entry.js +14 -2
  210. package/dist/esm/bs-breadcrumb.entry.js +16 -2
  211. package/dist/esm/bs-breadcrumbs.entry.js +1 -1
  212. package/dist/esm/bs-button-skeleton.entry.js +1 -1
  213. package/dist/esm/bs-chatbot-feedback.entry.js +1 -1
  214. package/dist/esm/bs-chatbot-header.entry.js +1 -1
  215. package/dist/esm/bs-chatbot-response-action.entry.js +1 -1
  216. package/dist/esm/bs-chatbot-sources-drawer.entry.js +1 -1
  217. package/dist/esm/bs-chatbot-suggestion-button.entry.js +1 -1
  218. package/dist/esm/bs-checkbox-skeleton.entry.js +1 -1
  219. package/dist/esm/bs-data-table.entry.js +18 -4
  220. package/dist/esm/bs-datepicker.entry.js +408 -0
  221. package/dist/esm/bs-file-upload-item.entry.js +1 -1
  222. package/dist/esm/bs-file-upload.entry.js +1 -1
  223. package/dist/esm/bs-icon-button.entry.js +2 -2
  224. package/dist/esm/bs-inline-tab.entry.js +5 -1
  225. package/dist/esm/{bs-input.entry.js → bs-input_2.entry.js} +37 -5
  226. package/dist/esm/bs-menu.entry.js +1 -1
  227. package/dist/esm/bs-navigation-drawer-item.entry.js +1 -1
  228. package/dist/esm/bs-navigation-drawer.entry.js +119 -6
  229. package/dist/esm/bs-navigation-header.entry.js +2 -2
  230. package/dist/esm/bs-pagination.entry.js +1 -1
  231. package/dist/esm/bs-progress-linear.entry.js +2 -2
  232. package/dist/esm/bs-progress.entry.js +2 -2
  233. package/dist/esm/bs-radio.entry.js +1 -1
  234. package/dist/esm/bs-slider.entry.js +1 -1
  235. package/dist/esm/bs-snackbar.entry.js +1 -1
  236. package/dist/esm/bs-source-link.entry.js +2 -2
  237. package/dist/esm/bs-stepper-step.entry.js +1 -1
  238. package/dist/esm/bs-stepper.entry.js +1 -1
  239. package/dist/esm/bs-switch.entry.js +1 -1
  240. package/dist/esm/bs-tab.entry.js +5 -1
  241. package/dist/esm/bs-tabs.entry.js +48 -3
  242. package/dist/esm/bs-timepicker.entry.js +207 -0
  243. package/dist/esm/bs-toast.entry.js +1 -1
  244. package/dist/esm/bs-tooltip.entry.js +1 -1
  245. package/dist/esm/loader.js +1 -1
  246. package/dist/types/components/bs-breadcrumb/bs-breadcrumb/bs-breadcrumb.d.ts +10 -0
  247. package/dist/types/components/bs-breadcrumb/bs-breadcrumb-overflow/bs-breadcrumb-overflow.d.ts +8 -0
  248. package/dist/types/components/bs-button/bs-icon-button/bs-icon-button.d.ts +2 -1
  249. package/dist/types/components/bs-button/bs-icon-button/bs-icon-button.stories.d.ts +1 -0
  250. package/dist/types/components/bs-data-table/bs-data-table.d.ts +16 -2
  251. package/dist/types/components/bs-datepicker/bs-datepicker.cmp.test.d.ts +1 -0
  252. package/dist/types/components/bs-datepicker/bs-datepicker.d.ts +136 -0
  253. package/dist/types/components/bs-datepicker/bs-datepicker.stories.d.ts +20 -0
  254. package/dist/types/components/bs-menu/bs-menu.stories.d.ts +1 -0
  255. package/dist/types/components/bs-tab/bs-inline-tab/bs-inline-tab.d.ts +4 -0
  256. package/dist/types/components/bs-tab/bs-tab/bs-tab.d.ts +4 -0
  257. package/dist/types/components/bs-tab/bs-tabs/bs-tabs.d.ts +17 -0
  258. package/dist/types/components/bs-tab/bs-tabs/bs-tabs.stories.d.ts +1 -0
  259. package/dist/types/components/bs-timepicker/bs-timepicker.cmp.test.d.ts +1 -0
  260. package/dist/types/components/bs-timepicker/bs-timepicker.d.ts +101 -0
  261. package/dist/types/components/bs-timepicker/bs-timepicker.stories.d.ts +14 -0
  262. package/dist/types/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.d.ts +50 -1
  263. package/dist/types/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.stories.d.ts +2 -0
  264. package/dist/types/components.d.ts +645 -2
  265. package/package.json +1 -1
  266. package/dist/brandsync-wc/p-002d258d.entry.js +0 -1
  267. package/dist/brandsync-wc/p-153da845.entry.js +0 -1
  268. package/dist/brandsync-wc/p-2e7a1082.entry.js +0 -1
  269. package/dist/brandsync-wc/p-612cf79c.entry.js +0 -1
  270. package/dist/brandsync-wc/p-652d4068.entry.js +0 -1
  271. package/dist/brandsync-wc/p-7b00dd36.entry.js +0 -1
  272. package/dist/brandsync-wc/p-ae978c9a.entry.js +0 -1
  273. package/dist/brandsync-wc/p-bc69a96c.entry.js +0 -1
  274. package/dist/brandsync-wc/p-bf53ad52.entry.js +0 -1
  275. package/dist/brandsync-wc/p-c10d2529.entry.js +0 -1
  276. package/dist/brandsync-wc/p-c7f4c114.entry.js +0 -1
  277. package/dist/cjs/bs-menu-item.cjs.entry.js +0 -34
  278. package/dist/components/p-BkHYJIyj.js +0 -1
  279. package/dist/components/p-Buly0d5G.js +0 -1
  280. package/dist/esm/bs-menu-item.entry.js +0 -32
@@ -54,7 +54,7 @@ export class BsBreadcrumbs {
54
54
  });
55
55
  }
56
56
  render() {
57
- return (h("nav", { key: '14bf783e0dc34d1f8368bde48499e684b1410b94', "aria-label": this.label }, h("ol", { key: 'feef69a5164f6d7dad670e56cdbd7912aff96c2c', part: "list", class: "bs-breadcrumbs__list" }, h("slot", { key: 'b616fc5fcf0181025be6e5570b7d0fa10cae146e', onSlotchange: this.onSlotChange }))));
57
+ return (h("nav", { key: '2772673d54ceab8866fada7a3a460938fd03d00c', "aria-label": this.label }, h("ol", { key: 'd9021230f43d8c307ea89ba5ce665bacff66cadf', part: "list", class: "bs-breadcrumbs__list" }, h("slot", { key: 'eccde40d6fc8c47caf1813daccb906b748117840', onSlotchange: this.onSlotChange }))));
58
58
  }
59
59
  static get is() { return "bs-breadcrumbs"; }
60
60
  static get encapsulation() { return "shadow"; }
@@ -80,4 +80,24 @@ describe('bs-icon-button', () => {
80
80
  expect(iconWrapper.querySelector('slot')).not.toBeNull();
81
81
  expect(root.querySelector('svg.my-icon')).not.toBeNull();
82
82
  });
83
+ describe('icon-font glyphs', () => {
84
+ // Issue: a slotted icon-font glyph (<i class="ph-bold ...">) ignored the size prop, because
85
+ // glyphs are sized by font-size and only width/height were being set.
86
+ for (const [size, px] of [
87
+ ['xs', 8],
88
+ ['sm', 16],
89
+ ['md', 20],
90
+ ['lg', 24],
91
+ ]) {
92
+ it(`size="${size}" sizes a slotted glyph to ${px}px via font-size`, async () => {
93
+ const { root } = await render(h("bs-icon-button", { size: size, ariaLabel: "Bell" }, h("i", { class: "glyph" }, "A")));
94
+ const glyph = root.querySelector('i.glyph');
95
+ expect(getComputedStyle(glyph).fontSize).toBe(`${px}px`);
96
+ });
97
+ }
98
+ it('does not override a font-size the consumer sets on the glyph themselves', async () => {
99
+ const { root } = await render(h("bs-icon-button", { size: "md", ariaLabel: "Bell" }, h("i", { class: "glyph", style: { fontSize: '30px' } }, "A")));
100
+ expect(getComputedStyle(root.querySelector('i.glyph')).fontSize).toBe('30px');
101
+ });
102
+ });
83
103
  });
@@ -2,14 +2,14 @@
2
2
  * @prop --bs-icon-button-radius: Corner radius, size="sm"/"md"/"lg". Aliased to --bs-border-radius-100.
3
3
  * @prop --bs-icon-button-radius-xs: Corner radius, size="xs" -- a smaller radius than --bs-icon-button-radius, since --bs-border-radius-100 (8px) on a 16px box would look nearly circular. Aliased to --bs-border-radius-50.
4
4
  * @prop --bs-icon-button-size-xs: Container width/height at size="xs". 16px.
5
- * @prop --bs-icon-button-icon-size-xs: Slotted icon width/height at size="xs". 8px.
5
+ * @prop --bs-icon-button-icon-size-xs: Slotted icon width/height (and font-size, for icon-font glyphs) at size="xs". 8px.
6
6
  * @prop --bs-icon-button-padding-xs: Padding on all sides, size="xs" only -- distinct from --bs-icon-button-padding, since 16px/8px icon math (4px) differs from the sm/md/lg constant (12px). Aliased to --bs-spacing-50.
7
7
  * @prop --bs-icon-button-size-sm: Container width/height at size="sm". 40px.
8
8
  * @prop --bs-icon-button-size-md: Container width/height at size="md". 44px.
9
9
  * @prop --bs-icon-button-size-lg: Container width/height at size="lg". 48px.
10
- * @prop --bs-icon-button-icon-size-sm: Slotted icon width/height at size="sm". 16px.
11
- * @prop --bs-icon-button-icon-size-md: Slotted icon width/height at size="md". 20px.
12
- * @prop --bs-icon-button-icon-size-lg: Slotted icon width/height at size="lg". 24px.
10
+ * @prop --bs-icon-button-icon-size-sm: Slotted icon width/height (and font-size, for icon-font glyphs) at size="sm". 16px.
11
+ * @prop --bs-icon-button-icon-size-md: Slotted icon width/height (and font-size, for icon-font glyphs) at size="md". 20px.
12
+ * @prop --bs-icon-button-icon-size-lg: Slotted icon width/height (and font-size, for icon-font glyphs) at size="lg". 24px.
13
13
  * @prop --bs-icon-button-padding: Padding on all sides, all sizes (constant -- centers the icon
14
14
  * inside the container at every size). Aliased to --bs-spacing-150.
15
15
  * @prop --bs-icon-button-primary-default: Background, variant="primary", default. Aliased to --bs-color-primary-default.
@@ -161,6 +161,9 @@
161
161
  .bs-icon-button--xs .bs-icon-button__icon {
162
162
  width: var(--bs-icon-button-icon-size-xs);
163
163
  height: var(--bs-icon-button-icon-size-xs);
164
+ /* Icon-font glyphs (e.g. <i class="ph-bold ph-bell">) are sized by font-size, not width/height,
165
+ so they follow the same token as SVG icons do. */
166
+ font-size: var(--bs-icon-button-icon-size-xs);
164
167
  }
165
168
 
166
169
  .bs-icon-button--sm {
@@ -170,6 +173,9 @@
170
173
  .bs-icon-button--sm .bs-icon-button__icon {
171
174
  width: var(--bs-icon-button-icon-size-sm);
172
175
  height: var(--bs-icon-button-icon-size-sm);
176
+ /* Icon-font glyphs (e.g. <i class="ph-bold ph-bell">) are sized by font-size, not width/height,
177
+ so they follow the same token as SVG icons do. */
178
+ font-size: var(--bs-icon-button-icon-size-sm);
173
179
  }
174
180
 
175
181
  .bs-icon-button--md {
@@ -179,6 +185,9 @@
179
185
  .bs-icon-button--md .bs-icon-button__icon {
180
186
  width: var(--bs-icon-button-icon-size-md);
181
187
  height: var(--bs-icon-button-icon-size-md);
188
+ /* Icon-font glyphs (e.g. <i class="ph-bold ph-bell">) are sized by font-size, not width/height,
189
+ so they follow the same token as SVG icons do. */
190
+ font-size: var(--bs-icon-button-icon-size-md);
182
191
  }
183
192
 
184
193
  .bs-icon-button--lg {
@@ -188,6 +197,9 @@
188
197
  .bs-icon-button--lg .bs-icon-button__icon {
189
198
  width: var(--bs-icon-button-icon-size-lg);
190
199
  height: var(--bs-icon-button-icon-size-lg);
200
+ /* Icon-font glyphs (e.g. <i class="ph-bold ph-bell">) are sized by font-size, not width/height,
201
+ so they follow the same token as SVG icons do. */
202
+ font-size: var(--bs-icon-button-icon-size-lg);
191
203
  }
192
204
 
193
205
  .bs-icon-button__icon {
@@ -195,12 +207,17 @@
195
207
  align-items: center;
196
208
  justify-content: center;
197
209
  color: inherit;
210
+ line-height: 1;
198
211
  }
199
212
 
200
213
  .bs-icon-button__icon ::slotted(*) {
201
214
  display: block;
202
215
  width: 100%;
203
216
  height: 100%;
217
+ /* Make a slotted icon font glyph take the wrapper's font-size (above) rather than whatever the
218
+ page's inherited size happens to be. */
219
+ font-size: inherit;
220
+ line-height: 1;
204
221
  }
205
222
 
206
223
  .bs-icon-button--primary {
@@ -20,7 +20,8 @@ import { h } from "@stencil/core";
20
20
  * just conditionally, unlike `bs-button`) -- there's no visible label text to fall back on for an
21
21
  * accessible name.
22
22
  *
23
- * @slot - Default slot: the icon content. Exactly one icon, no label.
23
+ * @slot - Default slot: the icon content. Exactly one icon, no label -- an inline SVG, or an icon-font
24
+ * glyph (sized by `font-size`, which this component sets from the `size` prop).
24
25
  * @part icon - The icon wrapper element.
25
26
  */
26
27
  // Note: this component's CSS Custom Properties (--bs-icon-button-*) are documented via @prop
@@ -61,7 +62,7 @@ export class BsIconButton {
61
62
  form.reset();
62
63
  };
63
64
  render() {
64
- return (h("button", { key: '1d0525e3ad0b90e723cab2c9cd0217046a905a5e', class: `bs-icon-button bs-icon-button--${this.variant} bs-icon-button--${this.size}`, type: this.type, disabled: this.disabled, "aria-label": this.ariaLabel, onClick: this.onClick }, h("span", { key: '1c94ad4a861dde9e4942f6795a2a868161c0b4a1', part: "icon", class: "bs-icon-button__icon" }, h("slot", { key: '9cd3a6aa2d9cbfebaa1d0e70fc10448af4ef4429' }))));
65
+ return (h("button", { key: '433827a167fc7ce5183358c3e8469d68dd67788a', class: `bs-icon-button bs-icon-button--${this.variant} bs-icon-button--${this.size}`, type: this.type, disabled: this.disabled, "aria-label": this.ariaLabel, onClick: this.onClick }, h("span", { key: 'e7d50a6755e7e4de39db799abef38c118d888b98', part: "icon", class: "bs-icon-button__icon" }, h("slot", { key: 'a737620ca5d0baf69abc1964a160492c32359d09' }))));
65
66
  }
66
67
  static get is() { return "bs-icon-button"; }
67
68
  static get encapsulation() { return "shadow"; }
@@ -42,6 +42,16 @@ export const Sizes = {
42
42
  </div>
43
43
  `,
44
44
  };
45
+ // An icon-font glyph in the slot (here a plain text glyph, so the story needs no icon-font
46
+ // dependency) scales with `size` exactly like the SVG above -- no manual font-size needed.
47
+ export const IconFontGlyph = {
48
+ name: 'Icon-font glyph',
49
+ render: args => html `
50
+ <div style="display:flex; gap:12px; align-items:center;">
51
+ ${['xs', 'sm', 'md', 'lg'].map(size => html `<bs-icon-button size=${size} aria-label=${args.ariaLabel}><i style="font-style: normal;">★</i></bs-icon-button>`)}
52
+ </div>
53
+ `,
54
+ };
45
55
  export const Disabled = {
46
56
  args: { disabled: true },
47
57
  };
@@ -25,7 +25,7 @@ export class BsButtonSkeleton {
25
25
  /** Sizing scale, matching the `bs-button` size it stands in for. */
26
26
  size = 'md';
27
27
  render() {
28
- return h("span", { key: '1b7d442c2522cd4591862c6d72ee9bc491570518', part: "container", class: `bs-button-skeleton bs-button-skeleton--${this.size}`, role: "status", "aria-label": "Loading" });
28
+ return h("span", { key: 'e272ddf5faf4cf9875b3d70d22ae4a406d1fdf02', part: "container", class: `bs-button-skeleton bs-button-skeleton--${this.size}`, role: "status", "aria-label": "Loading" });
29
29
  }
30
30
  static get is() { return "bs-button-skeleton"; }
31
31
  static get encapsulation() { return "shadow"; }
@@ -123,7 +123,7 @@ export class BsChatbotFeedback {
123
123
  }
124
124
  render() {
125
125
  const stars = [1, 2, 3, 4, 5];
126
- return (h("div", { key: 'a0b98bb7dad05b9b2a03d56f7c1d56a969b8e9c1', class: "bs-chatbot-feedback", role: "region", "aria-label": this.computedHeading() }, h("button", { key: '2a4aae0fd5ac35bf0d399c6d0a2f9a2c8ab11e08', type: "button", part: "close", class: "bs-chatbot-feedback__close", "aria-label": "Close", onClick: this.onCloseClick }, h(CloseIcon, { key: 'fa79bfca02a4b098d5c1695de4ae149f9aef7296' })), h("div", { key: '2f921033f1797610773778dc078b754cd6ce90a2', class: "bs-chatbot-feedback__top" }, h("div", { key: 'a7e86fca322312a90674b8f3ae7250551a56609b', class: "bs-chatbot-feedback__text" }, h("p", { key: 'eff8c54234588b9e990c48c9aa01d72d1f12e4b8', part: "heading", class: "bs-chatbot-feedback__heading" }, this.computedHeading()), h("p", { key: 'a742285c906efd60feb339484af1a7e310b929fb', part: "subtitle", class: "bs-chatbot-feedback__subtitle" }, this.computedSubtitle())), h("div", { key: '700e4903e909bfe6fec476092a84407c84eea5fa', class: "bs-chatbot-feedback__stars", role: "radiogroup", "aria-label": "Rating" }, stars.map(value => (h("button", { type: "button", part: "star", class: "bs-chatbot-feedback__star", role: "radio", "aria-checked": (this.rating >= value).toString(), "aria-label": `${value} star${value > 1 ? 's' : ''}`, disabled: this.submitted, onClick: () => this.onStarClick(value) }, h(StarIcon, { filled: this.rating >= value }))))), !this.submitted && (h("div", { key: '8a00fa2e36f0206725af72814843cc9197e23eaf', class: "bs-chatbot-feedback__field" }, h("label", { key: '49a347d77ee0670c120141b7d5d1faf52a5ff68c', part: "label", class: "bs-chatbot-feedback__label", htmlFor: "bs-chatbot-feedback-comment" }, "Tell us what you think"), h("textarea", { key: 'dbf93923d2e234d20a12d01d826dfaf0d5a3c567', part: "textarea", id: "bs-chatbot-feedback-comment", class: "bs-chatbot-feedback__textarea", placeholder: "e.g.The response was helpful ....", value: this.comment, onInput: this.onCommentInput }))), this.submitted && this.comment && (h("p", { key: 'ca73192186871074989c44ea80c419d0c0765419', part: "comment", class: "bs-chatbot-feedback__comment" }, this.comment))), h("div", { key: '9a244b833310b3e43bbe46833998591482b0dde7', class: "bs-chatbot-feedback__actions" }, !this.submitted && (h("button", { key: '27f8921c9c57bf5b0c1f0e6d7b9e95203b33622a', type: "button", part: "submit", class: "bs-chatbot-feedback__submit", disabled: this.rating === 0, onClick: this.onSubmitClick }, h(SendIcon, { key: '29a7c5b279b82036b5d066b6946ca529e04ee25e' }), "Submit feedback")), h("button", { key: '64676e0e917e0464cf9cfe84b8fecdcb5db26d50', type: "button", part: "new-chat", class: `bs-chatbot-feedback__new-chat ${this.submitted ? 'bs-chatbot-feedback__new-chat--primary' : 'bs-chatbot-feedback__new-chat--outlined'}`, onClick: this.onNewChatClick }, h(EditIcon, { key: '6756913ca0e15a119123d4f78bb7b6c4edc3fd32' }), "Start new chat"))));
126
+ return (h("div", { key: 'aabc3b63a41eb5324d572f20a0af29ccea5d9507', class: "bs-chatbot-feedback", role: "region", "aria-label": this.computedHeading() }, h("button", { key: '2b01df39b773ba1ab55b654b688b8e514af87c29', type: "button", part: "close", class: "bs-chatbot-feedback__close", "aria-label": "Close", onClick: this.onCloseClick }, h(CloseIcon, { key: 'bcffb7d5c49c3de2fd62296c0a815a9ebab92bef' })), h("div", { key: '2d2f9c08f0b345dacda9d6514e6b2c665967358c', class: "bs-chatbot-feedback__top" }, h("div", { key: 'd06bf92615cb05d6b3142c7b375297b78a8ebb1c', class: "bs-chatbot-feedback__text" }, h("p", { key: '0c362bce3ad81312816565fff5440593daf55790', part: "heading", class: "bs-chatbot-feedback__heading" }, this.computedHeading()), h("p", { key: '0fe0980b52c758fcbed0176c5791e1ac088cc5ae', part: "subtitle", class: "bs-chatbot-feedback__subtitle" }, this.computedSubtitle())), h("div", { key: '556b835d8cc4543f524fdb01791c3af7aacd8669', class: "bs-chatbot-feedback__stars", role: "radiogroup", "aria-label": "Rating" }, stars.map(value => (h("button", { type: "button", part: "star", class: "bs-chatbot-feedback__star", role: "radio", "aria-checked": (this.rating >= value).toString(), "aria-label": `${value} star${value > 1 ? 's' : ''}`, disabled: this.submitted, onClick: () => this.onStarClick(value) }, h(StarIcon, { filled: this.rating >= value }))))), !this.submitted && (h("div", { key: 'f99093e2962ffedc27999e3a517c48904dea468b', class: "bs-chatbot-feedback__field" }, h("label", { key: 'c9bb2ebb44265fc2700e4a862e5ccb6076eaa25e', part: "label", class: "bs-chatbot-feedback__label", htmlFor: "bs-chatbot-feedback-comment" }, "Tell us what you think"), h("textarea", { key: '847b1022f1d7e1507e5e82186e03735eccafd80e', part: "textarea", id: "bs-chatbot-feedback-comment", class: "bs-chatbot-feedback__textarea", placeholder: "e.g.The response was helpful ....", value: this.comment, onInput: this.onCommentInput }))), this.submitted && this.comment && (h("p", { key: '7817ecda3a3778cd8b3fb05565ca4ed4101da612', part: "comment", class: "bs-chatbot-feedback__comment" }, this.comment))), h("div", { key: 'e0415b534c83f0fd8026c70aa01db12e68f0bab4', class: "bs-chatbot-feedback__actions" }, !this.submitted && (h("button", { key: 'f2969b4501a1e0af5adb302d5c80e813cd1aec26', type: "button", part: "submit", class: "bs-chatbot-feedback__submit", disabled: this.rating === 0, onClick: this.onSubmitClick }, h(SendIcon, { key: 'd3ca363da5f1e278a4d1653137cacfe42ac7daf7' }), "Submit feedback")), h("button", { key: '0b40842d7d9bf740847d0b851a10c9459db4a9c1', type: "button", part: "new-chat", class: `bs-chatbot-feedback__new-chat ${this.submitted ? 'bs-chatbot-feedback__new-chat--primary' : 'bs-chatbot-feedback__new-chat--outlined'}`, onClick: this.onNewChatClick }, h(EditIcon, { key: '6fe3c4955794d0d6167d307afe5156923ae871f2' }), "Start new chat"))));
127
127
  }
128
128
  static get is() { return "bs-chatbot-feedback"; }
129
129
  static get encapsulation() { return "shadow"; }
@@ -74,7 +74,7 @@ export class BsChatbotHeader {
74
74
  this.bsClose.emit();
75
75
  };
76
76
  render() {
77
- return (h("div", { key: 'ace9458a7d7a57dd371274deceb493714fc953e2', class: "bs-chatbot-header", role: "banner", "aria-label": this.heading }, h("div", { key: 'a4b4b0c3df647f978f5d473f050d87d6d88fb417', part: "logo", class: "bs-chatbot-header__logo", role: "img", "aria-label": this.heading, innerHTML: GENIE_LOGO_SVG }), h("div", { key: '94ea222a987e644f60d5c1e7f8c3e6f90100dcbf', class: "bs-chatbot-header__actions" }, h("span", { key: '411727c2c8274381cf41a08d9fe99150ff7ce4f7', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '3fa054ae17e01d61a27c01f59798cfe8dc451e76', type: "button", part: "new-chat", class: "bs-chatbot-header__button", "aria-label": "New chat", onClick: this.onNewChatClick }, h("slot", { key: '4151abddb9737773b8d7c30715e3a226752bc4d7', name: "new-chat-icon" }, h(NewChatIcon, { key: '8c626ad045ad9e501e9d68f42b6811170ef780e9' }))), h("bs-tooltip", { key: 'cf7e1370c68f7ee2480970b628b0f43b5c31e7c2', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "New chat")), h("span", { key: '50de92e37c966442cc8785dd7294a2769aca4cca', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '37fd0763df000a5a98eb0ce99c3808c4307b3ea6', type: "button", part: "history", class: "bs-chatbot-header__button", "aria-label": "History", onClick: this.onHistoryClick }, h("slot", { key: 'a8751f3d787f24225b8cdd2e12a92e51f46ff759', name: "history-icon" }, h(HistoryIcon, { key: '426227289c53d661ae1c79f33dfca5dc68dfb701' }))), h("bs-tooltip", { key: 'f22b93a3e898b6dad241de7d6d1a1def1e2fd127', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "History")), h("span", { key: 'b7671f7ccc56d59a41a2bbb648ec38c3ae6da071', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '94b47506fddd5b21b38f6dc63d2e2da76f5bce46', type: "button", part: "expand", class: "bs-chatbot-header__button", "aria-label": this.expanded ? 'Collapse' : 'Expand', "aria-pressed": this.expanded ? 'true' : 'false', onClick: this.onExpandClick }, h("slot", { key: '033e0cf8cab7a62fc562fd9b3a6b39d0a6f45bc1', name: "expand-icon" }, h(ExpandIcon, { key: 'a05f02a22f4faa22704c66ce6705377878bbc63b' }))), h("bs-tooltip", { key: 'b1170cfecb1e28f18aae64053bf053b35c8456dd', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, this.expanded ? 'Collapse' : 'Expand')), h("span", { key: '2ec5e1c8e875bf390680dc003325456f8c7304fd', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '45c7865b57760e8acfb375ab4fab9d76db97d01a', type: "button", part: "close", class: "bs-chatbot-header__button", "aria-label": "Close", onClick: this.onCloseClick }, h("slot", { key: 'dc00d32a101d05c3468f37de7a4d06d5f9f36718', name: "close-icon" }, h(CloseIcon, { key: '63dc3094f4b9a637ebcd68541efc562d091f1e96' }))), h("bs-tooltip", { key: 'f735fcc2cc2a24a9bb58a9f5b479c369b11133c6', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "Close")))));
77
+ return (h("div", { key: '05419c0e9659074c99586190cd78a8de7a5eafcc', class: "bs-chatbot-header", role: "banner", "aria-label": this.heading }, h("div", { key: 'db67056399c3cd0054f24b8b6622221ec9a0817a', part: "logo", class: "bs-chatbot-header__logo", role: "img", "aria-label": this.heading, innerHTML: GENIE_LOGO_SVG }), h("div", { key: 'c8d98740445bd9d53c096b16058e721c9aea098c', class: "bs-chatbot-header__actions" }, h("span", { key: 'fbe6e910956684325e7ea9945f6ec16dbaf89b47', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '30d9914ab21468151821e45b6243196e8e69f5b0', type: "button", part: "new-chat", class: "bs-chatbot-header__button", "aria-label": "New chat", onClick: this.onNewChatClick }, h("slot", { key: '1d85439aa66bc42d0a479c90fa581f9ef4fd4245', name: "new-chat-icon" }, h(NewChatIcon, { key: 'adada1b7014df77327f48279fcab1468c9ebcb5e' }))), h("bs-tooltip", { key: '5854895579df420a25b8ad367aa31acf7a5afc05', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "New chat")), h("span", { key: 'd2ce725b9b07208ae540601afc99845adceaf110', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: 'a2dd3406bc6b557dd0323bffd843e21a9c4cbed6', type: "button", part: "history", class: "bs-chatbot-header__button", "aria-label": "History", onClick: this.onHistoryClick }, h("slot", { key: '2acf5d76225c710ed8da26d7b28a15b9906f6938', name: "history-icon" }, h(HistoryIcon, { key: 'a5452154bd46c7b0cfdfc77b584aed0078e9d808' }))), h("bs-tooltip", { key: 'da3ca7cdeb6182fb45e969e6cfb8232ec8410a17', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "History")), h("span", { key: 'c69f4bba7e92f341f57c77909dfa4f5365f0ec3a', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: 'cd42ff4dd756295b93948fb2c9a9b5b957c4e335', type: "button", part: "expand", class: "bs-chatbot-header__button", "aria-label": this.expanded ? 'Collapse' : 'Expand', "aria-pressed": this.expanded ? 'true' : 'false', onClick: this.onExpandClick }, h("slot", { key: '8c13063be20983c2c504042d3811c5dc4820f399', name: "expand-icon" }, h(ExpandIcon, { key: 'd508f7f4ff1a3b9e5622aab3775c786d85472b3e' }))), h("bs-tooltip", { key: 'e575d46939327fb304c3fdd6e78b169271b39848', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, this.expanded ? 'Collapse' : 'Expand')), h("span", { key: '9b4bab65e49526f20b2760f3bc8d98fcefad30c7', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '519b509a320894329ba716f604d185dd4d2746b0', type: "button", part: "close", class: "bs-chatbot-header__button", "aria-label": "Close", onClick: this.onCloseClick }, h("slot", { key: '4205d1d665053faf07ba13d929f5120f05806a89', name: "close-icon" }, h(CloseIcon, { key: '76bf4ab39f7e5fbdcdbb62d0baabb381192fbae7' }))), h("bs-tooltip", { key: 'cbebd979aef708c05e75bf835cd353834bcf83cb', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "Close")))));
78
78
  }
79
79
  static get is() { return "bs-chatbot-header"; }
80
80
  static get encapsulation() { return "shadow"; }
@@ -105,7 +105,7 @@ export class BsChatbotResponseAction {
105
105
  };
106
106
  render() {
107
107
  const hasSources = !!this.sourcesCount && this.sourcesCount > 0;
108
- return (h("div", { key: 'cbd257d7e8028f159bd8034217c3996d44cdf8ff', class: "bs-chatbot-response-action" }, h("button", { key: 'b1727aa3c799a5f7847784bbb5bee5abb9021800', type: "button", part: "like", class: "bs-chatbot-response-action__button", "aria-label": "Like", onClick: this.onLikeClick }, h("slot", { key: 'a39db9d1411f5923415f2f4576d715b7d44f5746', name: "like-icon" }, h(ThumbsUpIcon, { key: 'ad66ffbe937780037b25842da79ed34cede4fb8a' }))), h("button", { key: 'dd3fff82e054acba64d469aa939fe5d9e948ee86', type: "button", part: "dislike", class: "bs-chatbot-response-action__button", "aria-label": "Dislike", onClick: this.onDislikeClick }, h("slot", { key: '4d63753fca15dd1c9def17dd723fdbe549e286ea', name: "dislike-icon" }, h(ThumbsDownIcon, { key: '5985ecd4c1174a1d853dcafd27662eb0065f6a2f' }))), h("button", { key: '9a42c214ad271dd393deb319ae15815297970296', type: "button", part: "copy", class: "bs-chatbot-response-action__button", "aria-label": "Copy", onClick: this.onCopyClick }, h("slot", { key: '9b59e4e174e146e933683cb1e8f5b8887e17171b', name: "copy-icon" }, h(CopyIcon, { key: '73ed83f0056a5acb93ca731a69f5d0d320e2806c' }))), h("button", { key: '435980b95cfd5bd5c0c082520c9d2507357a481f', type: "button", part: "regenerate", class: "bs-chatbot-response-action__button", "aria-label": "Regenerate", onClick: this.onRegenerateClick }, h("slot", { key: '1f536807ea55e6332bb4c98314e747f1ff775ea4', name: "regenerate-icon" }, h(RegenerateIcon, { key: '461ce99ba4bb70ca07810a5883e00d96f6ff3e6c' }))), h("div", { key: '086174d1bc011ad39bf100ffb0ce39b7815c50e2', class: "bs-chatbot-response-action__menu-wrapper" }, h("button", { key: 'd1d52de0d6becf762e7f754f65cbd4430de168aa', type: "button", part: "menu", class: "bs-chatbot-response-action__button", "aria-label": "More options", "aria-haspopup": "menu", "aria-expanded": this.menuOpen ? 'true' : 'false', onClick: this.onMenuClick }, h("slot", { key: 'e70c2b467bb50751afd3b84874b653d1124fc3e5', name: "menu-icon" }, h(MenuIcon, { key: 'b73c887976b2e143638d61c0b9ea41ebb2e388bd' }))), this.menuOpen && (h("div", { key: '18891a0da434a7f50b6050806c2953e5e5e21484', class: "bs-chatbot-response-action__menu-popup" }, h("slot", { key: '288b3280f09def47f5065b4e85e91142ddd3d2a6', name: "menu" })))), hasSources && (h("button", { key: 'cfa661c9c2096975d250aa3a0b28b36ff3a397a1', type: "button", part: "sources", class: "bs-chatbot-response-action__sources", onClick: this.onSourcesClick }, h("slot", { key: 'a7b610479f2efaded9714c2ad75cd1fd168df55b', name: "sources-icon" }, h(SourcesIcon, { key: '49b12b3ab6011131147909158ec88518a86ce706' })), h("span", { key: 'f063468b3e386f1b88d41a668ac470a601322b51', class: "bs-chatbot-response-action__sources-label" }, this.sourcesCount, " ", this.sourcesCount === 1 ? 'source' : 'sources')))));
108
+ return (h("div", { key: '580d2b0a324ade66bf1ae17b03634bf6d746b2c0', class: "bs-chatbot-response-action" }, h("button", { key: 'a623ca741c690bf3746974d7bac37efa43e5e754', type: "button", part: "like", class: "bs-chatbot-response-action__button", "aria-label": "Like", onClick: this.onLikeClick }, h("slot", { key: '22fd030c4828821509e5e5be53fd790d4bb98aa4', name: "like-icon" }, h(ThumbsUpIcon, { key: '2a1f8e1ad10f1d3c5f76de648b9a423c463677e1' }))), h("button", { key: 'f980f7d1f3df9aefa3a125563fe555132497e390', type: "button", part: "dislike", class: "bs-chatbot-response-action__button", "aria-label": "Dislike", onClick: this.onDislikeClick }, h("slot", { key: 'ceb6da0a1809e8fc2f889c3ad53cc2a480fecef5', name: "dislike-icon" }, h(ThumbsDownIcon, { key: '6b45b16449372ddc167e976791fbd0b5d7c7b897' }))), h("button", { key: 'e46e67457ee5f0f011e1f71de30c05a2ebcccd87', type: "button", part: "copy", class: "bs-chatbot-response-action__button", "aria-label": "Copy", onClick: this.onCopyClick }, h("slot", { key: 'f6173acf30c68012a2c99edc84b28955bc275d6a', name: "copy-icon" }, h(CopyIcon, { key: 'f1ca6efa24d6b65a270244a37978a0c791c2e897' }))), h("button", { key: '40a809efd3bf8d9fa1fc1975082e9d8a5dbfd55a', type: "button", part: "regenerate", class: "bs-chatbot-response-action__button", "aria-label": "Regenerate", onClick: this.onRegenerateClick }, h("slot", { key: 'b85d02a27fe9ba9f10e008255139a2f6221530c8', name: "regenerate-icon" }, h(RegenerateIcon, { key: '38eb20d38c90462d7b337f5f9b5e1320a8d0382b' }))), h("div", { key: '94d8abdc602ca68fc86b29c3440b5458916a9d5c', class: "bs-chatbot-response-action__menu-wrapper" }, h("button", { key: '9684224b6e5d246011d9b07dd6a7b7e09dc38c01', type: "button", part: "menu", class: "bs-chatbot-response-action__button", "aria-label": "More options", "aria-haspopup": "menu", "aria-expanded": this.menuOpen ? 'true' : 'false', onClick: this.onMenuClick }, h("slot", { key: '2bff489b2b6df0cb072b57335e73b6b17be19b0b', name: "menu-icon" }, h(MenuIcon, { key: 'f65fd8348f6b426a0230e5248d0ecc0707169d59' }))), this.menuOpen && (h("div", { key: '8ed1d83ed4e2fcc69a638dbe8579d4e77b8a70ff', class: "bs-chatbot-response-action__menu-popup" }, h("slot", { key: '7d61fb81284b9351a7beb876052024b4a8b58094', name: "menu" })))), hasSources && (h("button", { key: '8c858a645dbdaefafb27b0b799fbb64892aaae40', type: "button", part: "sources", class: "bs-chatbot-response-action__sources", onClick: this.onSourcesClick }, h("slot", { key: '3a9d96c3adae31db76a29d06f0a2467cd479055a', name: "sources-icon" }, h(SourcesIcon, { key: '12503e29497a08a5aaf1448d02791e735f274d09' })), h("span", { key: 'cb5a49e1892a5f5f1cfb92095514ea3b1da59c5f', class: "bs-chatbot-response-action__sources-label" }, this.sourcesCount, " ", this.sourcesCount === 1 ? 'source' : 'sources')))));
109
109
  }
110
110
  static get is() { return "bs-chatbot-response-action"; }
111
111
  static get encapsulation() { return "shadow"; }
@@ -57,7 +57,7 @@ export class BsChatbotSourcesDrawer {
57
57
  };
58
58
  render() {
59
59
  const displayCount = this.count ?? this.slottedCount;
60
- return (h("div", { key: '124930cad859a46032214d2025a6d0dc0601bd7e', class: "bs-chatbot-sources-drawer", role: "region", "aria-label": this.heading }, h("div", { key: 'edac48bbd2236fc8382595d0bb3fab2029d5471b', class: "bs-chatbot-sources-drawer__header" }, h("div", { key: '67c816b8604c8a7bdba2939162d9296f4213d8d0', class: "bs-chatbot-sources-drawer__title-group" }, h("h2", { key: '998b580f25800c4d25a288c60d78313ebb18ba96', part: "heading", class: "bs-chatbot-sources-drawer__heading" }, this.heading), displayCount > 0 && (h("span", { key: '7f04669f0c8a4df3383f4d37502620cee268249e', part: "count", class: "bs-chatbot-sources-drawer__count" }, displayCount))), h("button", { key: '3e672753a8eb658b5f63878411f4e98b8f0e9aeb', type: "button", part: "collapse", class: "bs-chatbot-sources-drawer__collapse", "aria-label": "Collapse sources", onClick: this.onCollapseClick }, h("slot", { key: '032e644f385e92882837386e02fcf04f4d4f7818', name: "collapse-icon" }, h(CaretRightIcon, { key: 'eed919d8fac971c37c45dbee46c3395e36dbd436' })))), h("div", { key: '7901e0b8eb2c904a779735dc613040bab1cec881', part: "list", class: "bs-chatbot-sources-drawer__list" }, h("slot", { key: '511ac408aae665461103da7b88664636a773f043', onSlotchange: this.onSlotchange }))));
60
+ return (h("div", { key: 'cc40e8f428b35477177886763cabe96a2414a2b6', class: "bs-chatbot-sources-drawer", role: "region", "aria-label": this.heading }, h("div", { key: '13c34662ec82fcf7537564d12be6a7c4681631c4', class: "bs-chatbot-sources-drawer__header" }, h("div", { key: '8d82c247489192a70dd23cf7e35f08d59048e05a', class: "bs-chatbot-sources-drawer__title-group" }, h("h2", { key: 'b4eb825451c43a835b050edaf5ad559234a75124', part: "heading", class: "bs-chatbot-sources-drawer__heading" }, this.heading), displayCount > 0 && (h("span", { key: 'b6503e6525dd9a3464ad7cbab2225009392ce4cc', part: "count", class: "bs-chatbot-sources-drawer__count" }, displayCount))), h("button", { key: '26059a165b9e8a45b1902bb82e89c2fd693dfc85', type: "button", part: "collapse", class: "bs-chatbot-sources-drawer__collapse", "aria-label": "Collapse sources", onClick: this.onCollapseClick }, h("slot", { key: '720e1f4baff8d3764163934134966fd25aea0097', name: "collapse-icon" }, h(CaretRightIcon, { key: '779d738da467eb380801263bcffc1935ae5e9875' })))), h("div", { key: '29279a88a3f3b76c7418522af48b4bd9e652e453', part: "list", class: "bs-chatbot-sources-drawer__list" }, h("slot", { key: '85d006ab484a4427e363647b290d7a8e9229c7c6', onSlotchange: this.onSlotchange }))));
61
61
  }
62
62
  static get is() { return "bs-chatbot-sources-drawer"; }
63
63
  static get encapsulation() { return "shadow"; }
@@ -45,7 +45,7 @@ export class BsChatbotSuggestionButton {
45
45
  this.bsSelect.emit();
46
46
  };
47
47
  render() {
48
- return (h("button", { key: 'ae54b5a19bb4ad4d215c34df56018f49fd1907a9', type: "button", part: "button", class: "bs-chatbot-suggestion-button", disabled: this.disabled, onClick: this.onClick }, h("slot", { key: '950eefcbff22f6f09547b805bb72b6bc7599d060' })));
48
+ return (h("button", { key: '3eed6a4eb359433fe682984f4628bdc46045f910', type: "button", part: "button", class: "bs-chatbot-suggestion-button", disabled: this.disabled, onClick: this.onClick }, h("slot", { key: '2e5bdcd63ed01ca0235bd6f74fa40a0e9e835863' })));
49
49
  }
50
50
  static get is() { return "bs-chatbot-suggestion-button"; }
51
51
  static get encapsulation() { return "shadow"; }
@@ -33,7 +33,7 @@ export class BsCheckboxSkeleton {
33
33
  * (unusual) default `bs-checkbox` itself uses. */
34
34
  size = 'lg';
35
35
  render() {
36
- return (h("span", { key: '8797658f07231a6da75540109d31a0046e87391e', part: "container", class: "bs-checkbox-skeleton", role: "status", "aria-label": "Loading" }, h("span", { key: '5476c7e0b6fbfa41c2b9aabe64452665841c516d', part: "box", class: `bs-checkbox-skeleton__box bs-checkbox-skeleton__box--${this.size}` }), h("span", { key: 'f0d9bf8dee42ce498f8f627ed5a536e0ec8029c4', part: "label", class: "bs-checkbox-skeleton__label" })));
36
+ return (h("span", { key: '062c3abd38665132c305089335d3eb993c910061', part: "container", class: "bs-checkbox-skeleton", role: "status", "aria-label": "Loading" }, h("span", { key: 'bc682511b4e316e13460186388e13aa09b77b139', part: "box", class: `bs-checkbox-skeleton__box bs-checkbox-skeleton__box--${this.size}` }), h("span", { key: '20dc4a2ebff2e678a990cb244359bec2f515d742', part: "label", class: "bs-checkbox-skeleton__label" })));
37
37
  }
38
38
  static get is() { return "bs-checkbox-skeleton"; }
39
39
  static get encapsulation() { return "shadow"; }
@@ -99,6 +99,25 @@ describe('bs-data-table', () => {
99
99
  expect(cell.querySelector('img')).toBeNull();
100
100
  expect(cell.textContent).toBe('<img src=x onerror="window.__xss = true">');
101
101
  });
102
+ it('renders rich cell content: a link and a web component become real elements', async () => {
103
+ const { root, waitForChanges } = await render(h("bs-data-table", { columns: columns, rows: rows, cellRenderer: (row) => `<a href="/users/${row.id}">Open</a><bs-badge variant="success">Active</bs-badge>` }));
104
+ await waitForChanges();
105
+ const cell = root.shadowRoot.querySelector('tbody td');
106
+ expect(cell.querySelector('a')).toEqualAttribute('href', '/users/1');
107
+ const badge = cell.querySelector('bs-badge');
108
+ expect(badge).not.toBeNull();
109
+ expect(badge.shadowRoot).not.toBeNull(); // upgraded: it is a live component, not inert text
110
+ });
111
+ it('applies cellRenderer to every cell, and does NOT escape what it returns (the caller must)', async () => {
112
+ const unsafeRows = [{ id: 1, name: '<img src=x onerror="window.__xss2 = true">', status: 'Active' }];
113
+ const unsafe = await render(h("bs-data-table", { columns: columns, rows: unsafeRows, cellRenderer: (row, col) => String(row[col.key]) }));
114
+ expect(unsafe.root.shadowRoot.querySelector('tbody td').querySelector('img')).not.toBeNull();
115
+ const esc = (v) => String(v).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
116
+ const safe = await render(h("bs-data-table", { columns: columns, rows: unsafeRows, cellRenderer: (row, col) => esc(row[col.key]) }));
117
+ const cell = safe.root.shadowRoot.querySelector('tbody td');
118
+ expect(cell.querySelector('img')).toBeNull();
119
+ expect(cell.textContent).toBe('<img src=x onerror="window.__xss2 = true">');
120
+ });
102
121
  it('still allows cellRenderer to render trusted custom HTML when explicitly supplied', async () => {
103
122
  const { root } = await render(h("bs-data-table", { columns: columns, rows: rows, cellRenderer: () => '<button type="button">Edit</button>' }));
104
123
  const cell = root.shadowRoot.querySelector('tbody td');
@@ -29,8 +29,22 @@ export class BsDataTable {
29
29
  sortColumn;
30
30
  sortDirection = 'asc';
31
31
  selectable = false;
32
- /** Optional custom cell renderer, e.g. for an actions column. Function props are JS-property-only,
33
- * same as array props -- there is no attribute equivalent. Falls back to the raw cell value. */
32
+ /** Optional custom cell renderer, for rich cells such as a status badge, a link or an actions
33
+ * column. It returns an **HTML string** that is inserted into the cell as real markup
34
+ * (`innerHTML`), so links, buttons, `<img>` and other web components (e.g. `<bs-badge>`) all render.
35
+ *
36
+ * **When set it renders every cell, not just the special ones** -- return the plain value for
37
+ * ordinary columns too. And **the returned string is not escaped or sanitized** (same trust
38
+ * model as React's `dangerouslySetInnerHTML`): escape any untrusted row data you put in it, plain
39
+ * columns included, or it will be interpreted as HTML. Without a `cellRenderer`, values are
40
+ * rendered as escaped text.
41
+ *
42
+ * The markup is static HTML -- attach behavior yourself, e.g. one `click` listener on the table
43
+ * that reads `event.composedPath()[0]` (events from inside the shadow root are retargeted to the
44
+ * table itself).
45
+ *
46
+ * Function props are JS-property-only, same as array props -- there is no attribute equivalent.
47
+ * Falls back to the raw cell value (as escaped text) when unset. */
34
48
  cellRenderer;
35
49
  bsSort;
36
50
  bsRowSelect;
@@ -71,11 +85,11 @@ export class BsDataTable {
71
85
  }
72
86
  }
73
87
  render() {
74
- return (h("table", { key: 'a1a9d0c92eb2af12f8f9cab9ba4ae0994a10958a', part: "table", class: "bs-data-table" }, h("thead", { key: '2b51500b221465d750b55c31e00af94684367eb2', part: "head" }, h("tr", { key: '57aa25ca9b255213f155b5b30841e284d1eebaf8' }, this.selectable && (h("th", { key: '44b709a7cd1f2072934efe8fef44a63b2ed20d62', class: "bs-data-table__select-col" }, this.rows.length > 0 && (h("input", { key: '084d099453c53333d77c60786b7eb6bedfa21ef3', type: "checkbox", "aria-label": "Select all rows", checked: this.selectedIds.size > 0 && this.selectedIds.size === this.rows.length, indeterminate: this.selectedIds.size > 0 && this.selectedIds.size < this.rows.length, onChange: this.onSelectAllChange })))), this.columns.map(column => {
88
+ return (h("table", { key: '7ea79a70148513512cdba2f8cb8d5a5c7ae09706', part: "table", class: "bs-data-table" }, h("thead", { key: 'e9cf73cbba196e9caf4b03a837866a5370c13e19', part: "head" }, h("tr", { key: 'a33fbd57f1925e34c6e7ce292b6945d2ee5c6113' }, this.selectable && (h("th", { key: '876948b66e54ae68d6aa6148c962781223fa0441', class: "bs-data-table__select-col" }, this.rows.length > 0 && (h("input", { key: 'cf8f26258bcee486190e3ac4b3d5ce4715784d5f', type: "checkbox", "aria-label": "Select all rows", checked: this.selectedIds.size > 0 && this.selectedIds.size === this.rows.length, indeterminate: this.selectedIds.size > 0 && this.selectedIds.size < this.rows.length, onChange: this.onSelectAllChange })))), this.columns.map(column => {
75
89
  const isSorted = this.sortColumn === column.key;
76
90
  const ariaSort = column.sortable ? (isSorted ? (this.sortDirection === 'asc' ? 'ascending' : 'descending') : 'none') : undefined;
77
91
  return (h("th", { part: "header-cell", class: "bs-data-table__header-cell", "aria-sort": ariaSort }, column.sortable ? (h("button", { type: "button", class: "bs-data-table__sort-button", onClick: () => this.onHeaderClick(column) }, column.label, isSorted && h("span", { class: "bs-data-table__sort-indicator" }, this.sortDirection === 'asc' ? ' ▲' : ' ▼'))) : (column.label)));
78
- }))), h("tbody", { key: '0438897652b9f8909ff67ae9f7b22d0950f1e5e8', part: "body" }, this.rows.map(row => (h("tr", { part: "row", class: "bs-data-table__row" }, this.selectable && (h("td", { class: "bs-data-table__select-col" }, h("input", { type: "checkbox", checked: this.selectedIds.has(row.id), onChange: ev => this.onRowCheckboxChange(row, ev) }))), this.columns.map(column => this.cellRenderer ? (
92
+ }))), h("tbody", { key: '3a0c2ce6fea27797b855ee2b3c724d0e99471d28', part: "body" }, this.rows.map(row => (h("tr", { part: "row", class: "bs-data-table__row" }, this.selectable && (h("td", { class: "bs-data-table__select-col" }, h("input", { type: "checkbox", checked: this.selectedIds.has(row.id), onChange: ev => this.onRowCheckboxChange(row, ev) }))), this.columns.map(column => this.cellRenderer ? (
79
93
  // cellRenderer is a developer-supplied, trusted function (JS-property-only, never
80
94
  // derived from untrusted input) -- its HTML output is intentionally not escaped,
81
95
  // same trust model as React's dangerouslySetInnerHTML.
@@ -228,7 +242,7 @@ export class BsDataTable {
228
242
  "optional": true,
229
243
  "docs": {
230
244
  "tags": [],
231
- "text": "Optional custom cell renderer, e.g. for an actions column. Function props are JS-property-only,\nsame as array props -- there is no attribute equivalent. Falls back to the raw cell value."
245
+ "text": "Optional custom cell renderer, for rich cells such as a status badge, a link or an actions\ncolumn. It returns an **HTML string** that is inserted into the cell as real markup\n(`innerHTML`), so links, buttons, `<img>` and other web components (e.g. `<bs-badge>`) all render.\n\n**When set it renders every cell, not just the special ones** -- return the plain value for\nordinary columns too. And **the returned string is not escaped or sanitized** (same trust\nmodel as React's `dangerouslySetInnerHTML`): escape any untrusted row data you put in it, plain\ncolumns included, or it will be interpreted as HTML. Without a `cellRenderer`, values are\nrendered as escaped text.\n\nThe markup is static HTML -- attach behavior yourself, e.g. one `click` listener on the table\nthat reads `event.composedPath()[0]` (events from inside the shadow root are retargeted to the\ntable itself).\n\nFunction props are JS-property-only, same as array props -- there is no attribute equivalent.\nFalls back to the raw cell value (as escaped text) when unset."
232
246
  },
233
247
  "getter": false,
234
248
  "setter": false
@@ -44,11 +44,18 @@ export const WithCustomCellRenderer = {
44
44
  const table = document.createElement('bs-data-table');
45
45
  table.columns = [...columns, { key: 'actions', label: 'Actions' }];
46
46
  table.rows = rows;
47
+ // cellRenderer renders EVERY cell and its output is not escaped, so plain columns are escaped
48
+ // here too -- copying `String(row[column.key])` unescaped would make untrusted row data HTML.
49
+ const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
47
50
  table.cellRenderer = (row, column) => {
48
51
  if (column.key === 'actions') {
49
52
  return `<button type="button">Edit</button>`;
50
53
  }
51
- return String(row[column.key] ?? '');
54
+ if (column.key === 'status') {
55
+ // Rich content: a web component in the cell.
56
+ return `<bs-badge variant="${row.status === 'Active' ? 'success' : 'neutral'}">${escapeHtml(row.status)}</bs-badge>`;
57
+ }
58
+ return escapeHtml(row[column.key]);
52
59
  };
53
60
  return table;
54
61
  },
@@ -0,0 +1,182 @@
1
+ import { render, h, describe, it, expect } from "@stencil/vitest";
2
+ const days = (root) => Array.from(root.shadowRoot.querySelectorAll('[part~="day"]'));
3
+ const day = (root, iso) => root.shadowRoot.querySelector(`[data-date="${iso}"]`);
4
+ describe('bs-datepicker', () => {
5
+ it('renders a 6-row grid starting on Sunday, matching the Figma (September 2026)', async () => {
6
+ const { root } = await render(h("bs-datepicker", { value: "2026-09-16" }));
7
+ const cells = days(root);
8
+ expect(cells.length).toBe(42);
9
+ // Sept 1 2026 is a Tuesday, so the grid leads with Sunday 30 August.
10
+ expect(cells[0].dataset.date).toBe('2026-08-30');
11
+ expect(cells[41].dataset.date).toBe('2026-10-10');
12
+ const weekdays = Array.from(root.shadowRoot.querySelectorAll('[part="weekday"]')).map(e => e.textContent);
13
+ expect(weekdays).toEqual(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']);
14
+ });
15
+ it('can start the week on Monday', async () => {
16
+ const { root } = await render(h("bs-datepicker", { value: "2026-09-16", firstDayOfWeek: 1 }));
17
+ expect(days(root)[0].dataset.date).toBe('2026-08-31');
18
+ expect(root.shadowRoot.querySelector('[part="weekday"]').textContent).toBe('Mon');
19
+ });
20
+ it('shows the selected date in the title and marks it selected', async () => {
21
+ const { root } = await render(h("bs-datepicker", { value: "2026-09-16" }));
22
+ expect(root.shadowRoot.querySelector('[part="title"]').textContent).toBe('16 September 2026');
23
+ expect(day(root, '2026-09-16').getAttribute('part')).toContain('selected');
24
+ });
25
+ it('marks days from adjacent months as outside', async () => {
26
+ const { root } = await render(h("bs-datepicker", { value: "2026-09-16" }));
27
+ expect(day(root, '2026-08-31').getAttribute('part')).toContain('outside');
28
+ expect(day(root, '2026-09-01').getAttribute('part')).not.toContain('outside');
29
+ });
30
+ it('selects a day on click, updates value and emits bsChange', async () => {
31
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
32
+ const events = [];
33
+ root.addEventListener('bsChange', (e) => events.push(e.detail));
34
+ day(root, '2026-09-20').click();
35
+ await waitForChanges();
36
+ expect(root.value).toBe('2026-09-20');
37
+ expect(events).toEqual([{ value: '2026-09-20' }]);
38
+ });
39
+ it('does not emit when value is set programmatically, and jumps to that month', async () => {
40
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
41
+ let fired = 0;
42
+ root.addEventListener('bsChange', () => fired++);
43
+ root.value = '2027-01-05';
44
+ await waitForChanges();
45
+ expect(fired).toBe(0);
46
+ expect(day(root, '2027-01-05').getAttribute('part')).toContain('selected');
47
+ });
48
+ it('navigates months with the prev/next buttons', async () => {
49
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
50
+ root.shadowRoot.querySelector('[part="next"]').click();
51
+ await waitForChanges();
52
+ expect(day(root, '2026-10-15')).not.toBeNull();
53
+ expect(day(root, '2026-10-15').getAttribute('part')).not.toContain('outside');
54
+ root.shadowRoot.querySelector('[part="prev"]').click();
55
+ root.shadowRoot.querySelector('[part="prev"]').click();
56
+ await waitForChanges();
57
+ expect(day(root, '2026-08-15').getAttribute('part')).not.toContain('outside');
58
+ });
59
+ it('disables days outside min/max and ignores clicks on them', async () => {
60
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16", min: "2026-09-10", max: "2026-09-20" }));
61
+ expect(day(root, '2026-09-09')).toHaveAttribute('disabled');
62
+ expect(day(root, '2026-09-21')).toHaveAttribute('disabled');
63
+ expect(day(root, '2026-09-10')).not.toHaveAttribute('disabled');
64
+ day(root, '2026-09-09').click();
65
+ await waitForChanges();
66
+ expect(root.value).toBe('2026-09-16');
67
+ });
68
+ it('keeps a single tab stop (roving tabindex) and moves focus with the arrow keys', async () => {
69
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
70
+ expect(days(root).filter(d => d.tabIndex === 0).map(d => d.dataset.date)).toEqual(['2026-09-16']);
71
+ day(root, '2026-09-16').dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
72
+ await waitForChanges();
73
+ expect(days(root).filter(d => d.tabIndex === 0).map(d => d.dataset.date)).toEqual(['2026-09-23']);
74
+ });
75
+ it('PageDown moves a month and clamps the day (Jan 31 -> Feb 28)', async () => {
76
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2027-01-31" }));
77
+ day(root, '2027-01-31').dispatchEvent(new KeyboardEvent('keydown', { key: 'PageDown', bubbles: true }));
78
+ await waitForChanges();
79
+ expect(days(root).filter(d => d.tabIndex === 0).map(d => d.dataset.date)).toEqual(['2027-02-28']);
80
+ });
81
+ describe('month/year dropdowns', () => {
82
+ const triggers = (root) => root.shadowRoot.querySelectorAll('bs-input');
83
+ const items = (root) => Array.from(root.shadowRoot.querySelectorAll('[part="menu"] bs-menu-item'));
84
+ it('renders a dropdown trigger per field showing the current month and year', async () => {
85
+ const { root } = await render(h("bs-datepicker", { value: "2026-09-16" }));
86
+ const t = triggers(root);
87
+ expect(t.length).toBe(2);
88
+ expect(t[0].type).toBe('dropdown');
89
+ expect(t[0].value).toBe('September');
90
+ expect(t[1].value).toBe('2026');
91
+ expect(root.shadowRoot.querySelector('[part="menu"]')).toBeNull();
92
+ });
93
+ it('opens a list of all 12 months with a checkmark on the current one', async () => {
94
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
95
+ triggers(root)[0].dispatchEvent(new CustomEvent('bsOpen', { detail: true }));
96
+ await waitForChanges();
97
+ const list = items(root);
98
+ expect(list.length).toBe(12);
99
+ const checked = list.filter(i => i.querySelector('svg'));
100
+ expect(checked.length).toBe(1);
101
+ expect(checked[0].textContent).toBe('September');
102
+ });
103
+ it('picking a month changes the displayed month and closes the list', async () => {
104
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
105
+ triggers(root)[0].dispatchEvent(new CustomEvent('bsOpen', { detail: true }));
106
+ await waitForChanges();
107
+ items(root)[5].dispatchEvent(new CustomEvent('bsSelect'));
108
+ await waitForChanges();
109
+ expect(root.shadowRoot.querySelector('[part="menu"]')).toBeNull();
110
+ expect(triggers(root)[0].value).toBe('June');
111
+ expect(day(root, '2026-06-15').getAttribute('part')).not.toContain('outside');
112
+ // Browsing doesn't change the selection.
113
+ expect(root.value).toBe('2026-09-16');
114
+ });
115
+ it('picking a year changes the displayed year', async () => {
116
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
117
+ triggers(root)[1].dispatchEvent(new CustomEvent('bsOpen', { detail: true }));
118
+ await waitForChanges();
119
+ const target = items(root).find(i => i.textContent === '2028');
120
+ target.dispatchEvent(new CustomEvent('bsSelect'));
121
+ await waitForChanges();
122
+ expect(triggers(root)[1].value).toBe('2028');
123
+ expect(day(root, '2028-09-15')).not.toBeNull();
124
+ });
125
+ it('limits the year list to min/max', async () => {
126
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16", min: "2025-01-01", max: "2027-12-31" }));
127
+ triggers(root)[1].dispatchEvent(new CustomEvent('bsOpen', { detail: true }));
128
+ await waitForChanges();
129
+ expect(items(root).map(i => i.textContent)).toEqual(['2025', '2026', '2027']);
130
+ });
131
+ it('closes on Escape', async () => {
132
+ const { root, waitForChanges } = await render(h("bs-datepicker", { value: "2026-09-16" }));
133
+ triggers(root)[0].dispatchEvent(new CustomEvent('bsOpen', { detail: true }));
134
+ await waitForChanges();
135
+ root.shadowRoot.querySelector('.bs-datepicker__dropdown').dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
136
+ await waitForChanges();
137
+ expect(root.shadowRoot.querySelector('[part="menu"]')).toBeNull();
138
+ });
139
+ });
140
+ describe('range', () => {
141
+ it('renders start/end fields instead of dropdowns, and highlights the days between', async () => {
142
+ const { root } = await render(h("bs-datepicker", { range: true, start: "2026-09-11", end: "2026-09-16" }));
143
+ const fields = root.shadowRoot.querySelectorAll('bs-input');
144
+ expect(fields.length).toBe(2);
145
+ expect(fields[0].label).toBe('Start date');
146
+ expect(fields[0].value).toBe('11 Sept 2026');
147
+ const inRange = Array.from(root.shadowRoot.querySelectorAll('[part~="in-range"]')).map(e => e.dataset.date);
148
+ expect(inRange).toEqual(['2026-09-12', '2026-09-13', '2026-09-14', '2026-09-15']);
149
+ expect(day(root, '2026-09-11').getAttribute('part')).toContain('selected');
150
+ expect(day(root, '2026-09-16').getAttribute('part')).toContain('selected');
151
+ });
152
+ it('two clicks pick a range: first emits an open range, second completes it', async () => {
153
+ const { root, waitForChanges } = await render(h("bs-datepicker", { range: true, start: "2026-09-01", end: "2026-09-02" }));
154
+ const events = [];
155
+ root.addEventListener('bsChange', (e) => events.push(e.detail));
156
+ day(root, '2026-09-10').click();
157
+ await waitForChanges();
158
+ day(root, '2026-09-15').click();
159
+ await waitForChanges();
160
+ expect(events).toEqual([
161
+ { start: '2026-09-10', end: '' },
162
+ { start: '2026-09-10', end: '2026-09-15' },
163
+ ]);
164
+ expect(root.start).toBe('2026-09-10');
165
+ expect(root.end).toBe('2026-09-15');
166
+ });
167
+ it('a click before the start restarts the range instead of reversing it', async () => {
168
+ const { root, waitForChanges } = await render(h("bs-datepicker", { range: true, start: "2026-09-11" }));
169
+ day(root, '2026-09-05').click();
170
+ await waitForChanges();
171
+ expect(root.start).toBe('2026-09-05');
172
+ expect(root.end).toBe('');
173
+ });
174
+ it('a click after a completed range starts a new one', async () => {
175
+ const { root, waitForChanges } = await render(h("bs-datepicker", { range: true, start: "2026-09-11", end: "2026-09-16" }));
176
+ day(root, '2026-09-20').click();
177
+ await waitForChanges();
178
+ expect(root.start).toBe('2026-09-20');
179
+ expect(root.end).toBe('');
180
+ });
181
+ });
182
+ });