@adyen/bento-mcp 0.0.0 → 0.1.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 (410) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -0
  4. package/dist/assets/components/accordion/accordion.docs.mdx +77 -0
  5. package/dist/assets/components/accordion/accordion.stories.ts +1 -0
  6. package/dist/assets/components/accordion/accordion.vue +1 -0
  7. package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -0
  8. package/dist/assets/components/accordion/components/accordion-item.vue +1 -0
  9. package/dist/assets/components/action-bar/action-bar.docs.mdx +103 -0
  10. package/dist/assets/components/action-bar/action-bar.stories.ts +1 -0
  11. package/dist/assets/components/action-bar/action-bar.vue +1 -0
  12. package/dist/assets/components/alert/alert.docs.mdx +106 -0
  13. package/dist/assets/components/alert/alert.stories.ts +1 -0
  14. package/dist/assets/components/alert/alert.vue +1 -0
  15. package/dist/assets/components/avatar/avatar.docs.mdx +44 -0
  16. package/dist/assets/components/avatar/avatar.stories.ts +1 -0
  17. package/dist/assets/components/avatar/avatar.vue +1 -0
  18. package/dist/assets/components/avatar/components/avatar-image/avatar-image.vue +1 -0
  19. package/dist/assets/components/button/button.docs.mdx +137 -0
  20. package/dist/assets/components/button/button.stories.ts +1 -0
  21. package/dist/assets/components/button/button.vue +1 -0
  22. package/dist/assets/components/button/components/ai-button/ai-button.docs.mdx +40 -0
  23. package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -0
  24. package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -0
  25. package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +169 -0
  26. package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -0
  27. package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -0
  28. package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +106 -0
  29. package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -0
  30. package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -0
  31. package/dist/assets/components/button/components/toggle-button/toggle-button.docs.mdx +81 -0
  32. package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -0
  33. package/dist/assets/components/button/components/toggle-button/toggle-button.vue +1 -0
  34. package/dist/assets/components/card/card.docs.mdx +166 -0
  35. package/dist/assets/components/card/card.stories.ts +1 -0
  36. package/dist/assets/components/card/card.vue +1 -0
  37. package/dist/assets/components/checkbox/checkbox.docs.mdx +116 -0
  38. package/dist/assets/components/checkbox/checkbox.stories.ts +1 -0
  39. package/dist/assets/components/checkbox/checkbox.vue +1 -0
  40. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +134 -0
  41. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -0
  42. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -0
  43. package/dist/assets/components/chip/chip.docs.mdx +51 -0
  44. package/dist/assets/components/chip/chip.stories.ts +1 -0
  45. package/dist/assets/components/chip/chip.vue +1 -0
  46. package/dist/assets/components/click-outside/click-outside.docs.mdx +30 -0
  47. package/dist/assets/components/click-outside/click-outside.stories.ts +1 -0
  48. package/dist/assets/components/code-snippet/code-snippet.docs.mdx +73 -0
  49. package/dist/assets/components/code-snippet/code-snippet.stories.ts +1 -0
  50. package/dist/assets/components/code-snippet/code-snippet.vue +1 -0
  51. package/dist/assets/components/country/country.docs.mdx +61 -0
  52. package/dist/assets/components/country/country.stories.ts +1 -0
  53. package/dist/assets/components/country/country.vue +1 -0
  54. package/dist/assets/components/currency/currency.docs.mdx +38 -0
  55. package/dist/assets/components/currency/currency.stories.ts +1 -0
  56. package/dist/assets/components/currency/currency.vue +1 -0
  57. package/dist/assets/components/dashed-underline/dashed-underline.docs.mdx +57 -0
  58. package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -0
  59. package/dist/assets/components/dashed-underline/dashed-underline.vue +1 -0
  60. package/dist/assets/components/data-grid/components/data-grid-cell/data-grid-cell.vue +1 -0
  61. package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -0
  62. package/dist/assets/components/data-grid/components/data-grid-columns/data-grid-columns.vue +1 -0
  63. package/dist/assets/components/data-grid/components/data-grid-config-settings/components/data-grid-config-settings-column-row/data-grid-config-settings-column-row.vue +1 -0
  64. package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -0
  65. package/dist/assets/components/data-grid/components/data-grid-lazy-load/data-grid-lazy-load.vue +1 -0
  66. package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -0
  67. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.docs.mdx +61 -0
  68. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -0
  69. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.vue +1 -0
  70. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.docs.mdx +56 -0
  71. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -0
  72. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -0
  73. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.docs.mdx +48 -0
  74. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -0
  75. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -0
  76. package/dist/assets/components/data-grid/data-grid.docs.mdx +951 -0
  77. package/dist/assets/components/data-grid/data-grid.stories.ts +1 -0
  78. package/dist/assets/components/data-grid/data-grid.vue +1 -0
  79. package/dist/assets/components/date/date.docs.mdx +49 -0
  80. package/dist/assets/components/date/date.stories.ts +1 -0
  81. package/dist/assets/components/date/date.vue +1 -0
  82. package/dist/assets/components/date-picker/date-picker.docs.mdx +69 -0
  83. package/dist/assets/components/date-picker/date-picker.stories.ts +1 -0
  84. package/dist/assets/components/date-picker/date-picker.vue +1 -0
  85. package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -0
  86. package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +168 -0
  87. package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -0
  88. package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -0
  89. package/dist/assets/components/divider/divider.docs.mdx +27 -0
  90. package/dist/assets/components/divider/divider.stories.ts +1 -0
  91. package/dist/assets/components/divider/divider.vue +1 -0
  92. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -0
  93. package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -0
  94. package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -0
  95. package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -0
  96. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -0
  97. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -0
  98. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -0
  99. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -0
  100. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -0
  101. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -0
  102. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -0
  103. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -0
  104. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -0
  105. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -0
  106. package/dist/assets/components/dropdown/dropdown.docs.mdx +420 -0
  107. package/dist/assets/components/dropdown/dropdown.stories.ts +1 -0
  108. package/dist/assets/components/dropdown/dropdown.vue +1 -0
  109. package/dist/assets/components/empty-state/empty-state.docs.mdx +115 -0
  110. package/dist/assets/components/empty-state/empty-state.stories.ts +1 -0
  111. package/dist/assets/components/empty-state/empty-state.vue +1 -0
  112. package/dist/assets/components/file-uploader/components/file-uploader-file-card/components/file-uploader-file-card-animation/file-uploader-file-card-animation.vue +1 -0
  113. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -0
  114. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -0
  115. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.stories.ts +1 -0
  116. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -0
  117. package/dist/assets/components/file-uploader/file-uploader.docs.mdx +117 -0
  118. package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -0
  119. package/dist/assets/components/file-uploader/file-uploader.vue +1 -0
  120. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -0
  121. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -0
  122. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-button/all-filters-modal-button.vue +1 -0
  123. package/dist/assets/components/filter-bar/components/base-filter/base-filter.docs.mdx +90 -0
  124. package/dist/assets/components/filter-bar/components/base-filter/base-filter.stories.ts +1 -0
  125. package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -0
  126. package/dist/assets/components/filter-bar/components/base-filter-button/base-filter-button.vue +1 -0
  127. package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -0
  128. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.stories.ts +1 -0
  129. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.vue +1 -0
  130. package/dist/assets/components/filter-bar/components/date-filter/date-filter.stories.ts +1 -0
  131. package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -0
  132. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.stories.ts +1 -0
  133. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -0
  134. package/dist/assets/components/filter-bar/components/filter-bar-button/filter-bar-button.vue +1 -0
  135. package/dist/assets/components/filter-bar/components/filter-bar-button-group/filter-bar-button-group.vue +1 -0
  136. package/dist/assets/components/filter-bar/components/input-filter/input-filter.docs.mdx +18 -0
  137. package/dist/assets/components/filter-bar/components/input-filter/input-filter.stories.ts +1 -0
  138. package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -0
  139. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.docs.mdx +18 -0
  140. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -0
  141. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -0
  142. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.stories.ts +1 -0
  143. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.vue +1 -0
  144. package/dist/assets/components/filter-bar/components/range-filter/range-filter.vue +1 -0
  145. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -0
  146. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-dropdown/select-filter-dropdown.vue +1 -0
  147. package/dist/assets/components/filter-bar/components/select-filter/select-filter.stories.ts +1 -0
  148. package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -0
  149. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -0
  150. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.vue +1 -0
  151. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -0
  152. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -0
  153. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.stories.ts +1 -0
  154. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.vue +1 -0
  155. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.stories.ts +1 -0
  156. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.vue +1 -0
  157. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.stories.ts +1 -0
  158. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.vue +1 -0
  159. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.stories.ts +1 -0
  160. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -0
  161. package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1005 -0
  162. package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -0
  163. package/dist/assets/components/filter-bar/filter-bar.vue +1 -0
  164. package/dist/assets/components/focus-trap/focus-trap.docs.mdx +10 -0
  165. package/dist/assets/components/focus-trap/focus-trap.stories.ts +1 -0
  166. package/dist/assets/components/focus-trap/focus-trap.vue +1 -0
  167. package/dist/assets/components/form-layout/components/form-layout-field-tip/form-layout-field-tip.vue +1 -0
  168. package/dist/assets/components/form-layout/components/form-layout-group/form-layout-group.vue +1 -0
  169. package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -0
  170. package/dist/assets/components/form-layout/form-layout.docs.mdx +452 -0
  171. package/dist/assets/components/form-layout/form-layout.stories.ts +1 -0
  172. package/dist/assets/components/form-layout/form-layout.vue +1 -0
  173. package/dist/assets/components/header/header.docs.mdx +85 -0
  174. package/dist/assets/components/header/header.stories.ts +1 -0
  175. package/dist/assets/components/header/header.vue +1 -0
  176. package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -0
  177. package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -0
  178. package/dist/assets/components/header-with-views/header-with-views.docs.mdx +103 -0
  179. package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -0
  180. package/dist/assets/components/header-with-views/header-with-views.vue +1 -0
  181. package/dist/assets/components/image/image.docs.mdx +34 -0
  182. package/dist/assets/components/image/image.stories.ts +1 -0
  183. package/dist/assets/components/image/image.vue +1 -0
  184. package/dist/assets/components/info-icon/info-icon.docs.mdx +57 -0
  185. package/dist/assets/components/info-icon/info-icon.stories.ts +1 -0
  186. package/dist/assets/components/info-icon/info-icon.vue +1 -0
  187. package/dist/assets/components/input-field/input-field.docs.mdx +141 -0
  188. package/dist/assets/components/input-field/input-field.stories.ts +1 -0
  189. package/dist/assets/components/input-field/input-field.vue +1 -0
  190. package/dist/assets/components/input-field-password/input-field-password.docs.mdx +292 -0
  191. package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -0
  192. package/dist/assets/components/input-field-password/input-field-password.vue +1 -0
  193. package/dist/assets/components/input-field-phone-number/input-field-phone-number.docs.mdx +72 -0
  194. package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -0
  195. package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -0
  196. package/dist/assets/components/inspector/inspector.docs.mdx +118 -0
  197. package/dist/assets/components/inspector/inspector.stories.ts +1 -0
  198. package/dist/assets/components/inspector/inspector.vue +1 -0
  199. package/dist/assets/components/internal/base-button/base-button.vue +1 -0
  200. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +1 -0
  201. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +1 -0
  202. package/dist/assets/components/internal/calendar/calendar.stories.ts +1 -0
  203. package/dist/assets/components/internal/calendar/calendar.vue +1 -0
  204. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +1 -0
  205. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +1 -0
  206. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +1 -0
  207. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +1 -0
  208. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +1 -0
  209. package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +1 -0
  210. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +1 -0
  211. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +1 -0
  212. package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +1 -0
  213. package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +1 -0
  214. package/dist/assets/components/internal/controls-group/controls-group.vue +1 -0
  215. package/dist/assets/components/internal/copy/copy.docs.mdx +43 -0
  216. package/dist/assets/components/internal/copy/copy.stories.ts +1 -0
  217. package/dist/assets/components/internal/copy/copy.vue +1 -0
  218. package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +1 -0
  219. package/dist/assets/components/internal/dialog-page/dialog-page.vue +1 -0
  220. package/dist/assets/components/internal/error-message/error-message.vue +1 -0
  221. package/dist/assets/components/internal/field-label/field-label.vue +1 -0
  222. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +21 -0
  223. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +1 -0
  224. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +1 -0
  225. package/dist/assets/components/internal/footer-actions/footer-actions.vue +1 -0
  226. package/dist/assets/components/internal/grid-layout/grid-layout.vue +1 -0
  227. package/dist/assets/components/internal/header-meta/header-meta.vue +1 -0
  228. package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +1 -0
  229. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-category/listbox-multi-select-category.vue +1 -0
  230. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +1 -0
  231. package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +1 -0
  232. package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +1 -0
  233. package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +1 -0
  234. package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +1 -0
  235. package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +1 -0
  236. package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +1 -0
  237. package/dist/assets/components/internal/listbox/listbox.stories.ts +1 -0
  238. package/dist/assets/components/internal/listbox/listbox.vue +1 -0
  239. package/dist/assets/components/internal/radio-input/radio-input.stories.ts +1 -0
  240. package/dist/assets/components/internal/radio-input/radio-input.vue +1 -0
  241. package/dist/assets/components/internal/teleport/teleport.vue +1 -0
  242. package/dist/assets/components/internal/tooltip/tooltip.test.vue +1 -0
  243. package/dist/assets/components/internal/tooltip/tooltip.vue +1 -0
  244. package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -0
  245. package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -0
  246. package/dist/assets/components/layout/layout.docs.mdx +108 -0
  247. package/dist/assets/components/layout/layout.stories.ts +1 -0
  248. package/dist/assets/components/layout/layout.vue +1 -0
  249. package/dist/assets/components/link/link.docs.mdx +119 -0
  250. package/dist/assets/components/link/link.stories.ts +1 -0
  251. package/dist/assets/components/link/link.vue +1 -0
  252. package/dist/assets/components/loading-indicator/loading-indicator.docs.mdx +65 -0
  253. package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -0
  254. package/dist/assets/components/loading-indicator/loading-indicator.vue +1 -0
  255. package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -0
  256. package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -0
  257. package/dist/assets/components/menu/components/menu-list-container/menu-list-container.vue +1 -0
  258. package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -0
  259. package/dist/assets/components/menu/menu.docs.mdx +121 -0
  260. package/dist/assets/components/menu/menu.stories.ts +1 -0
  261. package/dist/assets/components/menu/menu.vue +1 -0
  262. package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -0
  263. package/dist/assets/components/modal/components/modal-default/modal-default.vue +1 -0
  264. package/dist/assets/components/modal/components/modal-dialog/modal-dialog.vue +1 -0
  265. package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -0
  266. package/dist/assets/components/modal/modal.docs.mdx +215 -0
  267. package/dist/assets/components/modal/modal.stories.ts +1 -0
  268. package/dist/assets/components/modal/modal.vue +1 -0
  269. package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -0
  270. package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +217 -0
  271. package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -0
  272. package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -0
  273. package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -0
  274. package/dist/assets/components/pagination/components/pagination-controls/pagination-controls.vue +1 -0
  275. package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -0
  276. package/dist/assets/components/pagination/pagination.docs.mdx +116 -0
  277. package/dist/assets/components/pagination/pagination.stories.ts +1 -0
  278. package/dist/assets/components/pagination/pagination.vue +1 -0
  279. package/dist/assets/components/payment-method/payment-method.docs.mdx +34 -0
  280. package/dist/assets/components/payment-method/payment-method.stories.ts +1 -0
  281. package/dist/assets/components/payment-method/payment-method.vue +1 -0
  282. package/dist/assets/components/popover/components/popover-dismiss-button/popover-dismiss-button.vue +1 -0
  283. package/dist/assets/components/popover/components/popover-image/popover-image.vue +1 -0
  284. package/dist/assets/components/popover/components/popover-title/popover-title.vue +1 -0
  285. package/dist/assets/components/popover/popover.docs.mdx +89 -0
  286. package/dist/assets/components/popover/popover.stories.ts +1 -0
  287. package/dist/assets/components/popover/popover.vue +1 -0
  288. package/dist/assets/components/popper-container/popper-container.docs.mdx +11 -0
  289. package/dist/assets/components/popper-container/popper-container.stories.ts +1 -0
  290. package/dist/assets/components/popper-container/popper-container.vue +1 -0
  291. package/dist/assets/components/promo-banner/promo-banner.docs.mdx +94 -0
  292. package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -0
  293. package/dist/assets/components/promo-banner/promo-banner.vue +1 -0
  294. package/dist/assets/components/radio-group/components/radio-button/radio-button.docs.mdx +76 -0
  295. package/dist/assets/components/radio-group/components/radio-button/radio-button.stories.ts +1 -0
  296. package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -0
  297. package/dist/assets/components/radio-group/radio-group.docs.mdx +154 -0
  298. package/dist/assets/components/radio-group/radio-group.stories.ts +1 -0
  299. package/dist/assets/components/radio-group/radio-group.vue +1 -0
  300. package/dist/assets/components/rich-text-editor/components/rich-text-editor-content-area/rich-text-editor-content-area.vue +1 -0
  301. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-code-block/rich-text-editor-code-block.vue +1 -0
  302. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-inline-code/rich-text-editor-inline-code.vue +1 -0
  303. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-link/rich-text-editor-link.vue +1 -0
  304. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-list-item/rich-text-editor-list-item.vue +1 -0
  305. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-table/rich-text-editor-table.vue +1 -0
  306. package/dist/assets/components/rich-text-editor/rich-text-editor.docs.mdx +52 -0
  307. package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -0
  308. package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -0
  309. package/dist/assets/components/search-bar/search-bar.docs.mdx +117 -0
  310. package/dist/assets/components/search-bar/search-bar.stories.ts +1 -0
  311. package/dist/assets/components/search-bar/search-bar.vue +1 -0
  312. package/dist/assets/components/secondary-nav/components/secondary-nav-category/secondary-nav-category.vue +1 -0
  313. package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -0
  314. package/dist/assets/components/secondary-nav/secondary-nav.docs.mdx +40 -0
  315. package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -0
  316. package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -0
  317. package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -0
  318. package/dist/assets/components/segmented-control/segmented-control.docs.mdx +90 -0
  319. package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -0
  320. package/dist/assets/components/segmented-control/segmented-control.vue +1 -0
  321. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +119 -0
  322. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -0
  323. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -0
  324. package/dist/assets/components/selection-card/selection-card.docs.mdx +108 -0
  325. package/dist/assets/components/selection-card/selection-card.stories.ts +1 -0
  326. package/dist/assets/components/selection-card/selection-card.vue +1 -0
  327. package/dist/assets/components/sidepanel/components/sidepanel-page.stories.ts +1 -0
  328. package/dist/assets/components/sidepanel/components/sidepanel-page.vue +1 -0
  329. package/dist/assets/components/sidepanel/sidepanel.docs.mdx +129 -0
  330. package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -0
  331. package/dist/assets/components/sidepanel/sidepanel.vue +1 -0
  332. package/dist/assets/components/status/status.docs.mdx +62 -0
  333. package/dist/assets/components/status/status.stories.ts +1 -0
  334. package/dist/assets/components/status/status.vue +1 -0
  335. package/dist/assets/components/stepper/components/step/step.stories.ts +1 -0
  336. package/dist/assets/components/stepper/components/step/step.vue +1 -0
  337. package/dist/assets/components/stepper/stepper.docs.mdx +127 -0
  338. package/dist/assets/components/stepper/stepper.stories.ts +1 -0
  339. package/dist/assets/components/stepper/stepper.vue +1 -0
  340. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.stories.ts +1 -0
  341. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.vue +1 -0
  342. package/dist/assets/components/structured-list/structured-list.docs.mdx +68 -0
  343. package/dist/assets/components/structured-list/structured-list.stories.ts +1 -0
  344. package/dist/assets/components/structured-list/structured-list.vue +1 -0
  345. package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.vue +1 -0
  346. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.stories.ts +1 -0
  347. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.vue +1 -0
  348. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.stories.ts +1 -0
  349. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.vue +1 -0
  350. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.stories.ts +1 -0
  351. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.vue +1 -0
  352. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.stories.ts +1 -0
  353. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.vue +1 -0
  354. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.stories.ts +1 -0
  355. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.vue +1 -0
  356. package/dist/assets/components/summary-grid/summary-grid.docs.mdx +152 -0
  357. package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -0
  358. package/dist/assets/components/summary-grid/summary-grid.vue +1 -0
  359. package/dist/assets/components/tabs/components/tab.vue +1 -0
  360. package/dist/assets/components/tabs/tabs.docs.mdx +98 -0
  361. package/dist/assets/components/tabs/tabs.stories.ts +1 -0
  362. package/dist/assets/components/tabs/tabs.vue +1 -0
  363. package/dist/assets/components/tag/tag.docs.mdx +137 -0
  364. package/dist/assets/components/tag/tag.stories.ts +1 -0
  365. package/dist/assets/components/tag/tag.vue +1 -0
  366. package/dist/assets/components/textarea/textarea.docs.mdx +53 -0
  367. package/dist/assets/components/textarea/textarea.stories.ts +1 -0
  368. package/dist/assets/components/textarea/textarea.vue +1 -0
  369. package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -0
  370. package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -0
  371. package/dist/assets/components/timeline/timeline.docs.mdx +131 -0
  372. package/dist/assets/components/timeline/timeline.stories.ts +1 -0
  373. package/dist/assets/components/timeline/timeline.vue +1 -0
  374. package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -0
  375. package/dist/assets/components/toast/components/toast-shadow-stack/toast-shadow-stack.vue +1 -0
  376. package/dist/assets/components/toast/toast.docs.mdx +151 -0
  377. package/dist/assets/components/toast/toast.stories.ts +1 -0
  378. package/dist/assets/components/toast/toast.vue +1 -0
  379. package/dist/assets/components/toggle/toggle.docs.mdx +81 -0
  380. package/dist/assets/components/toggle/toggle.stories.ts +1 -0
  381. package/dist/assets/components/toggle/toggle.vue +1 -0
  382. package/dist/assets/components/tutorial/components/tutorial-popover/components/tutorial-indicator/tutorial-indicator.vue +1 -0
  383. package/dist/assets/components/tutorial/components/tutorial-popover/tutorial-popover.vue +1 -0
  384. package/dist/assets/components/tutorial/tutorial.docs.mdx +85 -0
  385. package/dist/assets/components/tutorial/tutorial.stories.ts +1 -0
  386. package/dist/assets/components/tutorial/tutorial.vue +1 -0
  387. package/dist/assets/components/typography/typography.docs.mdx +124 -0
  388. package/dist/assets/components/typography/typography.stories.ts +1 -0
  389. package/dist/assets/components/typography/typography.vue +1 -0
  390. package/dist/assets/composables/use-bento-currency/use-bento-currency.docs.mdx +28 -0
  391. package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -0
  392. package/dist/assets/composables/use-click-outside/use-click-outside.docs.mdx +10 -0
  393. package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -0
  394. package/dist/assets/directives/highlight/highlight.docs.mdx +64 -0
  395. package/dist/assets/directives/highlight/highlight.stories.ts +1 -0
  396. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.docs.mdx +41 -0
  397. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -0
  398. package/dist/assets/directives/tooltip/tooltip.docs.mdx +86 -0
  399. package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -0
  400. package/dist/assets/index.ts +1 -0
  401. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -0
  402. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.vue +1 -0
  403. package/dist/assets/templates/dashboard-template/dashboard-template.docs.mdx +148 -0
  404. package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -0
  405. package/dist/assets/templates/dashboard-template/dashboard-template.vue +1 -0
  406. package/dist/assets/templates/data-grid-template/data-grid-template.docs.mdx +117 -0
  407. package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -0
  408. package/dist/assets/templates/data-grid-template/data-grid-template.vue +1 -0
  409. package/dist/main.js +30031 -0
  410. package/package.json +45 -3
@@ -0,0 +1 @@
1
+ <template> <bento-button class="b-toggle-button" v-bind="$attrs" :aria-expanded="`${toggled}`" :aria-pressed="`${toggled}`" :condensed="condensed" :class="conditionalClasses" :disabled="disabled" :inverse="inverse" :variant="computedVariant" @click.capture.stop="onClick" > <template v-for="(_, innerSlot) of slots" #[innerSlot]> <slot :name="innerSlot" v-bind="getSlotKeyIfVue3(innerSlot)" /> </template> </bento-button> </template> <script setup lang="ts"> import { computed, useSlots } from 'vue'; import BentoButton from '@/components/button/button.vue'; import { type BentoButtonVariant } from '../../button.types'; import { isVue3 } from 'vue-demi'; const props = defineProps({ /** * Reduces sizing of button */ condensed: { type: BentoButton.props.condensed.type, default: false, }, /** * Disables button functonality */ disabled: { type: BentoButton.props.disabled.type, default: false, }, /** * Sets color set to inverse */ inverse: { type: Boolean, default: false, }, /** * Types of flavour of Button * @values primary, secondary, tertiary */ variant: { type: BentoButton.props.variant.type, default: 'primary', validator: BentoButton.props.variant.validator, }, /** * The state of the button whether it is toggled or not. */ toggled: { type: Boolean, default: false, }, }); const emit = defineEmits<{ /** * Click event handler */ (e: 'click', event: MouseEvent): void; /** * Update the toggle state when the button has been clicked. */ (e: 'update:toggled', newToggleState: boolean): void; }>(); const computedVariant = computed( () => (props.variant === 'tertiary' ? 'tertiary-with-background' : props.variant) as BentoButtonVariant ); const conditionalClasses = computed(() => ({ [`b-toggle-button--condensed`]: props?.condensed, [`b-toggle-button--${computedVariant.value}-toggled`]: props.toggled, [`b-toggle-button--inverse`]: props.inverse, })); const onClick = (e: MouseEvent) => { // Prevent Vue3 from triggering event twice e.stopPropagation(); if (!props?.disabled) { emit('click', e); emit('update:toggled', !props.toggled); } }; const slots = useSlots(); // If we don't provide a key to `slot` in vue 3, we will get type errors const getSlotKeyIfVue3 = (slotName: string | number) => (isVue3 ? { key: slotName } : {}); </script> <script lang="ts"> /** * Toggle button is used to switch between two states (example: menu shown, menu hidden). * It functions as a regular button but retains its state to indicate whether the associated menu or element is active or inactive. * * @example * import { BentoToggleButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoToggleButton }, * template: ` * <bento-toggle-button state="toggled" variant="secondary"> * Button that can be toggled * </bento-toggle-button> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./toggle-button.scss" />
@@ -0,0 +1,166 @@
1
+ import { Canvas, Meta, Source } from '@storybook/blocks';
2
+ import * as CardStories from './card.stories';
3
+
4
+ <Meta of={CardStories} />
5
+
6
+ # Card
7
+
8
+ Card is a simple container that should be used to display related information that is associated with one subject or
9
+ destination.
10
+
11
+ <Canvas of={CardStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use `bento-card` component when you need to display content related to a single subject.
16
+
17
+ Here are some examples:
18
+
19
+ - Information Display: to display information in a well-organized manner.
20
+ - User Profiles: to display user profile information. This could include a user's picture, name, and other pertinent
21
+ details.
22
+
23
+ ## Guidelines
24
+
25
+ It's okay to use multiple cards on the page when necessary.
26
+
27
+ ## Variations
28
+
29
+ ### Regular
30
+
31
+ It's a default variation of `bento-card` component. It's simple container coming with rounded corners, container fill
32
+ and container outline.
33
+
34
+ It's non-interactive component, that may contain default title, descriptive subtitle, content sections and actions.
35
+
36
+ <Canvas of={CardStories.WithActions} />
37
+
38
+ ### Expandable
39
+
40
+ Expandable `bento-card` is a container that can show small amount of information and reveal more information when
41
+ clicked.
42
+
43
+ It's an interactive component, that can be in one of 2 states: either expanded or not.
44
+
45
+ If card is in collapsed state > Click on Card title area will expand the card and expose hidden content.
46
+
47
+ If card is in expanded state > Click on Card title area will collapse the card and hide extra content leaving just card
48
+ title visible.
49
+
50
+ <Canvas of={CardStories.Expandable} />
51
+
52
+ ### Clickable
53
+
54
+ A clickable `bento-card` is a card container that can be actionable, similar to a button. It becomes interactive, and it
55
+ can have a click event handler attached to it.
56
+
57
+ It also becomes focusable and can be triggerd by "Enter" or "Space" key press.
58
+
59
+ **Note: Do not make an entire card clickable if the card contains other interactive elements, such as buttons or links.
60
+ This creates a confusing and inaccessible experience for users of assistive technologies.**
61
+
62
+ <Canvas of={CardStories.Clickable} />
63
+
64
+ #### Closed
65
+
66
+ When expandable `bento-card` is used, then it's rendered by default with opened state.
67
+
68
+ You can control the opened/closed behaviour of expandable `bento-card` with 'closed' property.
69
+
70
+ <Canvas of={CardStories.ExpandableClosed} />
71
+
72
+ ## Modifiers
73
+
74
+ ### Disabled
75
+
76
+ The disabled property is only applied to `clickable` or `expandable` card
77
+
78
+ It renders the `bento-card` in a disabled state. In practice this makes the component non-interactive and renders a
79
+ `not-allowed` cursor on hover.
80
+
81
+ <Canvas of={CardStories.Disabled} />
82
+
83
+ ## Actions
84
+
85
+ The `bento-card` actions follow the pattern defined for all actions in Bento which is also described in detail in the
86
+ [Button actions docs](../?path=/docs/components-button-button-actions--docs#button-actions). To use them, use the prop
87
+ `:actions` and provide a list of actions where each is an object with `title`, `event` and `icon` (optional). Each item
88
+ in the list will be rendered as a button. The first element in the list will be rendered as "primary". Every other
89
+ element after the first will be rendered as "secondary".
90
+
91
+ <Canvas of={CardStories.WithActions} />
92
+
93
+ ### Actions layout
94
+
95
+ To configure the actions layout, you can use the different modifier options with the `:actions-layout` property. For
96
+ detailed information on the modifiers check the
97
+ [Button actions documentation](../?path=/docs/components-button-button-actions--docs#modifiers).
98
+
99
+ ### Actions as links
100
+
101
+ You can use the `event` prop of the `actions` object to link to another page. Here is an example on how to do this:
102
+
103
+ - Provide a reference to actions array from the template
104
+
105
+ <Source
106
+ dark
107
+ language="html"
108
+ code={`<bento-card
109
+ actionsLayout="buttons-start"
110
+ :actions="cardButtonActions"
111
+ >
112
+ Card title
113
+ <template #description>
114
+ A little context goes a long way
115
+ </template>
116
+ <template #content>
117
+ <bento-typography>Card content</bento-typography>
118
+ </template>
119
+ </bento-card>`}
120
+ />
121
+
122
+ - In the `event` prop, reference a function that will execute the `router.push`
123
+
124
+ <Source
125
+ dark
126
+ language="typescript"
127
+ code={`const navigateToPage = () => router.push({ name: 'routeName' });
128
+ const cardButtonActions = [{
129
+ title: 'Primary',
130
+ event: navigateToPage,
131
+ }, {
132
+ title: 'Secondary',
133
+ event: clickedAction,
134
+ }];
135
+ `}
136
+ />
137
+
138
+ > **Important note:** Actions as links will still be rendered as buttons meaning that native link behaviour like right
139
+ > clicking to open in new tab won't work. The Bento team is currently working on a solution to provide native link
140
+ > behaviour to the actions.
141
+
142
+ ## Accessibility
143
+
144
+ On top of already using the correct semantic elements for the buttons, the `bento-card` component used the ARIA
145
+ `article` role.
146
+
147
+ ### Keyboard interaction
148
+
149
+ The `expandable` card header and action buttons are reachable through keyboard navigation and follows logical navigation
150
+ flow of the page. A detailed list of keyboard interactions for `expandable` card is provided in the table below.
151
+
152
+ | Keyboard interaction | Output |
153
+ | -------------------- | ----------------------------------------------------------------------------- |
154
+ | `Tab` | Moves focus to the card, header or action buttons in card content. |
155
+ | `Enter` | Activates the card, header or action button, where the focus is currently on. |
156
+ | `Space` | Activates the card or button, where the focus is currently on. |
157
+
158
+ ### Roles, states and properties
159
+
160
+ | ARIA attribute | is automatic? | fallback used | manual override |
161
+ | -------------- | ----------------------------- | ------------- | --------------- |
162
+ | `role` | Yes, has the `article` value. | - | - |
163
+
164
+ ## Resources
165
+
166
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=3672-75024&t=t0vz2EC0tMDFOarD-0)
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { BentoButton, BentoButtonActionsLayout } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { BentoLink } from '@/components/link'; import { BentoImage } from '@/components/image'; import { cardImageSrcStub } from '../../__tests__/img-stub.json'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoCardBackground, BentoCardTitleSize } from '@/components/card/card.types'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoCard from './card.vue'; import './card.stories.scss?module'; enum CardHeaderSlotType { TEXT = 'text', LINK = 'link', BUTTON = 'button', } export default { components: { BentoTypography, BentoLink }, title: 'Card', component: BentoCard, argTypes: { titleSize: { options: Object.values(BentoCardTitleSize), mapping: BentoCardTitleSize, control: { type: 'select', }, }, default: { name: 'default', defaultValue: '', description: 'Card content', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text' }, }, image: { name: 'image', description: 'Image', table: { type: { summary: 'BentoImage' }, }, control: { disable: true }, }, header: { name: 'header', defaultValue: '', description: 'Header content', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text' }, }, headerSlotExample: { name: 'headerSlotExample', options: Object.values(CardHeaderSlotType), mapping: CardHeaderSlotType, defaultValue: CardHeaderSlotType.TEXT, description: 'Header content', table: { category: 'Storybook controls', }, control: { type: 'select' }, }, click: { name: 'click', defaultValue: '', description: `Click event handler. Transforms the card into a clickable element if there is no content, footer or actions present in the template. `, table: { type: { summary: '($event) => void' }, }, control: { disable: true }, }, description: { name: 'description', defaultValue: '', description: 'Secondary title, below the main one', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, actionsLayout: { options: Object.values(BentoButtonActionsLayout), mapping: BentoButtonActionsLayout, control: { type: 'select', }, }, background: { options: Object.values(BentoCardBackground), mapping: BentoCardBackground, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoCard>; const CARD_TITLE = 'Card title'; const CARD_HEADER_SLOT = 'Card header slot'; const CARD_DESCRIPTION = 'A little context goes a long way'; const CARD_CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis, ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam. Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue. Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.'; const CARD_ACTIONS = [ { title: 'Primary', event: () => action('Primary')(), }, { title: 'Secondary', event: () => action('Secondary')(), }, ]; const IMAGE_SRC = cardImageSrcStub; const IMAGE_ALT = 'Example image'; const DEFAULT_CARD_PROPS = { default: CARD_TITLE, description: CARD_DESCRIPTION, content: CARD_CONTENT, background: 'primary', expandable: false, disabled: false, titleSize: 'default', headerSlotExample: CardHeaderSlotType.TEXT, }; const Template: Story = { render: (_args, { argTypes }) => ({ components: { BentoCard, BentoTypography, BentoLink, BentoButton, BentoImage }, props: Object.keys(argTypes), template: ` <bento-card v-bind="args" class="b-card-story" @click='cardClicked'> <template #default v-if='args.default'> {{ args.default }} </template> <template #image v-if='args.image'> <bento-image :src="args.image.src" :alt="args.image.alt" /> </template> <template #header v-if='args.header'> <bento-typography v-if="args.headerSlotExample === CardHeaderSlotType.TEXT"> {{ args.header }} </bento-typography> <bento-link v-if="args.headerSlotExample === CardHeaderSlotType.LINK" to="https://www.adyen.com/" external> <bento-typography> {{ args.header }} </bento-typography> </bento-link> <template v-if="args.headerSlotExample === CardHeaderSlotType.BUTTON"> <bento-button > {{ args.header }} </bento-button> </template> </template> <template #description v-if='args.description'> {{ args.description }} </template> <template #content> <bento-typography el="div">{{ args.content }}</bento-typography> </template> </bento-card> `, setup(props) { return { // Enums CardHeaderSlotType, // Values args: isVue2 ? props : _args, // Events cardClicked: action('onCardClicked'), }; }, }), }; const defaultCode = ` <bento-card> ${CARD_TITLE} <template #header> ${CARD_HEADER_SLOT} </template> <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const Default = { ...Template, parameters: storybookDocsParameter(defaultCode), args: { ...DEFAULT_CARD_PROPS }, }; const secondaryCode = ` <bento-card background="secondary"> ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const SecondaryBackground = { ...Template, parameters: storybookDocsParameter(secondaryCode), args: { ...DEFAULT_CARD_PROPS, background: 'secondary', }, }; const expandableCode = ` <bento-card expandable> ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const Expandable = { ...Template, parameters: storybookDocsParameter(expandableCode), args: { ...DEFAULT_CARD_PROPS, expandable: true, }, }; const expandableClosedCode = ` <bento-card expandable closed> ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const ExpandableClosed = { ...Template, parameters: storybookDocsParameter(expandableClosedCode), args: { ...DEFAULT_CARD_PROPS, expandable: true, closed: true, }, }; const disabledCode = ` <bento-card expandable disabled> ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const Disabled = { ...Template, parameters: storybookDocsParameter(disabledCode), args: { ...DEFAULT_CARD_PROPS, expandable: true, disabled: true, }, }; const withActionsCode = ` <bento-card actionsLayout="${BentoButtonActionsLayout.BUTTONS_START}" actions="${JSON.stringify(CARD_ACTIONS)}" > ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const WithActions = { ...Template, parameters: storybookDocsParameter(withActionsCode), args: { ...DEFAULT_CARD_PROPS, actionsLayout: 'buttons-start', actions: CARD_ACTIONS, }, }; export const WithHeaderSlot: Story = { render: (_args, { argTypes }) => ({ components: { BentoCard, BentoTypography, BentoLink, BentoButton }, props: Object.keys(argTypes), template: ` <bento-card v-bind="args" class="b-card-story"> <template #default v-if='args.default'> {{ args.default }} </template> <template #header v-if='args.header'> <bento-typography v-if="headerSlotExample === CardHeaderSlotType.TEXT"> {{ args.header }} </bento-typography> <bento-link v-if="headerSlotExample === CardHeaderSlotType.LINK" to="https://www.adyen.com/" external> <bento-typography> {{ args.header }} </bento-typography> </bento-link> <template v-if="headerSlotExample === CardHeaderSlotType.BUTTON"> <bento-button > {{ args.header }} </bento-button> </template> </template> <template #description> {{ args.description }} </template> <template #content> <bento-typography el="div">{{ args.content }}</bento-typography> </template> </bento-card> `, setup(props) { return { // Enums CardHeaderSlotType, // Values args: isVue2 ? props : _args, }; }, }), args: { ...DEFAULT_CARD_PROPS, header: CARD_HEADER_SLOT, expandable: true, }, }; const withImageCode = ` <bento-card> ${CARD_TITLE} <template #image> <bento-image src="${IMAGE_SRC}" alt="${IMAGE_ALT}" /> </template> <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const WithImage = { components: { BentoImage, }, ...Template, parameters: storybookDocsParameter(withImageCode), args: { ...DEFAULT_CARD_PROPS, image: { src: IMAGE_SRC, alt: IMAGE_ALT }, }, }; const clickableCode = ` <bento-card clickable @click="onClick"> ${CARD_TITLE} <template #description> ${CARD_DESCRIPTION} </template> <template #content> <bento-typography>${CARD_CONTENT}</bento-typography> </template> </bento-card> `; export const Clickable = { ...Template, parameters: storybookDocsParameter(clickableCode), args: { ...DEFAULT_CARD_PROPS, clickable: true, }, };
@@ -0,0 +1 @@
1
+ <template> <div class="b-card" :class="cardConditionalClasses" :tabindex="tabIndex" data-testid="card" @click="onClick" @keydown.enter="onKeyDown" @keydown.space="onKeyDown" > <div v-if="hasSlot('image')" class="b-card__image"> <slot name="image"></slot> </div> <div v-if="showHeader" class="b-card__header" :class="cardHeaderConditionalClasses" :tabindex="headerTabIndex" :aria-controls="expandable && showCardBody ? cardId : null" :aria-expanded="expandable ? `${showContent}` : null" :role="expandable ? 'button' : null" @click="toggleExpansion" @keypress.enter="toggleExpansion" @keypress.space="toggleExpansion" > <div class="b-card__header-wrapper"> <div v-if="showTitleWrapper" class="b-card__header-slot-wrapper"> <div class="b-card__title-wrapper" :class="cardTitleWrapperConditionalClasses"> <div v-if="expandable" class="b-card__toggle" :class="cardToggleConditionalClasses"> <chevron-up-icon v-if="showContent" :svg-title="t('hideContent')" aria-hidden="true" /> <chevron-down-icon v-else :svg-title="t('showContent')" aria-hidden="true" /> </div> <bento-typography v-if="hasSlot('default')" class="b-card__title" v-bind="typography"> <slot /> </bento-typography> </div> <template v-if="hasSlot('header')"> <slot name="header"></slot> </template> </div> <bento-typography v-if="hasSlot('description')" class="b-card__description" el="div" variant="body"> <slot name="description" /> </bento-typography> </div> </div> <Transition name="b-card__animation"> <div v-if="showCardBody" class="b-card__wrapper" :style="contentMaxHeight"> <div :id="expandable ? cardId : null" ref="contentDiv" class="b-card__body" :class="cardBodyConditionalClasses" > <div v-if="hasSlot('content')" class="b-card__content" :class="cardContentConditionalClasses"> <slot name="content" /> </div> <div v-if="actions" class="b-card__actions" :class="cardActionsConditionalClasses"> <bento-button-actions :actions="actions" :layout="actionsLayout"></bento-button-actions> </div> </div> </div> </Transition> </div> </template> <script setup lang="ts"> import { computed, ref, useSlots, watch } from 'vue'; // Components import { BentoTypography } from '@/components/typography'; import { BentoButtonActions, BentoButtonActionsLayout, type BentoButtonActionsList } from '@/components/button'; import { BentoCardBackground, BentoCardTitleSize } from '@/components/card/card.types'; import ChevronDownIcon from '@adyen/ui-assets-icons-16/vue/chevron-down'; import ChevronUpIcon from '@adyen/ui-assets-icons-16/vue/chevron-up'; // Composables import { useExpandableContentHeight } from '@/composables/use-expandable-content-height/use-expandable-content-height'; // Utils import { useHasSlot } from '@/composables'; import { generateUid } from '@/core/utils/ts'; import { useI18n } from '@/utils/ts/i18n'; import { observeSizeOfElement } from '@/utils/ts/resize'; import messages from './messages.json'; import { deprecate } from '@/utils/ts/deprecate'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const slots = useSlots(); const hasSlot = useHasSlot(slots); const props = withDefaults( defineProps<{ /** * List of actions that will be used to render the buttons. * First element in the list will be rendered as "primary". * All elements after the first will be rendered as "secondary". * * Each object in the list should have a `title`, an `event` and * (optional) a Vue `icon` from the library `@adyen/ui-assets-icons-16`. * * `title`: Button's text. * * `event`: Event triggered when clicking the Button. * * `icon`: Vue Icon from the library `@adyen/ui-assets-icons-16`. * * ``` * [{ * title: 'Primary', * event: () => { ... }, * icon: FourPeopleIcon * }, { * title: 'Secondary', * event: () => { ... } * }] * ``` */ actions?: BentoButtonActionsList; /** * Defines the layout in which the button actions is presented. * It can be one of: * * @values BUTTONS_START * @values BUTTONS_END * @values FILL_CONTAINER * @values SPACE_BETWEEN * @values VERTICAL_STACK */ actionsLayout?: BentoButtonActionsLayout | `${BentoButtonActionsLayout}`; /** * Defines the color of card background */ background?: BentoCardBackground | `${BentoCardBackground}`; /** * Makes the whole card clickable */ clickable?: boolean; /** * Sets the expandable card in a closed state */ closed?: boolean; /** * Disables the expandable card */ disabled?: boolean; /** * The whole content of the card will be expandable in accordion manner */ expandable?: boolean; /** * Defines the title size of the card header */ titleSize?: BentoCardTitleSize | `${BentoCardTitleSize}`; }>(), { actions: null, actionsLayout: BentoButtonActionsLayout.BUTTONS_END, background: BentoCardBackground.PRIMARY, clickable: false, closed: false, disabled: false, expandable: false, titleSize: BentoCardTitleSize.DEFAULT, } ); const emit = defineEmits<{ (e: 'click'); (e: 'expanded', isExpanded: boolean); }>(); const contentDiv = ref<HTMLDivElement>(null); const cardId = generateUid('card'); const showContent = ref(!props.closed); watch( () => props.closed, value => { showContent.value = !value; } ); const showHeader = computed(() => hasSlot('default') || hasSlot('description') || hasSlot('header')); const showTitleWrapper = computed(() => hasSlot('default') || hasSlot('header')); const showCardBody = computed( () => (hasSlot('content') || props.actions?.length) && (!props.expandable || (props.expandable && showContent.value)) ); const tabIndex = computed(() => (props.clickable && !props.disabled ? 0 : -1)); const headerTabIndex = computed(() => (props.expandable && !props.disabled ? 0 : -1)); const cardConditionalClasses = computed(() => ({ [`b-card--${props.background}`]: props.background, 'b-card--clickable': props.clickable, 'b-card--expandable': props.expandable, 'b-card--disabled': (props.clickable || props.expandable) && props.disabled, })); const cardHeaderConditionalClasses = computed(() => ({ 'b-card__header--expandable': props.expandable, 'b-card__header--without-body': !props.expandable && !showCardBody.value, })); const cardToggleConditionalClasses = computed(() => ({ 'b-card__toggle--small-text': props.titleSize === BentoCardTitleSize.SMALL, })); const cardBodyConditionalClasses = computed(() => ({ 'b-card__body--expandable': props.expandable && hasSlot('content'), 'b-card__body--without-header': !props.expandable && !showHeader.value, })); const cardContentConditionalClasses = computed(() => ({ 'b-card__content--without-header': !props.expandable && !showHeader.value, 'b-card__content--expandable': props.expandable, })); const cardActionsConditionalClasses = computed(() => { const isNotExpandableWithoutContent = !props.expandable && props.actions && !hasSlot('content'); return { 'b-card__actions--without-content': isNotExpandableWithoutContent, 'b-card__actions--without-content-and-with-header': isNotExpandableWithoutContent && showHeader.value, }; }); const cardTitleWrapperConditionalClasses = computed(() => ({ 'b-card__title-wrapper--truncated': hasSlot('header'), })); const typography = computed( () => ({ el: 'div', variant: props.titleSize === BentoCardTitleSize.DEFAULT ? 'title' : 'body', strongest: props.titleSize === BentoCardTitleSize.SMALL, wide: props.titleSize === BentoCardTitleSize.SMALL, }) as InstanceType<typeof BentoTypography>['$props'] ); // Toggle open and close logic and animation const { updateContentHeight, contentHeight } = useExpandableContentHeight(contentDiv, showContent); const contentMaxHeight = computed(() => props.expandable && contentHeight.value ? { 'max-height': contentHeight.value } : {} ); watch( () => contentDiv.value, () => { let observer: ResizeObserver | null = null; if (!observer && contentDiv.value) { observer = observeSizeOfElement(contentDiv.value, () => { if (contentDiv.value) { updateContentHeight(); } }); } } ); const toggleExpansion = () => { if (!props.disabled && props.expandable) { showContent.value = !showContent.value; emit('expanded', showContent.value); updateContentHeight(); } }; const onClick = () => { if (!props.disabled) { emit('click'); if (!props.clickable) { deprecate( 'BentoCard "@click" property', `Use the BentoCard "@click" with the "clickable" property to enable accessible styles.`, '2.0.0' ); } } }; const onKeyDown = () => { if (props.clickable && !props.disabled) { emit('click'); } }; </script> <script lang="ts"> /** * A card is a flexible and composable container. * Use cards to represent a single object or to visually group certain content, like legal information within company settings. * * @example * import { BentoCard } from '@adyen/bento-vue2'; * * export default { * components: { BentoCard }, * template: ` * <bento-card> * Title of the Card * </bento-card> * ` * }; */ export default { i18n: { messages }, name: 'bento-card', }; </script> <style lang="scss" scoped src="./card.scss" />
@@ -0,0 +1,116 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as CheckboxStories from './checkbox.stories';
3
+ import * as CheckboxGroupStories from './components/checkbox-group/checkbox-group.stories';
4
+
5
+ <Meta of={CheckboxStories} />
6
+
7
+ # Checkbox
8
+
9
+ A checkbox allows the user to make a binary choice about an option. Checkboxes can be used by themselves or in a list
10
+ when more than one option (or none of them) can be selected at the same time. A checkbox usually requires the choice to
11
+ be confirmed with some sort of action, e.g. a `Save` or an `Apply` button.
12
+
13
+ <Canvas of={CheckboxStories.Default} />
14
+
15
+ ## Use Cases
16
+
17
+ Use a checkbox to:
18
+
19
+ - Accept terms and conditions.
20
+ - Select multiple items from a list, such as selecting items to add to a shopping cart or choosing multiple interests
21
+ in a user's profile.
22
+ - Select features in applications, allowing users to enable or disable specific preferences or settings.
23
+ - Make a todo list.
24
+
25
+ ### Do not use
26
+
27
+ Don't use a checkbox if you need to:
28
+
29
+ - Turn an option on or off without requiring confirmation. In that case, use a toggle.
30
+ - Select only one option from a list of mutually exclusive choices. Consider using the `input-radio` component
31
+ instead.
32
+
33
+ ### Guidelines
34
+
35
+ - Checkboxes labels should be short, descriptive and clearly convey what the user is opting in for or out of (e.g.
36
+ `Accept the terms and conditions`).
37
+ - Checkboxes work best when stacked vertically, so it’s easier to scan the options. If you need to display them
38
+ horizontally, make sure you have enough space between the options so that it’s clear which checkbox goes with which
39
+ label.
40
+ - If needed, an additional description label can also be used.
41
+ - Leave the checkbox blank by default so users must actively click it to opt in for their choice.
42
+
43
+ ## Variations
44
+
45
+ ### Indeterminate state
46
+
47
+ A checkbox can have an `indeterminate` state. This state is used to indicate that only some of that item’s child
48
+ elements have been selected.
49
+
50
+ <Canvas of={CheckboxStories.Indeterminate} />
51
+
52
+ ## Modifiers
53
+
54
+ ### Disabled option
55
+
56
+ Renders the `checkbox` component in a disabled state. In practice this makes the component non-interactive and renders a
57
+ not-allowed cursor on hover.
58
+
59
+ Use this checkbox when another action has to be completed before the checkbox is usable.
60
+
61
+ <Canvas of={CheckboxStories.Disabled} />
62
+
63
+ ### Readonly option
64
+
65
+ Renders the `checkbox` component in a readonly state. In terms of functionality, read only states are focusable and
66
+ submittable in a form. They're considered to be 'active' from an application's point of view. User can highlight and
67
+ copy the data in them.
68
+
69
+ ### Error message option
70
+
71
+ Renders the `checkbox` component in an error state with an error message.
72
+
73
+ <Canvas of={CheckboxStories.ErrorMessage} />
74
+
75
+ ### Error option
76
+
77
+ Renders the `checkbox` component in an error state.
78
+
79
+ <Canvas of={CheckboxStories.Error} />
80
+
81
+ ### Adding tags to items
82
+
83
+ If you would like to grab a user's attention or guide them in their choice on which item to select then you may want to
84
+ use `tag` property. This property accepts all properties a `BentoTag` accepts.
85
+
86
+ <Canvas of={CheckboxGroupStories.WithTag} />
87
+
88
+ ## Accessibility
89
+
90
+ The `label` and `description` areas are clickable. A click on these elements selects the checkbox input value.
91
+
92
+ Screen readers convey the state of the checkbox automatically given this component is built semantically and has the
93
+ right ARIA `role` set.
94
+
95
+ ### Keyboard interaction
96
+
97
+ The `checkbox` is reachable through keyboard navigation and follows the logical navigation flow of the page. A detailed
98
+ list of keyboard interactions is provided in the table below.
99
+
100
+ | Keyboard interaction | Output |
101
+ | -------------------- | ------------------------------------------------- |
102
+ | `Tab` | Moves focus to the checkbox or away from it. |
103
+ | `Space` | When focus is on the checkbox, selects the value. |
104
+
105
+ ### Roles, states and properties
106
+
107
+ | ARIA attribute | is automatic? | fallback used | manual override |
108
+ | -------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------- |
109
+ | `aria-label` | Yes, corresponds to the `default` slot text or the `description` slot when the `default` is empty. | If no text is passed to either of the slots, then the 'checkbox' string will be used. | - |
110
+ | `role` | Yes, through the semantic `input[type="checkbox"]` element with `role="checkbox"` set. | - | - |
111
+ | `aria-checked` | Yes, set when the checkbox gets selected through the `input`'s `checked` property. | - | - |
112
+
113
+ ## Resources
114
+
115
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=211-1394&t=1QyqMagNIghSFOGT-0)
116
+ - [WAI-ARIA guidelines](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/examples/checkbox/)
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { ref } from 'vue'; import { BentoCheckboxEvent } from './checkbox.types'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoCheckbox from './checkbox.vue'; const DEFAULT_LABEL = 'Label text'; const DEFAULT_SUB_LABEL = 'Description below label'; const ERROR_MESSAGE = 'Error message text'; export default { title: 'Checkbox', component: BentoCheckbox, argTypes: { default: { name: 'default', defaultValue: '', description: 'Label text slot', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, description: { name: 'description', defaultValue: '', description: 'Description text slot', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, content: { name: 'content', defaultValue: '', description: 'Content slot available via checkbox group only', table: { type: { summary: 'VNode[]' }, }, }, [BentoCheckboxEvent.UPDATE]: { name: BentoCheckboxEvent.UPDATE, defaultValue: '', description: 'Input event handler.', table: { type: { summary: '(newModelValue: BentoCheckboxType | BentoCheckboxGroupItems) => void' }, }, control: { disable: true }, }, [BentoCheckboxEvent.INPUT]: { name: BentoCheckboxEvent.INPUT, defaultValue: '', description: 'Input event handler.', table: { type: { summary: '(newModelValue: BentoCheckboxType | BentoCheckboxGroupItems) => void' }, }, control: { disable: true }, }, }, } as Meta; type Story = StoryObj<typeof BentoCheckbox>; const defaultCode = ` <template> <!-- With model-value --> <bento-checkbox @update:model-value="clickAction" :model-value="yourModelValueVariable" value="${DEFAULT_LABEL}"> ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> <!-- With v-model Vue2/Vue3 --> <bento-checkbox v-model="yourModelValueVariable" value="${DEFAULT_LABEL}"> ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> </template> <script setup lang="ts"> import { BentoCheckbox } from "@adyen/bento-vue2" import type { BentoCheckboxType } from "@adyen/bento-vue2"; const yourModelValueVariable = ref<BentoCheckboxType>(false); const clickAction = (newValue: BentoCheckboxType) = { yourModelValueVariable.value = newValue; } </script> `; const Basic: Story = { render: (_args, { argTypes }) => ({ components: { BentoCheckbox }, props: Object.keys(argTypes), template: ` <bento-checkbox v-bind="args" :model-value="selectedValue" @update:model-value="clickedAction"> {{ args.default }} <template #description>{{ args.description }}</template> </bento-checkbox> `, setup(props) { const selectedValue = ref(undefined); const clickedAction = value => { selectedValue.value = value; action(BentoCheckboxEvent.UPDATE)(value); }; return { // Values selectedValue, args: isVue2 ? props : _args, // Events clickedAction, }; }, }), }; export const Default: Story = { ...Basic, args: { default: DEFAULT_LABEL, description: DEFAULT_SUB_LABEL, }, parameters: storybookDocsParameter(defaultCode), }; const disabledCode = ` <bento-checkbox @update:model-value="clickedAction" :model-value="yourModelValueVariable" value="${DEFAULT_LABEL}" disabled > ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> `; export const Disabled: Story = { ...Basic, args: { default: 'I am disabled', disabled: true, }, parameters: storybookDocsParameter(disabledCode), }; const indeterminateCode = ` <bento-checkbox @update:modelvalue="clickedAction" :model-value="yourModelValueVariable" value="${DEFAULT_LABEL}" indeterminate > ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> `; export const Indeterminate = { ...Basic, args: { default: 'I am indeterminate', indeterminate: true, }, parameters: storybookDocsParameter(indeterminateCode), }; const errorCode = ` <bento-checkbox @update:modelvalue="clickedAction" :model-value="yourModelValueVariable" value="${DEFAULT_LABEL}" has-error > ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> `; export const Error = { ...Basic, args: { default: 'I am in error state', hasError: true, }, parameters: storybookDocsParameter(errorCode), }; const errorMessageCode = ` <bento-checkbox @update:modelvalue="clickedAction" :model-value="yourModelValueVariable" value="${DEFAULT_LABEL}" :error-message="${ERROR_MESSAGE}" > ${DEFAULT_LABEL} <template #description>${DEFAULT_SUB_LABEL}</template> </bento-checkbox> `; export const ErrorMessage = { ...Basic, args: { default: 'I have an error message', errorMessage: ERROR_MESSAGE, }, parameters: storybookDocsParameter(errorMessageCode), };
@@ -0,0 +1 @@
1
+ <template> <label class="b-checkbox" :class="conditionalClass" v-bind="styleAttrs" :aria-hidden="getAriaHidden($attrs)"> <checkbox-input v-bind="$attrs" ref="input" :aria-label="computedAriaLabel" :aria-describedby="ariaDescribedBy" :disabled="disabled" :has-error="shouldShowError" :indeterminate="isIndeterminate" :value="value" :model-value="modelValue" :readonly="readonly" @update:model-value="onChange" /> <span v-if="hasSlot('default') || shouldShowErrorMessage" class="b-checkbox__text"> <bento-typography v-if="hasSlot('default')" class="b-checkbox__label" el="span" variant="body"> <span><slot /></span> <bento-typography v-if="optional" class="b-checkbox__label-requirement" el="span" >({{ t('optional') }})</bento-typography > <bento-typography v-else-if="required" class="b-checkbox__label-requirement" el="span" >({{ t('required') }})</bento-typography > <bento-tag v-if="tag" class="b-checkbox__label-tag" v-bind="tag" /> </bento-typography> <bento-typography v-if="hasSlot('default') && hasSlot('description')" :id="descriptionId" class="b-checkbox__description" el="span" variant="body" > <slot name="description"></slot> </bento-typography> <error-message v-if="shouldShowErrorMessage" :id="errorId" :error-message="errorMessage" /> <bento-typography v-if="contentSlotAvailable" class="b-checkbox__content" el="span" variant="body"> <slot name="content"></slot> </bento-typography> </span> </label> </template> <script lang="ts"> import { computed, defineComponent, type HTMLAttributes, onMounted, type PropType, ref, toRefs, useAttrs, } from 'vue'; import { BentoCheckboxEvent, BentoCheckboxState, type BentoCheckboxTag, type BentoCheckboxType, type BentoMultiCheckboxType, } from './checkbox.types'; import { BentoTypography } from '@/components/typography'; import { CheckboxInput } from '@/components/internal/checkbox-input'; import { ErrorMessage } from '@/components/internal/error-message'; import { useDefaultSlotValidator, useHasSlot } from '@/composables'; import { generateUid } from '@/core/utils/ts'; import { getSlotText } from '@/utils/ts/get-slot-text'; import { useI18n } from '@/utils/ts/i18n'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { BentoTag } from '@/components/tag'; import { type BentoCheckboxGroupItems } from './components/checkbox-group/checkbox-group.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; /** * Checkboxes are used to make a binary choice about an option. * The label tag area is also clickable. * * @usage * import { BentoCheckbox } from '@adyen/bento-vue2'; * * export default { * components: { BentoCheckbox }, * template: ` * <bento-checkbox> * Label text * <template #description>Sub label text</template> * </bento-checkbox> * `, * } */ export default defineComponent({ name: 'bento-checkbox', i18n: { messages }, components: { BentoTag, BentoTypography, CheckboxInput, ErrorMessage, }, inheritAttrs: false, model: { prop: 'modelValue' }, props: { /** * @deprecated from version 2.0.0. Use the aria-label attribute instead. */ /** * Accessible name for the radio button component. It is strongly recommended to pas this value in case this component is used without a label. */ ariaLabel: { type: String, default: null }, /** * Setting this property to true will disable the checkbox in your form. */ disabled: { type: Boolean, default: false }, /** * Renders the checkbox in an error state with an error message. */ errorMessage: { type: String, default: null }, /** * Setting this property to true will show the checkbox in error state. */ hasError: { type: Boolean, default: false }, /** * The :indeterminate CSS pseudo-class represents any form element * whose state is indeterminate, such as checkboxes which have their HTML indeterminate attribute * set to true, radio buttons which are members of a group in which all radio buttons are unchecked, * and indeterminate <progress> elements. https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate * This property lets you control the input.indeterminate native property. */ indeterminate: { type: Boolean, default: false }, /** * When handling a single checkbox with a singular state, use a boolean. * * When handling multiple checkboxes, use an array of keys. * These keys exist as a set "value" prop for each checkbox. */ modelValue: { type: [Boolean, Array] as PropType<BentoCheckboxType | BentoCheckboxGroupItems>, default: false, }, /** * Highlights the checkbox is optional. */ optional: { type: Boolean, default: false }, /** * Setting this property to true will set the checkbox to read only state. */ readonly: { type: Boolean, default: false }, /** * Highlights the checkbox is required. */ required: { type: Boolean, default: false }, /** * Sets the properties of the BentoTag component that is placed next to the label. * {@see BentoTag} */ tag: { type: Object as PropType<BentoCheckboxTag>, default: undefined }, /** * The value of the component. The DOM API casts this to a string. * The browser uses "on" as the default value. * * In the case of multiple checkboxes this value is used as a unique key. */ value: { type: [String, Number], default: null }, }, emits: [BentoCheckboxEvent.UPDATE, BentoCheckboxEvent.INPUT], setup(props, { attrs, emit, slots }) { const { t } = useI18n<{ message: MessageSchema }>({ messages }); const descriptionId = generateUid('description'); const errorId = generateUid('error'); const hasSlot = useHasSlot(slots); const { disabled, indeterminate, modelValue, readonly, value } = toRefs(props); const defaultSlotValue = getSlotText(slots)('default'); const descriptionSlotValue = getSlotText(slots)('description'); useDefaultSlotValidator('bento-checkbox', slots.default, slots.description); const isCheckboxGroup = computed(() => Array.isArray(modelValue.value)); const isChecked = computed(() => { if (!isCheckboxGroup.value) { return modelValue.value as boolean; } return (modelValue.value as BentoMultiCheckboxType).some(item => item === value.value); }); const isIndeterminate = computed(() => indeterminate.value); const shouldShowError = computed( () => !props.disabled && !props.readonly && (props.hasError || !!props.errorMessage) ); const shouldShowErrorMessage = computed(() => !props.disabled && !props.readonly && !!props.errorMessage); // Content slot logic const hasContentSlot = computed(() => hasSlot('content')); const isCheckedOrIntermediate = computed(() => isIndeterminate?.value || isChecked?.value); const contentSlotAvailable = computed( () => hasContentSlot?.value && isCheckboxGroup?.value && isCheckedOrIntermediate?.value ); // ARIA fields const ariaDescribedBy = computed<HTMLAttributes['aria-describedby']>( () => `${shouldShowErrorMessage.value ? errorId : ''} ${ defaultSlotValue && descriptionSlotValue ? descriptionId : '' }`.trim() || null ); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel } = toRefs(props); const checkboxTranslation = ref(); // Translations used in the script tag need to be inside onMounted otherwise they fail in Vue 3 onMounted(() => { checkboxTranslation.value = t('checkbox'); }); // Only add aria-label if no label slot is present const computedAriaLabel = computed( () => useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, defaultFallback: hasSlot('default') ? (getSlotText(slots)('default') as string) : checkboxTranslation.value, }).value ); const getAriaHidden = (attributes: HTMLAttributes) => attributes?.['aria-hidden']; const styleAttrs = computed(() => { const { style, class: cls } = useAttrs(); return { class: cls as HTMLAttributes['class'], style: style as HTMLAttributes['style'], }; }); const conditionalClass = computed(() => ({ [`b-checkbox--${BentoCheckboxState.DISABLED}`]: disabled.value, [`b-checkbox--${BentoCheckboxState.READONLY}`]: readonly.value, })); const onChange = (newValue: BentoCheckboxType | BentoCheckboxGroupItems) => { if (disabled.value) { return; } emit(BentoCheckboxEvent.UPDATE, newValue); emit(BentoCheckboxEvent.INPUT, newValue); }; return { // Values descriptionId, errorId, ariaDescribedBy, computedAriaLabel, conditionalClass, defaultSlotValue, descriptionSlotValue, isIndeterminate, shouldShowError, shouldShowErrorMessage, contentSlotAvailable, hasContentSlot, styleAttrs, // Methods hasSlot, onChange, getAriaHidden, t, }; }, }); </script> <style lang="scss" scoped src="./checkbox.scss" />
@@ -0,0 +1,134 @@
1
+ import { Canvas, Meta, Source } from '@storybook/blocks';
2
+ import * as CheckboxGroupStories from './checkbox-group.stories';
3
+
4
+ <Meta of={CheckboxGroupStories} />
5
+
6
+ # Checkbox Group
7
+
8
+ A checkbox group component allows to group together checkboxes that are related to each other.
9
+
10
+ <Canvas of={CheckboxGroupStories.Default} />
11
+
12
+ > IMPORTANT
13
+ >
14
+ > Labels without a description are hidden by default. You must set a `label` and a `description` to display both. If
15
+ > only a `label` (without `description`) is meant to be displayed, then set `hideLabel` to `false`. See more details in
16
+ > [hide label](#hide-label).
17
+
18
+ ## Use Cases
19
+
20
+ Use a checkbox group to:
21
+
22
+ - Group checkboxes related to each other.
23
+
24
+ ### Do not use
25
+
26
+ Don't use a checkbox group if:
27
+
28
+ - You only have one checkbox. In that case, use the `checkbox` component instead.
29
+ - The checkboxes in the group are not related to each other - use singular checkboxes instead.
30
+
31
+ ### Guidelines
32
+
33
+ Checkboxes work best when stacked vertically, so it’s easier to scan the options. If you need to display them
34
+ horizontally, make sure you have enough space to display them on one line so that it’s clear that they are all related.
35
+ If there is not enough space, the checkboxes that overflow the available space will go on a new line.
36
+
37
+ ## Variations
38
+
39
+ ### Variant
40
+
41
+ It can take up the following values:
42
+
43
+ - `'vertical'`: displays the checkboxes in the group with a vertical layout. It's the default option.
44
+ - `'horizontal'`: displays the checkboxes in the group with a horizontal layout. If not enough space is available, the
45
+ corresponding checkboxes will go on a new line.
46
+
47
+ ## Modifiers
48
+
49
+ ### Disabled
50
+
51
+ Renders the `checkbox-group` component with all the `checkbox`s in a disabled state. In practice this makes the
52
+ component non-interactive and renders a not-allowed cursor on hover.
53
+
54
+ ### Readonly
55
+
56
+ Renders the `checkbox-group` component with all the `checkbox`s in a readonly state. In terms of functionality, read
57
+ only states are focusable and submittable in a form. They're considered to be 'active' from an application's point of
58
+ view. User can highlight and copy the data in them.
59
+
60
+ ### Tooltip
61
+
62
+ A tooltip with extra information for the field can be rendered by passing the desired information to the `tooltipText`
63
+ prop.
64
+
65
+ ### Error
66
+
67
+ There are two properties that control the error state of `checkbox`s within `checkbox-group`:
68
+
69
+ - `errorMessage`: renders all checkboxes with an error state and displays an error message below
70
+ - `hasError`: renders all checkboxes with an error state
71
+
72
+ `hasError` is deprecated and will be removed in the near future, so please use `errorMessage`.
73
+
74
+ ### Hide Label
75
+
76
+ The `label` is not displayed by default in Bento `1.x` if passed without a `description` property set at the same time.
77
+
78
+ To display a `label` without a `description` set, the property `hideLabel` must be set to `false` (`true` by default).
79
+
80
+ Displaying a `label` (without a `description`) will be the default in Bento `2.x` and the property `hideLabel` will be
81
+ removed.
82
+
83
+ <Source
84
+ dark
85
+ language="html"
86
+ code={`<bento-checkbox-group
87
+ :items="[
88
+ { value: 1, label: 'Label text' },
89
+ { value: 2, label: 'Label text 2', description: 'Description text' },
90
+ ]"
91
+ label="Group label"
92
+ hide-label="false"
93
+ />`}
94
+ />
95
+
96
+ ### Slots
97
+
98
+ The `content` slot is available to the vertical layout of checkbox group, and will only display the content for the
99
+ selected checked or intermediately checked options.
100
+
101
+ <Canvas of={CheckboxGroupStories.Slots} />
102
+
103
+ ## Adding tags to items
104
+
105
+ If you would like to grab a user's attention or guide them in their choice on which item to select then you may want to
106
+ use `tag` property on each `BentoCheckboxGroupItem`. This property accepts all properties a `BentoTag` accepts.
107
+
108
+ <Canvas of={CheckboxGroupStories.WithTag} />
109
+
110
+ ## Accessibility
111
+
112
+ ### Keyboard interaction
113
+
114
+ The `bento-checkbox-group` has been implemented using WAI-ARIA's pattern for grouping controls. Since the component is
115
+ implemented via a `fieldset` tag, it is reachable through keyboard navigation and follows the logical navigation flow of
116
+ the page. A detailed list of keyboard interactions is provided in the table below.
117
+
118
+ | Keyboard interaction | Output |
119
+ | -------------------- | ------------------------------------------------------------- |
120
+ | `Tab` | Moves focus to each checkbox in the group or away from it. |
121
+ | `Space` | When focus is on one of the checkboxes, it selects the value. |
122
+
123
+ ### Roles, states and properties
124
+
125
+ | ARIA attribute | is automatic? | fallback used | manual override |
126
+ | -------------- | ---------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
127
+ | `role` | Yes, through the semantic `fieldset` element, which groups all its child elements. | - | - |
128
+ | `form` | No, it needs to be manually set via the `form` property. | - | The user can pass the form id with which the group needs to be associated with. This allows to use such component also externally from the form and have the value submitted along with the form. |
129
+ | `disabled` | No, it needs to be set by setting the `disabled` prop to `true`. | - | If set to reu, it disables all the elements contained in the group. |
130
+
131
+ ## Resources
132
+
133
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=1934-27878&t=ir5bsJdP6TgVgXy0-0)
134
+ - [WAI-ARIA guidelines for grouping controls](https://www.w3.org/WAI/tutorials/forms/grouping/)
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { ref } from 'vue'; import { BentoCheckboxGroupVariant } from '@/components/checkbox/components/checkbox-group/checkbox-group.types'; import { BentoCheckboxEvent } from '@/components/checkbox/checkbox.types'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoCheckboxGroup from './checkbox-group.vue'; import { BentoTag } from '@/components/tag'; const GROUP_LABEL = 'Group label'; const DESCRIPTION = 'Optional description'; const CHECKBOX_DEFAULT_LABEL = 'Label text'; const CHECKBOX_DEFAULT_SUB_LABEL = 'Sub label text'; const ERROR_MESSAGE = 'Error message text'; const ITEMS = [ { value: 1, label: CHECKBOX_DEFAULT_LABEL }, { value: 2, label: `${CHECKBOX_DEFAULT_LABEL} 2`, description: CHECKBOX_DEFAULT_SUB_LABEL }, { value: 3, label: 'Readonly checkbox', readonly: true }, { value: 4, label: 'Error message checkbox', errorMessage: ERROR_MESSAGE }, ]; export default { title: 'Checkbox/Checkbox Group', component: BentoCheckboxGroup, argTypes: { variant: { options: Object.values(BentoCheckboxGroupVariant), mapping: BentoCheckboxGroupVariant, control: { type: 'select', }, }, items: { table: { type: { summary: 'BentoCheckboxGroupItems' }, }, }, disabled: { control: { type: 'boolean', }, }, hasError: { control: { type: 'boolean' }, table: { category: 'Deprecated' }, }, [BentoCheckboxEvent.UPDATE]: { name: BentoCheckboxEvent.UPDATE, defaultValue: '', description: 'Input event handler.', table: { type: { summary: '(newGroupValue: Array<number | string | null>) => void' }, }, control: { disable: true }, }, [BentoCheckboxEvent.INPUT]: { name: BentoCheckboxEvent.INPUT, defaultValue: '', description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value` instead', table: { category: 'Deprecated', type: { summary: '(newGroupValue: Array<number | string | null>) => void' }, }, control: { disable: true }, }, }, } as Meta; type Story = StoryObj<typeof BentoCheckboxGroup>; const defaultCode = ` <template> <!-- With model-value --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" :model-value="yourModelValueVariable" label="Group label" description="Optional description" @update:model-value="yourUpdateHandler" /> <!-- With v-model Vue2/Vue3 --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" v-model="yourModelValueVariable" label="Group label" description="Optional description" /> </template> <script setup lang="ts"> import { BentoCheckboxGroup } from '@adyen/bento-vue2' const yourModelValueVariable = ref<Array<number>>([1]); const yourUpdateHandler = (newGroupValue: Array<number>) => { yourModelValueVariable.value = newGroupValue; } </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoCheckboxGroup }, props: Object.keys(argTypes), template: ` <div> <bento-checkbox-group v-bind="args" :model-value="selectedValue" @update:model-value="clickedAction" /> </div> `, setup(props) { const selectedValue = ref(undefined); const clickedAction = value => { selectedValue.value = value; action(BentoCheckboxEvent.UPDATE)(value); }; return { // Values selectedValue, args: isVue2 ? props : _args, // Events clickedAction, }; }, }), args: { items: ITEMS, variant: 'vertical', label: GROUP_LABEL, description: DESCRIPTION, }, parameters: storybookDocsParameter(defaultCode), }; export const Slots: Story = { render: (_args, { argTypes }) => ({ components: { BentoCheckboxGroup, BentoTag }, props: Object.keys(argTypes), template: ` <div> <bento-checkbox-group v-bind="args" :model-value="selectedValue" @update:model-value="clickedAction" > <template #content="{ value }"> <bento-tag :label="contentString(value)"/> </template> </bento-checkbox-group> </div> `, setup(props) { const selectedValue = ref([ITEMS[1].value]); const clickedAction = value => { selectedValue.value = value; action(BentoCheckboxEvent.UPDATE)(value); }; const contentString = value => `Slot for value ${value}`; return { // Values selectedValue, args: isVue2 ? props : _args, // Methods contentString, // Events clickedAction, }; }, }), args: { items: ITEMS, variant: 'vertical', label: GROUP_LABEL, description: DESCRIPTION, }, parameters: storybookDocsParameter(` <template> <!-- With model-value --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" :model-value="yourModelValueVariable" label="Group label" description="Optional description" @update:model-value="yourUpdateHandler" > <template #content="{ value }"> <bento-tag :label="value" /> </template> </bento-checkbox-group> <!-- With v-model Vue2/Vue3 --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" v-model="yourModelValueVariable" label="Group label" description="Optional description" > <template #content="{ value }"> <bento-tag :label="value" /> </template> </bento-checkbox-group> </template> <script setup lang="ts"> import { BentoCheckboxGroup } from '@adyen/bento-vue2' const yourModelValueVariable = ref<Array<number>>([1]); const yourUpdateHandler = (newGroupValue: Array<number>) => { yourModelValueVariable.value = newGroupValue; } </script> `), }; const withTagCode = ` <template> <!-- With model-value --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text', tag: { label: 'Recommended' } }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" :model-value="yourModelValueVariable" label="Group label" description="Optional description" @update:model-value="yourUpdateHandler" /> <!-- With v-model Vue2/Vue3 --> <bento-checkbox-group :items="[ { value: 1, label: 'Label text', tag: { label: 'Recommended' }, { value: 2, label: 'Label text 2', description: 'Sub label text' }, ]" v-model="yourModelValueVariable" label="Group label" description="Optional description" /> </template> <script setup lang="ts"> import { BentoCheckboxGroup } from '@adyen/bento-vue2' const yourModelValueVariable = ref<Array<number>>([1]); const yourUpdateHandler = (newGroupValue: Array<number>) => { yourModelValueVariable.value = newGroupValue; } </script> `; export const WithTag: Story = { ...Default, args: { ...Default.args, items: [ { value: 1, label: CHECKBOX_DEFAULT_LABEL, tag: { label: 'Recommended' } }, { value: 2, label: `${CHECKBOX_DEFAULT_LABEL} 2`, description: CHECKBOX_DEFAULT_SUB_LABEL }, { value: 3, label: 'Readonly checkbox', readonly: true }, { value: 4, label: 'Error message checkbox', errorMessage: ERROR_MESSAGE }, ], }, parameters: storybookDocsParameter(withTagCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-checkbox-group"> <controls-group class="b-checkbox-group__checkboxes" :aria-describedby="shouldShowGroupErrorMessage ? errorId : ''" component-name="bento-checkbox-group" :class="conditionalClasses" :description="description" :disabled="disabled" :form="formId" :hide-label="hideLabel" :label="label" :optional="optional" :required="required" :tooltip-text="tooltipText" > <bento-checkbox v-for="item in items" :key="item.value" :model-value="inputValue" :value="item.value" :disabled="disabled || item.disabled" :has-error="shouldShowGroupError || item.hasError" :error-message="shouldShowGroupError ? null : item.errorMessage" :readonly="isReadOnly || item.readonly" :tag="item.tag" @update:model-value="onModelValueUpdate" > <template #default>{{ item.label }}</template> <template v-if="item.description" #description>{{ item.description }}</template> <template v-if="hasContentSlot('content')" #content> <slot name="content" v-bind="item"></slot> </template> </bento-checkbox> </controls-group> <error-message v-if="shouldShowGroupErrorMessage" :id="errorId" :error-message="errorMessage" class="b-checkbox-group__error-message" /> </div> </template> <script setup lang="ts"> import { computed, onMounted, type PropType, ref, toRef, useSlots, watch } from 'vue'; import { ControlsGroup } from '@/components/internal/controls-group'; import { ErrorMessage } from '@/components/internal/error-message'; import { type BentoCheckboxGroupEmits, type BentoCheckboxGroupItems, BentoCheckboxGroupVariant, } from './checkbox-group.types'; import BentoCheckbox from '@/components/checkbox/checkbox.vue'; import { generateUid } from '@/core/utils/ts'; import { deprecate } from '@/utils/ts/deprecate'; import { useFormLayoutFieldLoading } from '@/composables/use-form-layout-loading/use-form-layout-loading'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; const props = defineProps({ /** * Descriptive supporting text visbile underneath the label. */ description: { type: String, default: null }, /** * If set to true, renders all checkboxes in the group in a disabled state. */ disabled: { type: Boolean, default: false }, /** * Renders all checkboxes in an error state with an error message. */ errorMessage: { type: String, default: null }, /** * The form id the checkbox group is associated with. Defaults to null, as a checkbox group can also be used standalone. */ formId: { type: String, default: null }, /** * If set to true, renders all checkboxes in the group in an error state. * * @deprecated Use "errorMessage" prop instead */ hasError: { type: Boolean, default: false }, /** * Shows/hides the group label. */ hideLabel: { type: Boolean, default: null }, /** * The checkbox group's items. */ items: { type: Array as PropType<BentoCheckboxGroupItems>, required: true }, /** * The name associated with the group. Required for accessibility purposes. */ label: { type: String, required: true }, /** * Determines the initially selected value of the group. Corresponds to the BentoCheckboxButtonItem's value. * Defaults to no value selected. */ modelValue: { type: Array as PropType<Array<number | string | null>>, default: () => [] }, /** * Indicates the field is optional. */ optional: { type: Boolean, default: false }, /** * If set to true, renders all checkboxes in the group in a readonly state. */ readonly: { type: Boolean, default: false }, /** * Indicates the field is required. */ required: { type: Boolean, default: false }, /** * Tooltip message. */ tooltipText: { type: String, default: null }, /** * The checkbox group's layout. It can be vertical or horizontal. Defaults to vertical. */ variant: { type: String as PropType<BentoCheckboxGroupVariant | `${BentoCheckboxGroupVariant}`>, default: BentoCheckboxGroupVariant.VERTICAL, validator: (value: BentoCheckboxGroupVariant) => Object.values(BentoCheckboxGroupVariant).includes(value), }, }); const emit = defineEmits<BentoCheckboxGroupEmits>(); const slots = useSlots(); const errorId = generateUid('error'); const { isReadOnly } = useFormLayoutFieldLoading(toRef(props, 'readonly')); const { emitValue } = useFormFieldEmits(emit); const shouldShowGroupError = computed( () => !props.disabled && !isReadOnly.value && (!!props.errorMessage || props.hasError) ); const shouldShowGroupErrorMessage = computed(() => !props.disabled && !isReadOnly.value && !!props.errorMessage); const conditionalClasses = computed(() => ({ 'b-checkbox-group__checkboxes--horizontal': props.variant === BentoCheckboxGroupVariant.HORIZONTAL, })); // Slots logic // Temporary, use-has-slots composable doesn't currently work for // vue3 named scoped slots const hasContentSlot = (name: string) => !!slots[name]; const shouldShowSlotsError = computed(() => hasContentSlot('content') && props.variant !== 'vertical'); watch( shouldShowSlotsError, newValue => { if (newValue) { throw new Error('The content slot is only available for the vertical checkbox group layout'); } }, { immediate: true } ); const inputValue = ref(props.modelValue); watch( () => props.modelValue, () => { inputValue.value = props.modelValue; } ); const onModelValueUpdate = (value: any) => { inputValue.value = value; emitValue(value); }; onMounted(() => { if (props.hasError) { deprecate( 'BentoCheckboxGroup "hasError" property', `Use the BentoCheckboxGroup "errorMessage" property instead to indicate that there is a modifier error. Having the field as "undefined" will hide/remove the error. <bento-checkbox-group errorMessage="Error message text" />`, '2.0.0' ); } }); </script> <script lang="ts"> const COMPONENT_NAME = 'bento-checkbox-group'; /** * A checkbox group component should be used whenever it is necessary to group a set of checkboxes that are related together * This component conforms to the official WAI-ARIA guidelines for grouping controls: https://www.w3.org/WAI/tutorials/forms/grouping/ * * import { BentoCheckboxGroup } from '@adyen/bento-vue2/'; * * export default { * components: { BentoCheckboxGroup }, * template: ` * <bento-checkbox-group * label="Your group label" * :items="[ * { value: 1, label: 'Your label' }, * { value: 2, label: 'Another label', description: 'Your sub label' }, * ]" * /> * ` * }; */ export default { name: COMPONENT_NAME, model: { prop: 'modelValue' }, }; </script> <style lang="scss" scoped src="./checkbox-group.scss" />
@@ -0,0 +1,51 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as ChipStories from './chip.stories';
3
+
4
+ <Meta of={ChipStories} />
5
+
6
+ # Chip
7
+
8
+ Chip is a dismissible tag-like component that can be used to display selected items, which may need to be removed by the
9
+ user.
10
+
11
+ <Canvas of={ChipStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use a Chip if:
16
+
17
+ - you need to show a selected item that can be removed by the user
18
+
19
+ ### Do not use
20
+
21
+ - for static tags - use [tag](https://adyen-bento.netlify.app/?path=/docs/components-tag--docs) component instead
22
+
23
+ ## Modifiers
24
+
25
+ ### Condensed
26
+
27
+ You can render `bento-chip` in a smaller size by passing the `condensed` prop.
28
+
29
+ - use `condensed` for small, cramped spaces, for example inside of a dropdown
30
+ - use default size for larger spaces
31
+ - do not mix different them in one layout or in close proximity
32
+
33
+ ### Character limit
34
+
35
+ You can limit the number of characters displayed in the chip by passing `characterLimit` prop. Additional characters
36
+ will be truncated.
37
+
38
+ ## Accessibility
39
+
40
+ ### Keyboard interaction
41
+
42
+ - User can use `Tab` key to move focus to the chip
43
+ - User can use `Spacebar`/`Enter` to trigger the chip's action
44
+
45
+ ### Roles, states and properties
46
+
47
+ Chip uses `BaseButton` as a base as it acts like a button. It uses a `button` tag.
48
+
49
+ ## Resources
50
+
51
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=41484-71284&p=f&m=dev)
@@ -0,0 +1 @@
1
+ import { isVue2 } from 'vue-demi'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoChip from './chip.vue'; import type { Meta, StoryObj } from '@storybook/vue'; const meta: Meta = { title: 'Chip', component: BentoChip, }; export default meta; type Story = StoryObj<typeof BentoChip>; const defaultCode = ` <bento-chip label="Chip label" :character-limit="20" @click="removeChip" /> <script setup> const removeChip = () => { // whatever action you want to trigger when the user clicks the chip // should remove the chip } </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoChip }, props: Object.keys(argTypes), template: ` <bento-chip v-bind="args" @click="clickAction" /> `, setup(props) { return { // Values args: isVue2 ? props : _args, clickAction: action('click'), }; }, }), args: { label: 'Chip label', characterLimit: 20, condensed: false, disabled: false, readonly: false, error: false, }, parameters: storybookDocsParameter(defaultCode), };
@@ -0,0 +1 @@
1
+ <template> <base-button class="b-chip" :disabled="disabled" :class="conditionalClasses" @click="emit('click')"> <bento-typography el="span" :variant="computedTypographyVariant" class="b-chip__text"> {{ computedLabel }} </bento-typography> <template #iconRight><cross-small-icon :svg-title="t('remove')" class="b-chip__icon" /></template> </base-button> </template> <script setup lang="ts"> import { BaseButton } from '@/components/internal/base-button'; import { BentoTypography, type BentoTypographyVariant } from '@/components/typography'; import CrossSmallIcon from '@adyen/ui-assets-icons-16/vue/cross-small'; import { useI18n } from '@/utils/ts/i18n'; import { truncate } from '@/utils/ts/truncate'; import type { BentoChipProps } from './chip.types'; import messages from './messages.json'; import { computed } from 'vue'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoChipProps>(), { disabled: false, readonly: false, error: false, condensed: false, characterLimit: null, }); const emit = defineEmits<{ /** * Emits when chip is clicked */ (e: 'click'): void; }>(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const computedLabel = computed(() => props.characterLimit ? truncate(props.label, props.characterLimit) : props.label ); const computedTypographyVariant = computed<`${BentoTypographyVariant}`>(() => props.condensed ? 'caption' : 'body' ); const conditionalClasses = computed(() => ({ 'b-chip--condensed': props.condensed, 'b-chip--readonly': props.readonly && !props.disabled, 'b-chip--error': props.error, })); </script> <script lang="ts"> /** * Chip is a dismissible tag-like component that can be used to display selected items, which may need to be removed by the user. * * @example * import { BentoChip } from '@adyen/bento-vue2'; * * export default { * components: { BentoChip }, * template: ` * <bento-chip * label="Label" * :character-limit="20" * condensed * @click="removeChip()" * * /> * ` * } */ export default { i18n: { messages } }; </script> <style lang="scss" scoped src="./chip.scss" />