@adyen/bento-mcp 0.11.0 → 0.12.1

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 (424) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +24 -2
  3. package/SKILL.md +37 -7
  4. package/dist/assets/components/accordion/accordion.stories.ts +1 -1
  5. package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -1
  6. package/dist/assets/components/accordion/components/accordion-item.vue +1 -1
  7. package/dist/assets/components/action-bar/action-bar.docs.mdx +10 -3
  8. package/dist/assets/components/action-bar/action-bar.internal.types.ts +1 -0
  9. package/dist/assets/components/action-bar/action-bar.stories.ts +1 -1
  10. package/dist/assets/components/action-bar/action-bar.types.ts +1 -1
  11. package/dist/assets/components/action-bar/action-bar.vue +1 -1
  12. package/dist/assets/components/ai-tag/ai-tag.stories.ts +1 -1
  13. package/dist/assets/components/ai-tag/ai-tag.vue +1 -1
  14. package/dist/assets/components/alert/alert.stories.ts +1 -1
  15. package/dist/assets/components/alert/alert.types.ts +1 -1
  16. package/dist/assets/components/anchor-scroller/anchor-scroller.stories.ts +1 -1
  17. package/dist/assets/components/anchor-scroller/anchor-scroller.vue +1 -1
  18. package/dist/assets/components/avatar/avatar.internal.types.ts +1 -0
  19. package/dist/assets/components/avatar/avatar.stories.ts +1 -1
  20. package/dist/assets/components/avatar/avatar.types.ts +1 -1
  21. package/dist/assets/components/button/button.docs.mdx +4 -3
  22. package/dist/assets/components/button/button.stories.ts +1 -1
  23. package/dist/assets/components/button/button.types.ts +1 -1
  24. package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -1
  25. package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -1
  26. package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +3 -0
  27. package/dist/assets/components/button/components/button-actions/button-actions.internal.types.ts +1 -0
  28. package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -1
  29. package/dist/assets/components/button/components/button-actions/button-actions.types.ts +1 -1
  30. package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -1
  31. package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +26 -23
  32. package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -1
  33. package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -1
  34. package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -1
  35. package/dist/assets/components/card/card.docs.mdx +18 -1
  36. package/dist/assets/components/card/card.stories.ts +1 -1
  37. package/dist/assets/components/card/card.types.ts +1 -1
  38. package/dist/assets/components/card/card.vue +1 -1
  39. package/dist/assets/components/checkbox/checkbox.stories.ts +1 -1
  40. package/dist/assets/components/checkbox/checkbox.types.ts +1 -1
  41. package/dist/assets/components/checkbox/checkbox.vue +1 -1
  42. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +9 -14
  43. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -1
  44. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.types.ts +1 -1
  45. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -1
  46. package/dist/assets/components/chip/chip.stories.ts +1 -1
  47. package/dist/assets/components/chip/chip.vue +1 -1
  48. package/dist/assets/components/click-outside/click-outside.stories.ts +1 -1
  49. package/dist/assets/components/code-snippet/code-snippet.types.ts +1 -1
  50. package/dist/assets/components/code-snippet/code-snippet.vue +1 -1
  51. package/dist/assets/components/country/country.stories.ts +1 -1
  52. package/dist/assets/components/country/country.types.ts +1 -1
  53. package/dist/assets/components/country/country.vue +1 -1
  54. package/dist/assets/components/currency/currency.stories.ts +1 -1
  55. package/dist/assets/components/currency/currency.vue +1 -1
  56. package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -1
  57. package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.types.ts +1 -1
  58. package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -1
  59. package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -1
  60. package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -1
  61. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -1
  62. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -1
  63. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -1
  64. package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.stories.ts +1 -1
  65. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -1
  66. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -1
  67. package/dist/assets/components/data-grid/data-grid.docs.mdx +14 -61
  68. package/dist/assets/components/data-grid/data-grid.stories.ts +1 -1
  69. package/dist/assets/components/data-grid/data-grid.vue +1 -1
  70. package/dist/assets/components/date/date.stories.ts +1 -1
  71. package/dist/assets/components/date-picker/date-picker.stories.ts +1 -1
  72. package/dist/assets/components/date-picker/date-picker.types.ts +1 -1
  73. package/dist/assets/components/date-picker/date-picker.vue +1 -1
  74. package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -1
  75. package/dist/assets/components/date-range-picker/components/date-time-input/date-time-input.types.ts +1 -1
  76. package/dist/assets/components/date-range-picker/components/date-time-input/date-time-input.vue +1 -1
  77. package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +31 -6
  78. package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -1
  79. package/dist/assets/components/date-range-picker/date-range-picker.types.ts +1 -1
  80. package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -1
  81. package/dist/assets/components/date-time-picker/date-time-picker.stories.ts +1 -1
  82. package/dist/assets/components/date-time-picker/date-time-picker.types.ts +1 -1
  83. package/dist/assets/components/divider/divider.internal.types.ts +1 -0
  84. package/dist/assets/components/divider/divider.stories.ts +1 -1
  85. package/dist/assets/components/divider/divider.types.ts +1 -1
  86. package/dist/assets/components/divider/divider.vue +1 -1
  87. package/dist/assets/components/draggable/components/draggable-handle/draggable-handle.stories.ts +1 -1
  88. package/dist/assets/components/draggable/draggable.stories.ts +1 -1
  89. package/dist/assets/components/draggable/draggable.vue +1 -1
  90. package/dist/assets/components/drawer/drawer.stories.ts +1 -1
  91. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.internal.types.ts +1 -0
  92. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.types.ts +1 -1
  93. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -1
  94. package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -1
  95. package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -1
  96. package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -1
  97. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -1
  98. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.types.ts +1 -1
  99. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -1
  100. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -1
  101. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.types.ts +1 -1
  102. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -1
  103. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -1
  104. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.types.ts +1 -1
  105. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -1
  106. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -1
  107. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.types.ts +1 -1
  108. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -1
  109. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -1
  110. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.types.ts +1 -1
  111. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -1
  112. package/dist/assets/components/dropdown/dropdown.docs.mdx +4 -40
  113. package/dist/assets/components/dropdown/dropdown.internal.types.ts +1 -0
  114. package/dist/assets/components/dropdown/dropdown.stories.ts +1 -1
  115. package/dist/assets/components/dropdown/dropdown.types.ts +1 -1
  116. package/dist/assets/components/dropdown/dropdown.vue +1 -1
  117. package/dist/assets/components/empty-state/empty-state.stories.ts +1 -1
  118. package/dist/assets/components/empty-state/empty-state.types.ts +1 -1
  119. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -1
  120. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.types.ts +1 -1
  121. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -1
  122. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.types.ts +1 -1
  123. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -1
  124. package/dist/assets/components/file-uploader/file-uploader.internal.types.ts +1 -0
  125. package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -1
  126. package/dist/assets/components/file-uploader/file-uploader.types.ts +1 -1
  127. package/dist/assets/components/file-uploader/file-uploader.vue +1 -1
  128. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -1
  129. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.types.ts +1 -1
  130. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -1
  131. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-filter-selection/all-filters-modal-filter-selection.types.ts +1 -0
  132. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-filter-selection/all-filters-modal-filter-selection.vue +1 -0
  133. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-selected-filter/all-filters-modal-selected-filter.vue +1 -0
  134. package/dist/assets/components/filter-bar/components/base-filter/base-filter.internal.types.ts +1 -0
  135. package/dist/assets/components/filter-bar/components/base-filter/base-filter.types.ts +1 -1
  136. package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -1
  137. package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -1
  138. package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -1
  139. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -1
  140. package/dist/assets/components/filter-bar/components/input-filter/input-filter.types.ts +1 -1
  141. package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -1
  142. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -1
  143. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -1
  144. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -1
  145. package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -1
  146. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -1
  147. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -1
  148. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -1
  149. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -1
  150. package/dist/assets/components/filter-bar/filter-bar.docs.mdx +4 -62
  151. package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -1
  152. package/dist/assets/components/filter-bar/filter-bar.types.ts +1 -1
  153. package/dist/assets/components/filter-bar/filter-bar.vue +1 -1
  154. package/dist/assets/components/form-layout/form-layout.docs.mdx +0 -23
  155. package/dist/assets/components/form-layout/form-layout.stories.ts +1 -1
  156. package/dist/assets/components/header/header.stories.ts +1 -1
  157. package/dist/assets/components/header/header.types.ts +1 -1
  158. package/dist/assets/components/header/header.vue +1 -1
  159. package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -1
  160. package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.types.ts +1 -1
  161. package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -1
  162. package/dist/assets/components/header-with-views/header-with-views.internal.types.ts +1 -0
  163. package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -1
  164. package/dist/assets/components/header-with-views/header-with-views.types.ts +1 -1
  165. package/dist/assets/components/header-with-views/header-with-views.vue +1 -1
  166. package/dist/assets/components/image/image.stories.ts +1 -1
  167. package/dist/assets/components/info-icon/info-icon.stories.ts +1 -1
  168. package/dist/assets/components/input-field/input-field.docs.mdx +1 -13
  169. package/dist/assets/components/input-field/input-field.stories.ts +1 -1
  170. package/dist/assets/components/input-field/input-field.types.ts +1 -1
  171. package/dist/assets/components/input-field/input-field.vue +1 -1
  172. package/dist/assets/components/input-field-password/input-field-password.internal.types.ts +1 -0
  173. package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -1
  174. package/dist/assets/components/input-field-password/input-field-password.types.ts +1 -1
  175. package/dist/assets/components/input-field-password/input-field-password.vue +1 -1
  176. package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -1
  177. package/dist/assets/components/input-field-phone-number/input-field-phone-number.types.ts +1 -1
  178. package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -1
  179. package/dist/assets/components/input-time/input-time.docs.mdx +17 -0
  180. package/dist/assets/components/input-time/input-time.stories.ts +1 -1
  181. package/dist/assets/components/input-time/input-time.types.ts +1 -1
  182. package/dist/assets/components/input-time/input-time.vue +1 -1
  183. package/dist/assets/components/inspector/inspector.stories.ts +1 -1
  184. package/dist/assets/components/inspector/inspector.vue +1 -1
  185. package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -1
  186. package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -1
  187. package/dist/assets/components/layout/layout.docs.mdx +10 -0
  188. package/dist/assets/components/layout/layout.internal.types.ts +1 -0
  189. package/dist/assets/components/layout/layout.stories.ts +1 -1
  190. package/dist/assets/components/layout/layout.types.ts +1 -1
  191. package/dist/assets/components/link/link.stories.ts +1 -1
  192. package/dist/assets/components/link/link.types.ts +1 -1
  193. package/dist/assets/components/link/link.vue +1 -1
  194. package/dist/assets/components/list/list.stories.ts +1 -1
  195. package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -1
  196. package/dist/assets/components/menu/components/menu-item/menu-item.types.ts +1 -1
  197. package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -1
  198. package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -1
  199. package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -1
  200. package/dist/assets/components/menu/menu.docs.mdx +9 -0
  201. package/dist/assets/components/menu/menu.stories.ts +1 -1
  202. package/dist/assets/components/menu/menu.types.ts +1 -1
  203. package/dist/assets/components/menu/menu.vue +1 -1
  204. package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -1
  205. package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -1
  206. package/dist/assets/components/modal/modal.internal.types.ts +1 -0
  207. package/dist/assets/components/modal/modal.stories.ts +1 -1
  208. package/dist/assets/components/modal/modal.types.ts +1 -1
  209. package/dist/assets/components/modal/modal.vue +1 -1
  210. package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -1
  211. package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -1
  212. package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -1
  213. package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.internal.types.ts +1 -0
  214. package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.types.ts +1 -1
  215. package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.vue +1 -1
  216. package/dist/assets/components/navigation-menu/components/navigation-menu-item/navigation-menu-item.vue +1 -1
  217. package/dist/assets/components/navigation-menu/navigation-menu.internal.types.ts +1 -0
  218. package/dist/assets/components/navigation-menu/navigation-menu.stories.ts +1 -1
  219. package/dist/assets/components/navigation-menu/navigation-menu.types.ts +1 -1
  220. package/dist/assets/components/navigation-menu/navigation-menu.vue +1 -1
  221. package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -1
  222. package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -1
  223. package/dist/assets/components/pagination/pagination.stories.ts +1 -1
  224. package/dist/assets/components/pagination/pagination.types.ts +1 -1
  225. package/dist/assets/components/pagination/pagination.vue +1 -1
  226. package/dist/assets/components/payment-method/payment-method.stories.ts +1 -1
  227. package/dist/assets/components/payment-method/payment-method.types.ts +1 -1
  228. package/dist/assets/components/popover/popover.docs.mdx +32 -9
  229. package/dist/assets/components/popover/popover.stories.ts +1 -1
  230. package/dist/assets/components/popover/popover.types.ts +1 -1
  231. package/dist/assets/components/popover/popover.vue +1 -1
  232. package/dist/assets/components/popper-container/popper-container.types.ts +1 -1
  233. package/dist/assets/components/popper-container/popper-container.vue +1 -1
  234. package/dist/assets/components/progress-bar/progress-bar.stories.ts +1 -1
  235. package/dist/assets/components/progress-bar/progress-bar.vue +1 -1
  236. package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -1
  237. package/dist/assets/components/promo-banner/promo-banner.types.ts +1 -1
  238. package/dist/assets/components/promo-banner/promo-banner.vue +1 -1
  239. package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -1
  240. package/dist/assets/components/radio-group/radio-group.docs.mdx +1 -6
  241. package/dist/assets/components/radio-group/radio-group.stories.ts +1 -1
  242. package/dist/assets/components/radio-group/radio-group.types.ts +1 -1
  243. package/dist/assets/components/radio-group/radio-group.vue +1 -1
  244. package/dist/assets/components/rich-text-editor/rich-text-editor.internal.types.ts +1 -0
  245. package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -1
  246. package/dist/assets/components/rich-text-editor/rich-text-editor.types.ts +1 -1
  247. package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -1
  248. package/dist/assets/components/search-bar/search-bar.stories.ts +1 -1
  249. package/dist/assets/components/search-bar/search-bar.vue +1 -1
  250. package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -1
  251. package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -1
  252. package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -1
  253. package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -1
  254. package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -1
  255. package/dist/assets/components/segmented-control/segmented-control.types.ts +1 -1
  256. package/dist/assets/components/segmented-control/segmented-control.vue +1 -1
  257. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +12 -13
  258. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -1
  259. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -1
  260. package/dist/assets/components/selection-card/selection-card.stories.ts +1 -1
  261. package/dist/assets/components/selection-card/selection-card.types.ts +1 -1
  262. package/dist/assets/components/selection-card/selection-card.vue +1 -1
  263. package/dist/assets/components/sidepanel/sidepanel.docs.mdx +1 -1
  264. package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -1
  265. package/dist/assets/components/sidepanel/sidepanel.vue +1 -1
  266. package/dist/assets/components/status/status.stories.ts +1 -1
  267. package/dist/assets/components/status/status.types.ts +1 -1
  268. package/dist/assets/components/stepper/components/step/step.vue +1 -1
  269. package/dist/assets/components/stepper/stepper.stories.ts +1 -1
  270. package/dist/assets/components/stepper/stepper.vue +1 -1
  271. package/dist/assets/components/structured-list/structured-list.stories.ts +1 -1
  272. package/dist/assets/components/structured-list/structured-list.vue +1 -1
  273. package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.types.ts +1 -1
  274. package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -1
  275. package/dist/assets/components/summary-grid/summary-grid.types.ts +1 -1
  276. package/dist/assets/components/table-of-contents/components/table-of-contents-list/table-of-contents-list.vue +1 -1
  277. package/dist/assets/components/table-of-contents/table-of-contents.internal.types.ts +1 -0
  278. package/dist/assets/components/table-of-contents/table-of-contents.stories.ts +1 -1
  279. package/dist/assets/components/table-of-contents/table-of-contents.types.ts +1 -1
  280. package/dist/assets/components/table-of-contents/table-of-contents.vue +1 -1
  281. package/dist/assets/components/tabs/components/tab.internal.types.ts +1 -0
  282. package/dist/assets/components/tabs/components/tab.types.ts +1 -1
  283. package/dist/assets/components/tabs/components/tab.vue +1 -1
  284. package/dist/assets/components/tabs/tabs.stories.ts +1 -1
  285. package/dist/assets/components/tabs/tabs.vue +1 -1
  286. package/dist/assets/components/tag/tag.docs.mdx +1 -1
  287. package/dist/assets/components/tag/tag.stories.ts +1 -1
  288. package/dist/assets/components/tag/tag.types.ts +1 -1
  289. package/dist/assets/components/tag/tag.vue +1 -1
  290. package/dist/assets/components/textarea/textarea.docs.mdx +0 -7
  291. package/dist/assets/components/textarea/textarea.stories.ts +1 -1
  292. package/dist/assets/components/textarea/textarea.types.ts +1 -1
  293. package/dist/assets/components/textarea/textarea.vue +1 -1
  294. package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -1
  295. package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -1
  296. package/dist/assets/components/timeline/timeline.docs.mdx +0 -10
  297. package/dist/assets/components/timeline/timeline.internal.types.ts +1 -0
  298. package/dist/assets/components/timeline/timeline.stories.ts +1 -1
  299. package/dist/assets/components/timeline/timeline.types.ts +1 -1
  300. package/dist/assets/components/timeline/timeline.vue +1 -1
  301. package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -1
  302. package/dist/assets/components/toggle/toggle.stories.ts +1 -1
  303. package/dist/assets/components/toggle/toggle.types.ts +1 -1
  304. package/dist/assets/components/toggle/toggle.vue +1 -1
  305. package/dist/assets/components/tutorial/tutorial.stories.ts +1 -1
  306. package/dist/assets/components/typography/typography.stories.ts +1 -1
  307. package/dist/assets/components/typography/typography.types.ts +1 -1
  308. package/dist/assets/components/typography/typography.vue +1 -1
  309. package/dist/assets/composables/use-bento-base-filter/index.ts +1 -0
  310. package/dist/assets/composables/use-bento-base-filter/use-bento-base-filter.ts +1 -0
  311. package/dist/assets/composables/use-bento-base-filter/utils/index.ts +1 -0
  312. package/dist/assets/composables/use-bento-base-filter/utils/utils.ts +1 -0
  313. package/dist/assets/composables/use-bento-currency/index.ts +1 -0
  314. package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -1
  315. package/dist/assets/composables/use-bento-currency/use-bento-currency.ts +1 -0
  316. package/dist/assets/composables/use-bento-delayed-hover/index.ts +1 -0
  317. package/dist/assets/composables/use-bento-delayed-hover/use-bento-delayed-hover.stories.ts +1 -1
  318. package/dist/assets/composables/use-bento-delayed-hover/use-bento-delayed-hover.ts +1 -0
  319. package/dist/assets/composables/use-bento-theme/index.ts +1 -0
  320. package/dist/assets/composables/use-bento-theme/install.ts +1 -1
  321. package/dist/assets/composables/use-bento-theme/install.types.ts +1 -1
  322. package/dist/assets/composables/use-bento-theme/use-bento-theme.ts +1 -0
  323. package/dist/assets/composables/use-bento-toast-controller/index.ts +1 -0
  324. package/dist/assets/composables/use-bento-toast-controller/toast-stack.ts +1 -0
  325. package/dist/assets/composables/use-bento-toast-controller/use-bento-toast-controller.ts +1 -0
  326. package/dist/assets/composables/use-click-outside/index.ts +1 -0
  327. package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -1
  328. package/dist/assets/composables/use-click-outside/use-click-outside.ts +1 -0
  329. package/dist/assets/composables/use-form-validate/index.ts +1 -0
  330. package/dist/assets/composables/use-form-validate/use-form.ts +1 -0
  331. package/dist/assets/composables/use-form-validate/use-form.types.ts +1 -1
  332. package/dist/assets/composables.json +9 -0
  333. package/dist/assets/deprecations.md +132 -236
  334. package/dist/assets/directives/highlight/highlight.stories.ts +1 -1
  335. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -1
  336. package/dist/assets/directives/tooltip/tooltip.internal.types.ts +1 -0
  337. package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -1
  338. package/dist/assets/directives/tooltip/tooltip.types.ts +1 -1
  339. package/dist/assets/icons.json +1 -0
  340. package/dist/assets/install.ts +1 -1
  341. package/dist/assets/tailwind/README.md +3 -3
  342. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -1
  343. package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -1
  344. package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -1
  345. package/dist/assets/usage.json +77 -77
  346. package/dist/assets/variables.css +113 -101
  347. package/dist/main.js +228 -34
  348. package/package.json +2 -2
  349. package/dist/assets/components/internal/base-button/base-button.types.ts +0 -1
  350. package/dist/assets/components/internal/base-button/base-button.vue +0 -1
  351. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +0 -1
  352. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.types.ts +0 -1
  353. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +0 -1
  354. package/dist/assets/components/internal/calendar/calendar.stories.ts +0 -1
  355. package/dist/assets/components/internal/calendar/calendar.types.ts +0 -1
  356. package/dist/assets/components/internal/calendar/calendar.vue +0 -1
  357. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +0 -1
  358. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.types.ts +0 -1
  359. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +0 -1
  360. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +0 -1
  361. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +0 -1
  362. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.types.ts +0 -1
  363. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +0 -1
  364. package/dist/assets/components/internal/calendar/components/calendar-month/composables/granular-min-max-date.types.ts +0 -1
  365. package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.types.ts +0 -1
  366. package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +0 -1
  367. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +0 -1
  368. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.types.ts +0 -1
  369. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +0 -1
  370. package/dist/assets/components/internal/calendar/components/calendar-year-selector/calendar-year-selector.types.ts +0 -1
  371. package/dist/assets/components/internal/calendar/components/calendar-year-selector/calendar-year-selector.vue +0 -1
  372. package/dist/assets/components/internal/calendar/composables/use-granularity-adjustments.types.ts +0 -1
  373. package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +0 -1
  374. package/dist/assets/components/internal/checkbox-input/checkbox-input.types.ts +0 -1
  375. package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +0 -1
  376. package/dist/assets/components/internal/controls-group/controls-group.vue +0 -1
  377. package/dist/assets/components/internal/copy/copy.docs.mdx +0 -43
  378. package/dist/assets/components/internal/copy/copy.stories.ts +0 -1
  379. package/dist/assets/components/internal/copy/copy.types.ts +0 -1
  380. package/dist/assets/components/internal/copy/copy.vue +0 -1
  381. package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +0 -1
  382. package/dist/assets/components/internal/dialog-page/dialog-page.types.ts +0 -1
  383. package/dist/assets/components/internal/dialog-page/dialog-page.vue +0 -1
  384. package/dist/assets/components/internal/error-message/error-message.vue +0 -1
  385. package/dist/assets/components/internal/field-label/field-label.vue +0 -1
  386. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +0 -21
  387. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +0 -1
  388. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +0 -1
  389. package/dist/assets/components/internal/footer-actions/footer-actions.vue +0 -1
  390. package/dist/assets/components/internal/grid-layout/grid-layout.types.ts +0 -1
  391. package/dist/assets/components/internal/grid-layout/grid-layout.vue +0 -1
  392. package/dist/assets/components/internal/header-meta/header-meta.types.ts +0 -1
  393. package/dist/assets/components/internal/header-meta/header-meta.vue +0 -1
  394. package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +0 -1
  395. package/dist/assets/components/internal/listbox/components/listbox-lazy-load/listbox-lazy-load.types.ts +0 -1
  396. package/dist/assets/components/internal/listbox/components/listbox-lazy-load/listbox-lazy-load.vue +0 -1
  397. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-category/listbox-multi-select-category.types.ts +0 -1
  398. 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 +0 -1
  399. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-nested-lazy-load/listbox-multi-select-nested-lazy-load.types.ts +0 -1
  400. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-nested-lazy-load/listbox-multi-select-nested-lazy-load.vue +0 -1
  401. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.types.ts +0 -1
  402. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +0 -1
  403. package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.types.ts +0 -1
  404. package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +0 -1
  405. package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +0 -1
  406. package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +0 -1
  407. package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +0 -1
  408. package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +0 -1
  409. package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +0 -1
  410. package/dist/assets/components/internal/listbox/listbox.stories.ts +0 -1
  411. package/dist/assets/components/internal/listbox/listbox.types.ts +0 -1
  412. package/dist/assets/components/internal/listbox/listbox.vue +0 -1
  413. package/dist/assets/components/internal/radio-input/radio-input.stories.ts +0 -1
  414. package/dist/assets/components/internal/radio-input/radio-input.types.ts +0 -1
  415. package/dist/assets/components/internal/radio-input/radio-input.vue +0 -1
  416. package/dist/assets/components/internal/teleport/teleport.types.ts +0 -1
  417. package/dist/assets/components/internal/teleport/teleport.vue +0 -1
  418. package/dist/assets/components/internal/tooltip/tooltip.test.vue +0 -1
  419. package/dist/assets/components/internal/tooltip/tooltip.types.ts +0 -1
  420. package/dist/assets/components/internal/tooltip/tooltip.vue +0 -1
  421. package/dist/assets/composables/use-form-validate-zod-4/use-form.types.ts +0 -1
  422. package/dist/assets/composables/use-numeric-input/use-numeric-input.types.ts +0 -1
  423. package/dist/assets/composables/use-section-navigation-keyboard/use-section-navigation-keyboard.types.ts +0 -1
  424. package/dist/assets/composables/use-virtual-list/use-virtual-list.types.ts +0 -1
@@ -1 +1 @@
1
- <template> <div class="b-ai-button" :class="conditionalClass"> <div class="b-ai-button__animation" /> <bento-button class="b-ai-button__button" :condensed="condensed" :disabled="disabled" :inverse="inverse" variant="secondary" @click.capture.stop="onClick" > <template #iconLeft> <ai-filled-icon aria-hidden="true" class="b-ai-button__icon" /> </template> <slot></slot> </bento-button> </div> </template> <script setup lang="ts"> import { computed, onMounted, useSlots } from 'vue'; import BentoButton from '../../button.vue'; import AiFilledIcon from '@adyen/ui-assets-icons-16/vue/artificial-intelligence-ai-filled'; import { useHasSlot } from '@/composables/use-has-slot/use-has-slot'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoAiButtonProps } from './ai-button.types'; const props = withDefaults(defineProps<BentoAiButtonProps>(), { link: undefined, type: 'button', condensed: false, disabled: false, inverse: false, }); const emit = defineEmits<{ /** * Click event handler */ (e: 'click', event: MouseEvent): void; }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const conditionalClass = computed(() => ({ 'b-ai-button--disabled': props.disabled, })); const onClick = (e: MouseEvent) => { // Prevent Vue3 from triggering event twice e.stopPropagation(); emit('click', e); }; onMounted(() => { if (!hasSlot('default')) { printDevelopmentWarning('An icon-only button cannot be a AI button'); } }); </script> <script lang="ts"> /** * AI Button component is used for cases where pressing a button starts an AI-related action. * * @example * import { BentoAiButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoAiButton }, * template: ` * <bento-ai-button @click="onClick">Generate with AI</bento-ai-button> * ` * } */ export default { name: 'bento-ai-button', }; </script> <style lang="scss" scoped src="./ai-button.scss" />
1
+ <template> <div class="b-ai-button" :class="conditionalClass"> <div class="b-ai-button__animation" /> <bento-button class="b-ai-button__button" :condensed="condensed" :disabled="disabled" :inverse="inverse" variant="secondary" @click.capture.stop="onClick" > <template #iconLeft> <ai-filled-icon aria-hidden="true" class="b-ai-button__icon" /> </template> <slot></slot> </bento-button> </div> </template> <script setup lang="ts"> import { computed, onMounted, useSlots } from 'vue'; import BentoButton from '../../button.vue'; import AiFilledIcon from '@adyen/ui-assets-icons-16/vue/artificial-intelligence-ai-filled'; import { useHasSlot } from '@/composables/use-has-slot/use-has-slot'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoAiButtonProps } from './ai-button.types'; const props = withDefaults(defineProps<BentoAiButtonProps>(), { link: undefined, type: 'button', condensed: false, disabled: false, inverse: false, }); const emit = defineEmits<{ /** * Click event handler */ (e: 'click', event: MouseEvent): void; }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const conditionalClass = computed(() => ({ 'b-ai-button--disabled': props.disabled, 'b-ai-button--inverse': props.inverse, })); const onClick = (e: MouseEvent) => { // Prevent Vue3 from triggering event twice e.stopPropagation(); emit('click', e); }; onMounted(() => { if (!hasSlot('default')) { printDevelopmentWarning('An icon-only button cannot be a AI button'); } }); </script> <script lang="ts"> /** * AI Button component is used for cases where pressing a button starts an AI-related action. * * @example * import { BentoAiButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoAiButton }, * template: ` * <bento-ai-button @click="onClick">Generate with AI</bento-ai-button> * ` * } */ export default { name: 'bento-ai-button', }; </script> <style lang="scss" scoped src="./ai-button.scss" />
@@ -74,6 +74,9 @@ configured:
74
74
 
75
75
  }`}/>
76
76
 
77
+ > **Icon requirement:** Only components from `@adyen/ui-assets-icons-16` are allowed in icon props and icon slots. Icon
78
+ > fonts, custom SVGs, and external icon libraries are not supported.
79
+
77
80
  - `title`: the button's text.
78
81
  - `condensed` (optional): condensed button.
79
82
  - `critical` (optional): the button's red border and color.
@@ -0,0 +1 @@
1
+ import type { BentoMenuProps } from '@/components/menu/menu.types'; import type { VueNodeElement } from '@/types/elements'; import type { OmitStrict } from '@/types/omit-strict'; import type { VNodeRef } from 'vue/types/vnode'; /** * Base interface for the Button and Menu objects */ export interface BentoButtonActionBase { title: string; icon?: VueNodeElement; iconLeft?: VueNodeElement; iconRight?: VueNodeElement; /** * Provides a template ref to the underlying component instance. */ elementRef?: Exclude<VNodeRef, string>; } /** * Type for the Menus passed as objects to Button Actions */ export type BentoActionMenu = BentoButtonActionBase & OmitStrict<BentoMenuProps, 'button' | 'data'> & Partial<Pick<BentoMenuProps, 'data'>>;
@@ -1 +1 @@
1
- import { action } from '@storybook/addon-actions'; import { BentoButtonActionsLayout } from '@/components/button'; import ButtonActionsWithMenu from './__tests__/button-actions-with-menu.vue?raw'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoButtonActions from './button-actions.vue'; import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; import type { BentoButtonActionsList } from './button-actions.types'; export default { title: 'Button/Button Actions', component: BentoButtonActions, argTypes: { layout: { options: Object.values(BentoButtonActionsLayout), mapping: BentoButtonActionsLayout, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoButtonActions>; const defaultCode = ` <script setup lang="ts"> import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; </script> <template> <bento-button-actions :actions="[{ title: 'Primary', event: clickedAction, icon: MachineLearningIcon, }, { title: 'Secondary', event: clickedAction, }, { title: 'Tertiary', event: clickedAction, }]" /> </template> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', event: clickedAction, icon: icon, }, { title: 'Secondary', event: clickedAction, }, { title: 'Tertiary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(defaultCode), }; export const Critical = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', critical: true, event: clickedAction, icon: args.icon, }, { title: 'Secondary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { return { args: isVue2 ? props : _args, clickedAction: action('input'), }; }, }), }; const withLinkCode = ` <script setup lang="ts"> import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; </script> <template> <bento-button-actions :actions="[{ title: 'Primary', event: clickedAction, icon: MachineLearningIcon, }, { title: 'Secondary', event: clickedAction, link: { variant: 'default', external: true, to: 'https://www.adyen.com/' }, }, { title: 'Tertiary', event: clickedAction, }]" /> </template> `; export const WithLink: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', event: clickedAction, icon: icon, }, { title: 'Secondary (a link)', event: clickedAction, link: { variant: 'default', external: true, to: 'https://www.adyen.com/' }, }, { title: 'Tertiary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(withLinkCode), }; const withLoadingButtonCode = ` <template> <bento-button-actions :actions="[ { title: 'Loading Button', event: loadingClickAction, state: 'loading', icon, }, { title: 'Secondary', event: secondaryClickAction, state: 'start', icon, }, ]" /> </template> `; export const WithLoadingButton: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[ { title: 'Loading Button', event: clickedAction, state: 'loading', icon, }, { title: 'Secondary', event: clickedAction, state: 'start', icon, }, ]" /> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(withLoadingButtonCode), }; export const WithMenu: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" /> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { actions: [ { title: 'Primary', event: () => true, }, { title: 'Secondary', event: () => true, }, { title: 'Tertiary', event: () => true, disabled: true, }, { title: 'Menu', data: [ { text: 'Item 1', secondaryText: 'Text', handler: () => true, critical: true, }, { text: 'Item 2', secondaryText: 'Text', handler: () => true, }, ], }, ] satisfies BentoButtonActionsList, }, parameters: storybookDocsParameter(ButtonActionsWithMenu), };
1
+ import { action } from '@storybook/addon-actions'; import { BentoButtonActionsLayout } from '@/components/button'; const ButtonActionsWithMenu = "<template>\n <bento-button-actions :actions=\"actions\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoButtonActions } from '@adyen/bento-vue2';\n\n const actions = [\n {\n title: 'Primary',\n event: () => true,\n },\n {\n title: 'Secondary',\n event: () => true,\n },\n {\n title: 'Tertiary',\n event: () => true,\n disabled: true,\n },\n {\n title: 'Menu',\n data: [\n {\n text: 'Item 1',\n secondaryText: 'Text',\n handler: () => true,\n critical: true,\n },\n {\n text: 'Item 2',\n secondaryText: 'Text',\n handler: () => true,\n },\n ],\n },\n ];\n</script>\n"; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoButtonActions from './button-actions.vue'; import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; import type { BentoButtonActionsList } from './button-actions.types'; const ButtonActionsCritical = "<template>\n <bento-button-actions :actions=\"actions\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoButtonActions } from '@adyen/bento-vue2';\n\n const clickedAction = () => true;\n const actions = [\n {\n title: 'Primary',\n critical: true,\n event: clickedAction,\n },\n {\n title: 'Secondary',\n event: clickedAction,\n },\n ];\n</script>\n"; const ButtonActionsDefault = "<template>\n <bento-button-actions :actions=\"actions\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoButtonActions } from '@adyen/bento-vue2';\n import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning';\n\n const clickedAction = () => true;\n const actions = [\n {\n title: 'Primary',\n event: clickedAction,\n icon: MachineLearningIcon,\n },\n {\n title: 'Secondary',\n event: clickedAction,\n },\n {\n title: 'Tertiary',\n event: clickedAction,\n },\n ];\n</script>\n"; const ButtonActionsWithLink = "<template>\n <bento-button-actions :actions=\"actions\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoButtonActions } from '@adyen/bento-vue2';\n import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning';\n\n const clickedAction = () => true;\n const actions = [\n {\n title: 'Primary',\n event: clickedAction,\n icon: MachineLearningIcon,\n },\n {\n title: 'Secondary (a link)',\n event: clickedAction,\n link: {\n variant: 'default',\n external: true,\n to: 'https://www.adyen.com/',\n },\n },\n {\n title: 'Tertiary',\n event: clickedAction,\n },\n ];\n</script>\n"; const ButtonActionsWithLoadingButton = "<template>\n <bento-button-actions :actions=\"actions\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoButtonActions, BentoLoadingButtonState } from '@adyen/bento-vue2';\n import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning';\n\n const clickedAction = () => true;\n const actions = [\n {\n title: 'Loading Button',\n event: clickedAction,\n state: BentoLoadingButtonState.LOADING,\n icon: MachineLearningIcon,\n },\n {\n title: 'Secondary',\n event: clickedAction,\n state: BentoLoadingButtonState.START,\n icon: MachineLearningIcon,\n },\n ];\n</script>\n"; export default { title: 'Button/Button Actions', component: BentoButtonActions, argTypes: { layout: { options: Object.values(BentoButtonActionsLayout), mapping: BentoButtonActionsLayout, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoButtonActions>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', event: clickedAction, icon: icon, }, { title: 'Secondary', event: clickedAction, }, { title: 'Tertiary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(ButtonActionsDefault), }; export const Critical = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', critical: true, event: clickedAction, icon: args.icon, }, { title: 'Secondary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { return { args: isVue2 ? props : _args, clickedAction: action('input'), }; }, }), parameters: storybookDocsParameter(ButtonActionsCritical), }; export const WithLink: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[{ title: 'Primary', event: clickedAction, icon: icon, }, { title: 'Secondary (a link)', event: clickedAction, link: { variant: 'default', external: true, to: 'https://www.adyen.com/' }, }, { title: 'Tertiary', event: clickedAction, }]" > </bento-button-actions> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(ButtonActionsWithLink), }; export const WithLoadingButton: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions, MachineLearningIcon }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" :actions="[ { title: 'Loading Button', event: clickedAction, state: 'loading', icon, }, { title: 'Secondary', event: clickedAction, state: 'start', icon, }, ]" /> `, setup(props) { const icon = MachineLearningIcon; return { args: isVue2 ? props : _args, clickedAction: action('input'), icon, }; }, }), parameters: storybookDocsParameter(ButtonActionsWithLoadingButton), }; export const WithMenu: Story = { render: (_args, { argTypes }) => ({ components: { BentoButtonActions }, props: Object.keys(argTypes), template: ` <bento-button-actions v-bind="args" /> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { actions: [ { title: 'Primary', event: () => true, }, { title: 'Secondary', event: () => true, }, { title: 'Tertiary', event: () => true, disabled: true, }, { title: 'Menu', data: [ { text: 'Item 1', secondaryText: 'Text', handler: () => true, critical: true, }, { text: 'Item 2', secondaryText: 'Text', handler: () => true, }, ], }, ] satisfies BentoButtonActionsList, }, parameters: storybookDocsParameter(ButtonActionsWithMenu), };
@@ -1 +1 @@
1
- import type { BentoButtonProps } from '../../button.types'; import type { BentoMenuProps } from '@/components/menu/menu.types'; import type { BentoLoadingButtonState } from '../loading-button/loading-button.types'; import type { VueNodeElement } from '@/types/elements'; import type { OmitStrict } from '@/types/omit-strict'; import type { VNodeRef } from 'vue/types/vnode'; export type BentoButtonActionObject = BentoActionButton; // TODO: Update to Array<BentoButtonActionButton | BentoButtonActionMenuObject> in v2.0.0 export type BentoButtonActionsList = Array<BentoActionButton & BentoActionMenu>; export enum BentoButtonActionsLayoutBasic { BUTTONS_END = 'buttons-end', FILL_CONTAINER = 'fill-container', SPACE_BETWEEN = 'space-between', VERTICAL_STACK = 'vertical-stack', } export enum BentoButtonActionsLayoutExtended { BUTTONS_START = 'buttons-start', } /** * @deprecated Since v2.0.0. Use string literal instead. */ export const BentoButtonActionsLayout = { ...BentoButtonActionsLayoutBasic, ...BentoButtonActionsLayoutExtended }; export type BentoButtonActionsLayout = BentoButtonActionsLayoutBasic | BentoButtonActionsLayoutExtended; export interface BentoButtonActionsProps { /** * 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` (optional): Vue Icon from the library `@adyen/ui-assets-icons-16`. * * @see BentoButton for a list of all the other props supported by each button action. * * ``` * [{ * title: 'Primary', * event: () => { ... }, * icon: FourPeopleIcon, * link: { * variant: 'default', * external: true, * to: 'https://www.adyen.com/' * }, * }, { * title: 'Secondary', * event: () => { ... } * disabled: true, * variant: 'secondary', * critical: true * }] * ``` */ actions: BentoButtonActionsList; /** * Sets the Button Actions to color scheme inverse */ inverse?: boolean; /** * 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 */ layout?: string; } /** * Private */ /** * Base interface for the Button and Menu objects */ export interface BentoButtonActionBase { title: string; icon?: VueNodeElement; iconLeft?: VueNodeElement; iconRight?: VueNodeElement; /** * Provides a template ref to the underlying component instance. */ elementRef?: Exclude<VNodeRef, string>; } /** * Type for the Buttons passed as objects to Button Actions */ export interface BentoActionButton extends BentoButtonActionBase, BentoButtonProps { event?: (event: Event) => void; state?: BentoLoadingButtonState | `${BentoLoadingButtonState}`; iconOnly?: boolean; } /** * Type for the Menus passed as objects to Button Actions */ export type BentoActionMenu = BentoButtonActionBase & OmitStrict<BentoMenuProps, 'button' | 'data'> & Partial<Pick<BentoMenuProps, 'data'>>;
1
+ import type { BentoButtonProps } from '../../button.types'; import type { BentoLoadingButtonState } from '../loading-button/loading-button.types'; import type { BentoActionMenu, BentoButtonActionBase } from './button-actions.internal.types'; export interface BentoButtonActionObject extends BentoButtonActionBase, BentoButtonProps { event?: (event: Event) => void; state?: BentoLoadingButtonState | `${BentoLoadingButtonState}`; iconOnly?: boolean; } // TODO: Update to Array<BentoButtonActionButton | BentoButtonActionMenuObject> in v2.0.0 export type BentoButtonActionsList = Array<BentoButtonActionObject & BentoActionMenu>; export enum BentoButtonActionsLayoutBasic { BUTTONS_END = 'buttons-end', FILL_CONTAINER = 'fill-container', SPACE_BETWEEN = 'space-between', VERTICAL_STACK = 'vertical-stack', } export enum BentoButtonActionsLayoutExtended { BUTTONS_START = 'buttons-start', } /** * @deprecated Since v3.0.0. Use string literal instead. */ export const BentoButtonActionsLayout = { ...BentoButtonActionsLayoutBasic, ...BentoButtonActionsLayoutExtended }; export type BentoButtonActionsLayout = BentoButtonActionsLayoutBasic | BentoButtonActionsLayoutExtended; export interface BentoButtonActionsProps { /** * 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` (optional): Vue Icon from the library `@adyen/ui-assets-icons-16`. * * @see BentoButton for a list of all the other props supported by each button action. * * ``` * [{ * title: 'Primary', * event: () => { ... }, * icon: FourPeopleIcon, * link: { * variant: 'default', * external: true, * to: 'https://www.adyen.com/' * }, * }, { * title: 'Secondary', * event: () => { ... } * disabled: true, * variant: 'secondary', * critical: true * }] * ``` */ actions: BentoButtonActionsList; /** * Sets the Button Actions to color scheme inverse */ inverse?: boolean; /** * 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 */ layout?: string; }
@@ -1 +1 @@
1
- <template> <div ref="buttonActions" class="b-button-actions"> <div class="b-button-actions__container-wrapper" :class="conditionalClasses" role="group"> <div v-for="(button, index) in reversedActionsList" :key="index" ref="buttons" class="b-button-actions__button-wrapper" > <template v-if="button.data"> <bento-menu :ref="button.elementRef" :data="button.data" :close-menu-on-item-select="button.closeMenuOnItemSelect" :close-on-click-outside-options="button.closeOnClickOutsideOptions" :disable-arrow-down-opening-menu="button.disableArrowDownOpeningMenu" :menu-position="button.menuPosition" :menu-fixed-positioning="button.menuFixedPositioning" :menu-width="button.menuWidth" :revert-focus-to-button="button.revertFocusToButton" :teleport="button.teleport" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> </bento-menu> </template> <template v-else> <bento-loading-button v-if="button.state" :ref="button.elementRef" :condensed="button.condensed" :critical="button.critical" :disabled="button.disabled" :inverse="inverse" :state="button.state" :variant="button.variant || generateButtonVariantByIndex(index)" v-on="generateButtonEvents(button)" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> </bento-loading-button> <bento-button v-else :ref="button.elementRef" :condensed="button.condensed" :critical="button.critical" :disabled="button.disabled" :inverse="inverse" :link="button.link" :variant="button.variant || generateButtonVariantByIndex(index)" v-on="generateButtonEvents(button)" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> <template v-if="button.iconRight" #iconRight> <component :is="button.iconRight" :svg-title="button.title"></component> </template> </bento-button> </template> </div> </div> </div> </template> <script setup lang="ts"> import { computed, ref, toRef } from 'vue'; import BentoButton from '../../button.vue'; import type { BentoActionButton, BentoButtonActionsProps } from './button-actions.types'; import BentoLoadingButton from '../loading-button/loading-button.vue'; import BentoMenu from '@/components/menu/menu.vue'; import { useStackButtons } from './composables/use-stack-buttons/use-stack-buttons'; const props = withDefaults(defineProps<BentoButtonActionsProps>(), { inverse: false, layout: 'buttons-end', }); const buttonActions = ref<HTMLDivElement>(null); const buttons = ref<Array<HTMLDivElement>>(null); const stackButtons = useStackButtons(buttonActions, buttons, toRef(props, 'actions')); const reversedActionsList = computed(() => props.actions.slice().reverse()); const generateButtonVariantByIndex = actionIndex => { const lastActionIndex = props.actions.length - 1; return actionIndex === lastActionIndex ? 'primary' : 'secondary'; }; const conditionalClasses = computed(() => ({ [`b-button-actions--${props.layout}`]: true, [`b-button-actions--stacked-buttons`]: stackButtons.value, })); const generateButtonEvents = (button: BentoActionButton) => button?.event !== undefined ? { click: button.event, } : {}; const isIconOnly = (button: BentoActionButton) => button.iconOnly && (button.iconLeft || button.iconRight || button.icon); </script> <script lang="ts"> /** * ButtonActions component manages a set of actions a user can choose from by displaying a group of buttons. * * @example * import { BentoButtonActions } from '@adyen/bento-vue2'; * import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; * * const clickedAction = () => { //your action here } * * <template> * <bento-button-actions * :actions="[{ * title: 'Primary', * event: clickedAction, * icon: MachineLearningIcon, * }, { * title: 'Secondary', * event: clickedAction, * }, { * title: 'Tertiary', * event: clickedAction, * }]" * /> * </template> */ export default {}; </script> <style lang="scss" scoped src="./button-actions.scss" />
1
+ <template> <div ref="buttonActions" class="b-button-actions"> <div class="b-button-actions__container-wrapper" :class="conditionalClasses" role="group"> <div v-for="(button, index) in reversedActionsList" :key="index" ref="buttons" class="b-button-actions__button-wrapper" > <template v-if="button.data"> <bento-menu :ref="button.elementRef" :data="button.data" :close-menu-on-item-select="button.closeMenuOnItemSelect" :close-on-click-outside-options="button.closeOnClickOutsideOptions" :disable-arrow-down-opening-menu="button.disableArrowDownOpeningMenu" :menu-position="button.menuPosition" :menu-fixed-positioning="button.menuFixedPositioning" :menu-width="button.menuWidth" :revert-focus-to-button="button.revertFocusToButton" :teleport="button.teleport" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> </bento-menu> </template> <template v-else> <bento-loading-button v-if="button.state" :ref="button.elementRef" :condensed="button.condensed" :critical="button.critical" :disabled="button.disabled" :inverse="inverse" :state="button.state" :variant="button.variant || generateButtonVariantByIndex(index)" v-on="generateButtonEvents(button)" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> </bento-loading-button> <bento-button v-else :ref="button.elementRef" :condensed="button.condensed" :critical="button.critical" :disabled="button.disabled" :inverse="inverse" :link="button.link" :variant="button.variant || generateButtonVariantByIndex(index)" v-on="generateButtonEvents(button)" > <template v-if="button.icon || button.iconLeft" #iconLeft> <component :is="button.icon || button.iconLeft" :svg-title="button.title"></component> </template> <template v-if="!isIconOnly(button)" #default> {{ button.title }} </template> <template v-if="button.iconRight" #iconRight> <component :is="button.iconRight" :svg-title="button.title"></component> </template> </bento-button> </template> </div> </div> </div> </template> <script setup lang="ts"> import { computed, ref, toRef } from 'vue'; import BentoButton from '../../button.vue'; import type { BentoButtonActionObject, BentoButtonActionsProps } from './button-actions.types'; import BentoLoadingButton from '../loading-button/loading-button.vue'; import BentoMenu from '@/components/menu/menu.vue'; import { useStackButtons } from './composables/use-stack-buttons/use-stack-buttons'; const props = withDefaults(defineProps<BentoButtonActionsProps>(), { inverse: false, layout: 'buttons-end', }); const buttonActions = ref<HTMLDivElement>(null); const buttons = ref<Array<HTMLDivElement>>(null); const stackButtons = useStackButtons(buttonActions, buttons, toRef(props, 'actions')); const reversedActionsList = computed(() => props.actions.slice().reverse()); const generateButtonVariantByIndex = actionIndex => { const lastActionIndex = props.actions.length - 1; return actionIndex === lastActionIndex ? 'primary' : 'secondary'; }; const conditionalClasses = computed(() => ({ [`b-button-actions--${props.layout}`]: true, [`b-button-actions--stacked-buttons`]: stackButtons.value, })); const generateButtonEvents = (button: BentoButtonActionObject) => button?.event !== undefined ? { click: button.event, } : {}; const isIconOnly = (button: BentoButtonActionObject) => button.iconOnly && (button.iconLeft || button.iconRight || button.icon); </script> <script lang="ts"> /** * ButtonActions component manages a set of actions a user can choose from by displaying a group of buttons. * * @example * import { BentoButtonActions } from '@adyen/bento-vue2'; * import MachineLearningIcon from '@adyen/ui-assets-icons-16/vue/machine-learning'; * * const clickedAction = () => { //your action here } * * <template> * <bento-button-actions * :actions="[{ * title: 'Primary', * event: clickedAction, * icon: MachineLearningIcon, * }, { * title: 'Secondary', * event: clickedAction, * }, { * title: 'Tertiary', * event: clickedAction, * }]" * /> * </template> */ export default {}; </script> <style lang="scss" scoped src="./button-actions.scss" />
@@ -20,17 +20,17 @@ Note that both the states of the button (`start`, `loading`, `success` or `fail`
20
20
  manually based on the state of the action you are waiting upon. For example, let's say clicking on this button would
21
21
  trigger a `POST` request:
22
22
 
23
- - Before the user clicks on the button, this will be shown initially in a `start` state.
24
- - Once the user clicks on the button, the `POST` request is triggered and the button's state has to be changed to
25
- `loading` and have a matching text of your liking to represent that state. At this point the button will be rendered
26
- as disabled and will not trigger any actions on click.
27
- - Once a response from the request is received, the loading button state has to be updated and its text updated
28
- accordingly:
29
- - If the request was successful a `success` state has to be set with a corresponding text that marks success.
30
- - Otherwise a `fail` state needs to be set with a corresponding text that indicates failure and tells the user
31
- they can retry the action if the button is clicked again.
32
- - In these two final states the button will be clickable again, for retrying on `fail` or doing any other action on
33
- `success`.
23
+ - Before the user clicks on the button, this will be shown initially in a `start` state.
24
+ - Once the user clicks on the button, the `POST` request is triggered and the button's state has to be changed to
25
+ `loading` and have a matching text of your liking to represent that state. At this point the button will be rendered
26
+ as disabled and will not trigger any actions on click.
27
+ - Once a response from the request is received, the loading button state has to be updated and its text updated
28
+ accordingly:
29
+ - If the request was successful a `success` state has to be set with a corresponding text that marks success.
30
+ - Otherwise a `fail` state needs to be set with a corresponding text that indicates failure and tells the user they
31
+ can retry the action if the button is clicked again.
32
+ - In these two final states the button will be clickable again, for retrying on `fail` or doing any other action on
33
+ `success`.
34
34
 
35
35
  ## Variations
36
36
 
@@ -39,22 +39,22 @@ trigger a `POST` request:
39
39
  This property represents the state of the button and is of type `BentoLoadingButtonState`, which can take up one of the
40
40
  following values:
41
41
 
42
- - `BentoLoadingButtonState.START`: the initial state of the button, this state represents a button that has not
43
- triggered any action yet.
44
- - `BentoLoadingButtonState.LOADING`: the state of the button that represents an action in process, during this state
45
- the button click events will not be emitted.
46
- - `BentoLoadingButtonState.SUCCESS`: the state of the button that represents that an action has been completed
47
- successfully.
48
- - `BentoLoadingButtonState.FAIL`: the state of the button that represents that an action has been completed
49
- unsuccessfully.
42
+ - `BentoLoadingButtonState.START`: the initial state of the button, this state represents a button that has not
43
+ triggered any action yet.
44
+ - `BentoLoadingButtonState.LOADING`: the state of the button that represents an action in process, during this state the
45
+ button click events will not be emitted.
46
+ - `BentoLoadingButtonState.SUCCESS`: the state of the button that represents that an action has been completed
47
+ successfully.
48
+ - `BentoLoadingButtonState.FAIL`: the state of the button that represents that an action has been completed
49
+ unsuccessfully.
50
50
 
51
51
  ### Variant
52
52
 
53
53
  This property represent the variant referenced in the `bento-button` component. It is one of the following values:
54
54
 
55
- - `'primary'`: renders the `bento-loading-button` in the primary variant.
56
- - `'secondary'`: renders the `bento-loading-button` in the secondary variant.
57
- - `'tertiary'`: renders the `bento-loading-button` in the tertiary variant.
55
+ - `'primary'`: renders the `bento-loading-button` in the primary variant.
56
+ - `'secondary'`: renders the `bento-loading-button` in the secondary variant.
57
+ - `'tertiary'`: renders the `bento-loading-button` in the tertiary variant.
58
58
 
59
59
  ## Slots
60
60
 
@@ -64,6 +64,9 @@ You can pass an icon to the `bento-loading-button` the same way you do to `bento
64
64
  However, the icon will only show in the `start` state, being replaced by the loading button respective icons in the
65
65
  other states.
66
66
 
67
+ > **Icon requirement:** Only components from `@adyen/ui-assets-icons-16` are allowed in icon props and icon slots. Icon
68
+ > fonts, custom SVGs, and external icon libraries are not supported.
69
+
67
70
  ## Modifiers
68
71
 
69
72
  ### Disabled
@@ -103,4 +106,4 @@ this component are as follows:
103
106
 
104
107
  Resources:
105
108
 
106
- - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=1901-27998&t=NHLd9msx8lGq3NNF-0)
109
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=1901-27998&t=NHLd9msx8lGq3NNF-0)
@@ -1 +1 @@
1
- import { ref, watch } from 'vue'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoButtonVariant } from '@/components/button'; import { computed, isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoLoadingButton from './loading-button.vue'; import DownloadIcon from '@adyen/ui-assets-icons-16/vue/download'; export default { title: 'Button/Loading Button', component: BentoLoadingButton, argTypes: { condensed: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, critical: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, default: { name: 'default', defaultValue: '', description: 'Button text', table: { type: { summary: 'VNode[]' }, }, control: { disable: true }, }, disabled: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, state: { control: { type: 'select', }, table: { type: { summary: 'BentoLoadingButtonState: start, loading, success, fail', }, }, }, variant: { options: Object.values(BentoButtonVariant), control: { type: 'select', }, table: { type: { summary: `BentoButtonVariant: ${Object.values(BentoButtonVariant)}` }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoLoadingButton>; const BUTTON_TEXT = { start: 'Button', loading: 'Loading', success: 'Success', fail: 'Retry', }; const defaultCode = ` <bento-loading-button state="loading" :variant="primary" @input="() => {}" > Loading </bento-loading-button> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoLoadingButton }, props: Object.keys(argTypes), template: ` <bento-loading-button v-bind="args" @click="clickedButtonAction"> {{ loadingButtonText }} </bento-loading-button> `, setup(props) { const args = isVue2 ? props : _args; const loadingButtonText = computed(() => BUTTON_TEXT[args.state]); return { clickedButtonAction: action('click'), loadingButtonText, args, }; }, }), args: { state: 'loading', variant: 'primary', }, parameters: storybookDocsParameter(defaultCode), }; const withIconCode = ` <bento-loading-button state="start" :variant="primary" @click="() => {}" > <template #iconLeft> <download-icon /> </template> Download </bento-loading-button> `; export const WithIcon: Story = { render: (_args, { argTypes }) => ({ components: { BentoLoadingButton, DownloadIcon }, props: Object.keys(argTypes), template: ` <bento-loading-button v-bind="args" @click="downloadAction" :state="loadingState"> <template #iconLeft> <download-icon /> </template> Download </bento-loading-button> `, setup(props) { const args = isVue2 ? props : _args; const loadingState = ref(args.state); const downloadAction = () => { loadingState.value = 'loading'; setTimeout(() => { loadingState.value = Math.random() < 0.5 ? 'success' : 'fail'; }, 1000); }; watch( () => args.state, () => { loadingState.value = args.state; } ); return { downloadAction, loadingState, args, }; }, }), args: { variant: 'primary', state: 'start', }, parameters: storybookDocsParameter(withIconCode), };
1
+ import { ref, watch } from 'vue'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoButtonVariant } from '@/components/button'; import { computed, isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoLoadingButton from './loading-button.vue'; import DownloadIcon from '@adyen/ui-assets-icons-16/vue/download'; const LoadingButtonDefault = "<template>\n <bento-loading-button state=\"loading\" variant=\"primary\" @click=\"clickedButtonAction\">\n Loading\n </bento-loading-button>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoLoadingButton } from '@adyen/bento-vue2';\n\n const clickedButtonAction = () => true;\n</script>\n"; const LoadingButtonWithIcon = "<template>\n <bento-loading-button :state=\"loadingState\" variant=\"primary\" @click=\"downloadAction\">\n <template #iconLeft>\n <download-icon />\n </template>\n Download\n </bento-loading-button>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoLoadingButton, BentoLoadingButtonState } from '@adyen/bento-vue2';\n import DownloadIcon from '@adyen/ui-assets-icons-16/vue/download';\n\n const loadingState = ref<BentoLoadingButtonState>(BentoLoadingButtonState.START);\n\n const downloadAction = () => {\n loadingState.value = BentoLoadingButtonState.LOADING;\n\n setTimeout(() => {\n loadingState.value = Math.random() < 0.5 ? BentoLoadingButtonState.SUCCESS : BentoLoadingButtonState.FAIL;\n }, 1000);\n };\n</script>\n"; export default { title: 'Button/Loading Button', component: BentoLoadingButton, argTypes: { condensed: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, critical: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, default: { name: 'default', defaultValue: '', description: 'Button text', table: { type: { summary: 'VNode[]' }, }, control: { disable: true }, }, disabled: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, state: { control: { type: 'select', }, table: { type: { summary: 'BentoLoadingButtonState: start, loading, success, fail', }, }, }, variant: { options: Object.values(BentoButtonVariant), control: { type: 'select', }, table: { type: { summary: `BentoButtonVariant: ${Object.values(BentoButtonVariant)}` }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoLoadingButton>; const BUTTON_TEXT = { start: 'Button', loading: 'Loading', success: 'Success', fail: 'Retry', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoLoadingButton }, props: Object.keys(argTypes), template: ` <bento-loading-button v-bind="args" @click="clickedButtonAction"> {{ loadingButtonText }} </bento-loading-button> `, setup(props) { const args = isVue2 ? props : _args; const loadingButtonText = computed(() => BUTTON_TEXT[args.state]); return { clickedButtonAction: action('click'), loadingButtonText, args, }; }, }), args: { state: 'loading', variant: 'primary', }, parameters: storybookDocsParameter(LoadingButtonDefault), }; export const WithIcon: Story = { render: (_args, { argTypes }) => ({ components: { BentoLoadingButton, DownloadIcon }, props: Object.keys(argTypes), template: ` <bento-loading-button v-bind="args" @click="downloadAction" :state="loadingState"> <template #iconLeft> <download-icon /> </template> Download </bento-loading-button> `, setup(props) { const args = isVue2 ? props : _args; const loadingState = ref(args.state); const downloadAction = () => { loadingState.value = 'loading'; setTimeout(() => { loadingState.value = Math.random() < 0.5 ? 'success' : 'fail'; }, 1000); }; watch( () => args.state, () => { loadingState.value = args.state; } ); return { downloadAction, loadingState, args, }; }, }), args: { variant: 'primary', state: 'start', }, parameters: storybookDocsParameter(LoadingButtonWithIcon), };
@@ -1 +1 @@
1
- <template> <bento-button :critical="isCritical" :variant="variant" :condensed="condensed" :disabled="disabled" :inverse="inverse" class="b-loading-button" :class="conditionalClasses" @click.capture.stop="onClick" > <slot></slot> <template v-if="state === 'start' && hasSlot('iconLeft')" #iconLeft> <slot name="iconLeft"></slot> </template> <template v-else-if="state !== 'start'" #iconLeft> <span class="b-loading-button__icon" data-testid="loading-button-rotating-icon"> <cross v-if="state === 'fail'" :svg-title="t('failed')" /> <checkmark v-else-if="state === 'success'" :svg-title="t('succeded')" /> <!-- TODO: Replace with new loading-indicator component --> <bento-loading-indicator v-else-if="state === 'loading'" small :inverse="isInverseLoadingIndicator" /> </span> </template> </bento-button> </template> <script setup lang="ts"> import { computed, type PropType, useSlots } from 'vue'; import { BentoLoadingButtonState } from './loading-button.types'; import { BentoLoadingIndicator } from '@/components/loading-indicator'; import BentoButton from '@/components/button/button.vue'; import Cross from '@adyen/ui-assets-icons-16/vue/cross-circle-fill'; import Checkmark from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import { useHasSlot } from '@/composables/use-has-slot/use-has-slot'; import { useI18n } from '@/utils/ts/i18n'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = defineProps({ /** * Reduces sizing of button */ condensed: { type: BentoButton.props.condensed.type, default: false, }, /** * Displays loading-button as critical */ critical: { type: BentoButton.props.critical.type, default: false, }, /** * Disables button functonality */ disabled: { type: BentoButton.props.disabled.type, default: false, }, /** * Sets color set to inverse */ inverse: { type: Boolean, default: false, }, /** * State of the button. * @values start, loading, success, fail */ state: { type: String as PropType<BentoLoadingButtonState | `${BentoLoadingButtonState}`>, default: BentoLoadingButtonState.START, validator: (value: BentoLoadingButtonState) => Object.values(BentoLoadingButtonState).includes(value), }, /** * Types of flavour of Button */ variant: { type: BentoButton.props.variant.type, default: 'primary', validator: BentoButton.props.variant.validator, }, }); const emit = defineEmits<{ /** * Click event handler */ (e: 'click', event: MouseEvent): void; }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const conditionalClasses = computed(() => ({ [`b-loading-button--${props.variant}-${BentoLoadingButtonState.LOADING}`]: props.state === BentoLoadingButtonState.LOADING && !props.disabled, 'b-loading-button--loading': props.state === BentoLoadingButtonState.LOADING && !props.disabled, [`b-loading-button--${props.variant}-${BentoLoadingButtonState.SUCCESS}`]: props.state === BentoLoadingButtonState.SUCCESS, [`b-loading-button--inverse`]: props.inverse, })); const isInverseLoadingIndicator = computed( () => (props.variant === 'primary' && !props.inverse) || (props.variant !== 'primary' && props.inverse) ); const isCritical = computed( () => (props.critical && props.state === BentoLoadingButtonState.START) || props.state === BentoLoadingButtonState.FAIL ); const onClick = (e: MouseEvent) => { // Prevent Vue3 from triggering event twice e.stopPropagation(); if (props.state !== BentoLoadingButtonState.LOADING) { emit('click', e); } }; </script> <script lang="ts"> /** * Loading Button component is used for cases where pressing a button starts an event that requires a loading state, e.g. when you have any sort of asynchronous action and are waiting for a response * * @example * import { BentoLoadingButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoLoadingButton }, * template: ` * <bento-loading-button state="loading" variant="secondary"> * Loading * </bento-loading button> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./loading-button.scss" />
1
+ <template> <bento-button :critical="isCritical" :variant="variant" :condensed="condensed" :disabled="disabled" :inverse="inverse" class="b-loading-button" :class="conditionalClasses" @click.capture.stop="onClick" > <slot></slot> <template v-if="state === 'start' && hasSlot('iconLeft')" #iconLeft> <slot name="iconLeft"></slot> </template> <template v-else-if="state !== 'start'" #iconLeft> <span class="b-loading-button__icon" data-testid="loading-button-rotating-icon"> <cross v-if="state === 'fail'" :svg-title="t('failed')" /> <checkmark v-else-if="state === 'success'" :svg-title="t('succeded')" /> <!-- TODO: Replace with new loading-indicator component --> <bento-loading-indicator v-else-if="state === 'loading'" small :inverse="isInverseLoadingIndicator" /> </span> </template> </bento-button> </template> <script setup lang="ts"> import { computed, type PropType, useSlots } from 'vue'; import { BentoLoadingButtonState } from './loading-button.types'; import { BentoLoadingIndicator } from '@/components/loading-indicator'; import BentoButton from '@/components/button/button.vue'; import Cross from '@adyen/ui-assets-icons-16/vue/cross-circle-fill'; import Checkmark from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import { useHasSlot } from '@/composables/use-has-slot/use-has-slot'; import { useI18n } from '@/utils/ts/i18n'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = defineProps({ /** * Reduces sizing of button */ condensed: { type: BentoButton.props.condensed.type, default: false, }, /** * Displays loading-button as critical */ critical: { type: BentoButton.props.critical.type, default: false, }, /** * Disables button functonality */ disabled: { type: BentoButton.props.disabled.type, default: false, }, /** * Sets color set to inverse */ inverse: { type: Boolean, default: false, }, /** * State of the button. * @values start, loading, success, fail */ state: { type: String as PropType<BentoLoadingButtonState | `${BentoLoadingButtonState}`>, default: BentoLoadingButtonState.START, validator: (value: BentoLoadingButtonState) => Object.values(BentoLoadingButtonState).includes(value), }, /** * Types of flavour of Button */ variant: { type: BentoButton.props.variant.type, default: 'primary', validator: BentoButton.props.variant.validator, }, }); const emit = defineEmits<{ /** * Click event handler */ (e: 'click', event: MouseEvent): void; }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const conditionalClasses = computed(() => ({ [`b-loading-button--${props.variant}-${BentoLoadingButtonState.LOADING}`]: props.state === BentoLoadingButtonState.LOADING && !props.disabled, 'b-loading-button--loading': props.state === BentoLoadingButtonState.LOADING && !props.disabled, [`b-loading-button--${props.variant}-${BentoLoadingButtonState.SUCCESS}`]: props.state === BentoLoadingButtonState.SUCCESS, [`b-loading-button--inverse`]: props.inverse, })); const isInverseLoadingIndicator = computed( () => (props.variant === 'primary' && !props.inverse) || (props.variant !== 'primary' && props.inverse) ); const isCritical = computed( () => (props.critical && props.state === BentoLoadingButtonState.START) || props.state === BentoLoadingButtonState.FAIL ); const onClick = (e: MouseEvent) => { // Prevent Vue3 from triggering event twice e.stopPropagation(); if (props.state !== BentoLoadingButtonState.LOADING) { emit('click', e); } }; </script> <script lang="ts"> /** * Loading Button component is used for cases where pressing a button starts an event that requires a loading state, e.g. when you have any sort of asynchronous action and are waiting for a response * * @example * import { BentoLoadingButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoLoadingButton }, * template: ` * <bento-loading-button state="loading" variant="secondary"> * Loading * </bento-loading button> * ` * } */ export default { i18n: { messages }, name: 'bento-loading-button', }; </script> <style lang="scss" scoped src="./loading-button.scss" />
@@ -1 +1 @@
1
- import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { computed, ref, watch } from 'vue'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoToggleButton from './toggle-button.vue'; import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; export default { title: 'Button/Toggle Button', component: BentoToggleButton, argTypes: { condensed: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, default: { name: 'default', defaultValue: '', description: 'Button text', table: { type: { summary: 'VNode[]' }, }, control: { disable: true }, }, disabled: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, variant: { control: { type: 'select', }, table: { type: { summary: 'BentoButtonVariant: primary, secondary, tertiary' }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoToggleButton>; const defaultCode = ` <bento-toggle-button variant="primary" :state="toggleState" @update:state="updateToggleState" > Toggle Button </bento-toggle-button> <script setup lang="ts"> import { BentoToggleButton } from '@adyen/bento-vue2' const toggleState = ref(false); const updateToggleState = (newToggleState: boolean) => { toggleState.value = newToggleState; }; </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > {{toggleButtonText}} </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const toggleButtonText = computed(() => (toggleState.value ? 'Toggled' : 'Un-toggled')); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleButtonText, toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(defaultCode), }; const withIconsCode = ` <bento-toggle-button variant="primary" :state="toggleState" @update:state="updateToggleState" > Toggle Button <template #iconLeft> <CheckmarkCircleFillIcon /> </template> <template #iconRight> <CheckmarkCircleFillIcon /> </template> </bento-toggle-button> <script setup lang="ts"> import { BentoToggleButton } from '@adyen/bento-vue2' import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; const toggleState = ref(false); const updateToggleState = (newToggleState: boolean) => { toggleState.value = newToggleState; }; </script> `; export const WithIcons: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton, CheckmarkCircleFillIcon }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > {{toggleButtonText}} <template #iconLeft> <CheckmarkCircleFillIcon /> </template> <template #iconRight> <CheckmarkCircleFillIcon /> </template> </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const toggleButtonText = computed(() => (toggleState.value ? 'Toggled' : 'Un-toggled')); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleButtonText, toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(withIconsCode), }; const onlyIconCode = ` <bento-toggle-button variant="primary" :state="toggleState" @update:state="updateToggleState" > <template #iconLeft> <OptionsVerticalIcon /> </template> </bento-toggle-button> <script setup lang="ts"> import { BentoToggleButton } from '@adyen/bento-vue2' import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; const toggleState = ref(false); const updateToggleState = (newToggleState: boolean) => { toggleState.value = newToggleState; }; </script> `; export const OnlyIcon: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton, OptionsVerticalIcon }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > <template #iconLeft> <OptionsVerticalIcon /> </template> </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(onlyIconCode), };
1
+ import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { computed, ref, watch } from 'vue'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoToggleButton from './toggle-button.vue'; import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; const ToggleButtonDefault = "<template>\n <bento-toggle-button :toggled=\"toggleState\" variant=\"primary\" @update:toggled=\"updateToggleState\">\n {{ toggleState ? 'Toggled' : 'Un-toggled' }}\n </bento-toggle-button>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoToggleButton } from '@adyen/bento-vue2';\n\n const toggleState = ref(false);\n const updateToggleState = (newToggleState: boolean) => {\n toggleState.value = newToggleState;\n };\n</script>\n"; const ToggleButtonOnlyIcon = "<template>\n <bento-toggle-button :toggled=\"toggleState\" variant=\"primary\" @update:toggled=\"updateToggleState\">\n <template #iconLeft>\n <options-vertical-icon />\n </template>\n </bento-toggle-button>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoToggleButton } from '@adyen/bento-vue2';\n import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical';\n\n const toggleState = ref(false);\n const updateToggleState = (newToggleState: boolean) => {\n toggleState.value = newToggleState;\n };\n</script>\n"; const ToggleButtonWithIcons = "<template>\n <bento-toggle-button :toggled=\"toggleState\" variant=\"primary\" @update:toggled=\"updateToggleState\">\n {{ toggleState ? 'Toggled' : 'Un-toggled' }}\n <template #iconLeft>\n <checkmark-circle-fill-icon />\n </template>\n <template #iconRight>\n <checkmark-circle-fill-icon />\n </template>\n </bento-toggle-button>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoToggleButton } from '@adyen/bento-vue2';\n import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill';\n\n const toggleState = ref(false);\n const updateToggleState = (newToggleState: boolean) => {\n toggleState.value = newToggleState;\n };\n</script>\n"; export default { title: 'Button/Toggle Button', component: BentoToggleButton, argTypes: { condensed: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, default: { name: 'default', defaultValue: '', description: 'Button text', table: { type: { summary: 'VNode[]' }, }, control: { disable: true }, }, disabled: { control: { type: 'boolean', }, table: { type: { summary: 'boolean' }, }, }, variant: { control: { type: 'select', }, table: { type: { summary: 'BentoButtonVariant: primary, secondary, tertiary' }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoToggleButton>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > {{toggleButtonText}} </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const toggleButtonText = computed(() => (toggleState.value ? 'Toggled' : 'Un-toggled')); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleButtonText, toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(ToggleButtonDefault), }; export const WithIcons: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton, CheckmarkCircleFillIcon }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > {{toggleButtonText}} <template #iconLeft> <CheckmarkCircleFillIcon /> </template> <template #iconRight> <CheckmarkCircleFillIcon /> </template> </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const toggleButtonText = computed(() => (toggleState.value ? 'Toggled' : 'Un-toggled')); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleButtonText, toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(ToggleButtonWithIcons), }; export const OnlyIcon: Story = { render: (_args, { argTypes }) => ({ components: { BentoToggleButton, OptionsVerticalIcon }, props: Object.keys(argTypes), template: ` <bento-toggle-button v-bind="args" :toggled="toggleState" @click="clickedButtonAction" @update:toggled="updateToggleState" > <template #iconLeft> <OptionsVerticalIcon /> </template> </bento-toggle-button> `, setup(props) { const args = isVue2 ? props : _args; const toggleState = ref<boolean>(args?.toggled); const updateToggleState = (newToggleState: boolean) => { action('update:toggled')(newToggleState); toggleState.value = newToggleState; }; watch( () => args.toggled, () => { toggleState.value = args.toggled; } ); return { // Methods clickedButtonAction: action('click'), updateToggleState, // Values toggleState, args, }; }, }), args: { toggled: false, variant: 'primary', disabled: false, inverse: false, condensed: false, }, parameters: storybookDocsParameter(ToggleButtonOnlyIcon), };
@@ -49,6 +49,16 @@ title visible.
49
49
 
50
50
  <Canvas of={CardStories.Expandable} />
51
51
 
52
+ ### Linked title
53
+
54
+ Use the `link` property to make the card title a native link. It accepts the same navigation properties as `bento-link`,
55
+ except for `variant`.
56
+
57
+ Do not combine `link` with the `clickable` or `expandable` property. An interactive card containing a nested link
58
+ creates conflicting interactive targets and a confusing experience for keyboard and assistive-technology users.
59
+
60
+ <Canvas of={CardStories.WithLink} />
61
+
52
62
  ### Clickable
53
63
 
54
64
  A clickable `bento-card` is a card container that can be actionable, similar to a button. It becomes interactive, and it
@@ -96,6 +106,13 @@ To configure the actions layout, you can use the different modifier options with
96
106
  detailed information on the modifiers check the
97
107
  [Button actions documentation](../?path=/docs/components-button-button-actions--docs#modifiers).
98
108
 
109
+ ## Last updated
110
+
111
+ Use `lastUpdated` to show when card content was last refreshed. Add the optional `reloadButton` configuration to let
112
+ users refresh the card. Last-updated metadata is available only for regular (non-expandable) cards.
113
+
114
+ <Canvas of={CardStories.WithTimestamp} />
115
+
99
116
  ### Actions as links
100
117
 
101
118
  You can use the `event` prop of the `actions` object to link to another page. Here is an example on how to do this:
@@ -105,7 +122,7 @@ You can use the `event` prop of the `actions` object to link to another page. He
105
122
  <Source
106
123
  dark
107
124
  language="html"
108
- code={`<bento-card
125
+ code={`<bento-card
109
126
  actionsLayout="buttons-start"
110
127
  :actions="cardButtonActions"
111
128
  >
@@ -1 +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, }, };
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'; const CardWithLinkExample = "<template>\n <bento-card :link=\"{ to: '/' }\">\n Card With Link Title\n\n <template #header>\n <bento-typography>Header slot</bento-typography>\n </template>\n\n <template #description>A little context goes a long way</template>\n\n <template #content>\n <bento-typography el=\"div\">\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardClickable = "<template>\n <bento-card clickable @click=\"onClick\">\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n\n const onClick = () => true;\n</script>\n"; const CardDefault = "<template>\n <bento-card>\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardDisabled = "<template>\n <bento-card disabled expandable>\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardExpandable = "<template>\n <bento-card expandable>\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardExpandableClosed = "<template>\n <bento-card closed expandable>\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardSecondaryBackground = "<template>\n <bento-card background=\"secondary\">\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardWithActions = "<template>\n <bento-card :actions=\"actions\" actions-layout=\"buttons-start\">\n Card title\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { type BentoButtonActionsList, BentoCard, BentoTypography } from '@adyen/bento-vue2';\n\n const actions: BentoButtonActionsList = [\n {\n title: 'Primary',\n event: () => true,\n },\n {\n title: 'Secondary',\n event: () => true,\n },\n ];\n</script>\n"; const CardWithHeaderSlot = "<template>\n <bento-card expandable>\n Card title\n <template #header>\n <bento-typography>Card header slot</bento-typography>\n </template>\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n</script>\n"; const CardWithImage = "<template>\n <bento-card>\n Card title\n <template #image>\n <bento-image alt=\"Example image\" :src=\"cardImageSrcStub\" />\n </template>\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam feugiat, nunc quis aliquam facilisis,\n ante nulla aliquet tortor, vel feugiat diam turpis ut neque. Sed ut venenatis ante, in iaculis quam.\n Nulla nisi nisl, semper non tempus vitae, semper vitae nulla. Nam ac nibh eu ipsum lacinia congue.\n Suspendisse vulputate nisi eu erat venenatis, ac venenatis nisl ullamcorper. Aliquam interdum sem sed\n leo sodales, vel accumsan purus consequat. Vivamus luctus non tellus vel gravida.\n </bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoImage, BentoTypography } from '@adyen/bento-vue2';\n import { cardImageSrcStub } from '../../../__tests__/img-stub.json';\n</script>\n"; const CardWithTimestamp = "<template>\n <bento-card :last-updated=\"lastUpdated\" :reload-button=\"reloadButton\">\n Card with timestamp and reload button\n <template #description>A little context goes a long way</template>\n <template #content>\n <bento-typography>Card content</bento-typography>\n </template>\n </bento-card>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoCard, BentoTypography } from '@adyen/bento-vue2';\n\n const lastUpdated = new Date();\n const reloadButton = {\n title: 'Reload',\n event: () => undefined,\n };\n</script>\n"; 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', }, }, lastUpdated: { control: { type: 'date', }, }, 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" v-on="args.link ? {} : { click: cardClicked }" class="b-card-story"> <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'), }; }, }), }; export const Default = { ...Template, parameters: storybookDocsParameter(CardDefault), args: { ...DEFAULT_CARD_PROPS }, }; export const SecondaryBackground = { ...Template, parameters: storybookDocsParameter(CardSecondaryBackground), args: { ...DEFAULT_CARD_PROPS, background: 'secondary', }, }; export const Expandable = { ...Template, parameters: storybookDocsParameter(CardExpandable), args: { ...DEFAULT_CARD_PROPS, expandable: true, }, }; export const ExpandableClosed = { ...Template, parameters: storybookDocsParameter(CardExpandableClosed), args: { ...DEFAULT_CARD_PROPS, expandable: true, closed: true, }, }; export const Disabled = { ...Template, parameters: storybookDocsParameter(CardDisabled), args: { ...DEFAULT_CARD_PROPS, expandable: true, disabled: true, }, }; export const WithActions = { ...Template, parameters: storybookDocsParameter(CardWithActions), args: { ...DEFAULT_CARD_PROPS, actionsLayout: 'buttons-start', actions: CARD_ACTIONS, }, }; export const WithTimestamp = { ...Template, parameters: storybookDocsParameter(CardWithTimestamp), args: { ...DEFAULT_CARD_PROPS, lastUpdated: new Date(), reloadButton: { title: 'Reload', event: () => action('Reload')(), }, }, }; 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, }, parameters: storybookDocsParameter(CardWithHeaderSlot), }; export const WithImage = { components: { BentoImage, }, ...Template, parameters: storybookDocsParameter(CardWithImage), args: { ...DEFAULT_CARD_PROPS, image: { src: IMAGE_SRC, alt: IMAGE_ALT }, }, }; export const WithLink = { ...Template, parameters: storybookDocsParameter(CardWithLinkExample), args: { ...DEFAULT_CARD_PROPS, default: 'Card With Link Title', header: 'Header slot', link: { to: '/', }, }, }; export const Clickable = { ...Template, parameters: storybookDocsParameter(CardClickable), args: { ...DEFAULT_CARD_PROPS, clickable: true, }, };
@@ -1 +1 @@
1
- /** * @deprecated Since v2.0.0. Use string literal instead. */ export enum BentoCardBackground { PRIMARY = 'primary', SECONDARY = 'secondary', } /** * @deprecated Since v2.0.0. Use string literal instead. */ export enum BentoCardTitleSize { DEFAULT = 'default', SMALL = 'small', }
1
+ import { type BentoLinkProps } from '../link'; export { type TitleDetailsReloadButton as BentoCardReloadButton } from '../internal/title-details/title-details.types'; /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoCardBackground { PRIMARY = 'primary', SECONDARY = 'secondary', } /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoCardTitleSize { DEFAULT = 'default', SMALL = 'small', } export type BentoCardLink = Omit<BentoLinkProps, 'variant'>;
@@ -1 +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 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 { contentHeight } = useExpandableContentHeight(contentDiv, showContent); const contentMaxHeight = computed(() => props.expandable && contentHeight.value ? { 'max-height': contentHeight.value } : {} ); const toggleExpansion = () => { if (!props.disabled && props.expandable) { showContent.value = !showContent.value; emit('expanded', showContent.value); } }; 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" />
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> <template v-if="hasSlot('default')"> <bento-link v-if="link && isRegularCard" class="b-card__link" v-bind="link" variant="quiet" disable-typography > <bento-typography class="b-card__title" v-bind="typography" el="span"> <slot /> </bento-typography> <chevron-right-small-icon class="b-card__link-icon" aria-hidden="true" /> </bento-link> <bento-typography v-else class="b-card__title" v-bind="typography"> <slot /> </bento-typography> </template> </div> <template v-if="hasSlot('header')"> <slot name="header"></slot> </template> </div> <div v-if="hasSlot('description') || showLastUpdated" class="b-card__title-details"> <title-details :last-updated="showLastUpdated ? lastUpdated : null" :reload-button="showLastUpdated ? reloadButton : null" > <template v-if="hasSlot('description')" #description><slot name="description" /></template> </title-details> </div> </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, onMounted, ref, useSlots, watch } from 'vue'; // Components import { BentoTypography } from '@/components/typography'; import { BentoLink } from '@/components/link'; import { BentoButtonActions, BentoButtonActionsLayout, type BentoButtonActionsList } from '@/components/button'; import { BentoCardBackground, type BentoCardLink, type BentoCardReloadButton, BentoCardTitleSize, } from '@/components/card/card.types'; import { TitleDetails } from '@/internal/title-details'; import ChevronDownIcon from '@adyen/ui-assets-icons-16/vue/chevron-down'; import ChevronUpIcon from '@adyen/ui-assets-icons-16/vue/chevron-up'; import ChevronRightSmallIcon from '@adyen/ui-assets-icons-16/vue/chevron-right-small'; // Composables import { useExpandableContentHeight } from '@/composables/use-expandable-content-height/use-expandable-content-height'; // Utils import { useHasSlot } from '@/composables'; import { generateUid } from '@/utils/ts/generate-uid'; import { useI18n } from '@/utils/ts/i18n'; 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; /** * JavaScript "Date" object for when the content was last updated * * Only supported by regular (non-expandable) cards. */ lastUpdated?: Date | number; /** * Reload button which shows next to the card's timestamp * * Only supported by regular (non-expandable) cards. */ reloadButton?: BentoCardReloadButton; /** * Link properties to make the card title a navigable link. Can only be used with non-expandable cards. */ link?: BentoCardLink; /** * 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, lastUpdated: null, reloadButton: null, link: undefined, 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 showLastUpdated = computed(() => !props.expandable && Boolean(props.lastUpdated)); const showHeader = computed( () => hasSlot('default') || hasSlot('description') || hasSlot('header') || showLastUpdated.value ); 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 isRegularCard = computed(() => !props.expandable); 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 { contentHeight } = useExpandableContentHeight(contentDiv, showContent); const contentMaxHeight = computed(() => props.expandable && contentHeight.value ? { 'max-height': contentHeight.value } : {} ); const toggleExpansion = () => { if (!props.disabled && props.expandable) { showContent.value = !showContent.value; emit('expanded', showContent.value); } }; 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.`, '3.0.0' ); } } }; const onKeyDown = () => { if (props.clickable && !props.disabled) { emit('click'); } }; onMounted(() => { if (props.link && props.clickable) { throw new Error( 'Do not combine the BentoCard "link" property with the "clickable" property. Nested interactive controls create a confusing and inaccessible experience for users of assistive technologies.' ); } }); </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" />
@@ -1 +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), };
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 CheckboxDefault = "<template>\n <div>\n <!-- With model-value -->\n <bento-checkbox :model-value=\"selectedValue\" @update:model-value=\"selectedValue = $event\">\n Label text\n <template #description>Description below label</template>\n </bento-checkbox>\n\n <!-- With v-model in Vue 2 and Vue 3 -->\n <bento-checkbox v-model=\"selectedValue\">\n Label text\n <template #description>Description below label</template>\n </bento-checkbox>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoCheckbox, type BentoCheckboxType } from '@adyen/bento-vue2';\n\n const selectedValue = ref<BentoCheckboxType>(false);\n</script>\n"; const CheckboxDisabled = "<template>\n <bento-checkbox v-model=\"selectedValue\" disabled>I am disabled</bento-checkbox>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoCheckbox, type BentoCheckboxType } from '@adyen/bento-vue2';\n\n const selectedValue = ref<BentoCheckboxType>(false);\n</script>\n"; const CheckboxError = "<template>\n <bento-checkbox v-model=\"selectedValue\" has-error>I am in error state</bento-checkbox>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoCheckbox, type BentoCheckboxType } from '@adyen/bento-vue2';\n\n const selectedValue = ref<BentoCheckboxType>(false);\n</script>\n"; const CheckboxErrorMessage = "<template>\n <bento-checkbox v-model=\"selectedValue\" error-message=\"Error message text\">\n I have an error message\n </bento-checkbox>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoCheckbox, type BentoCheckboxType } from '@adyen/bento-vue2';\n\n const selectedValue = ref<BentoCheckboxType>(false);\n</script>\n"; const CheckboxIndeterminate = "<template>\n <bento-checkbox v-model=\"selectedValue\" indeterminate> I am indeterminate </bento-checkbox>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoCheckbox, type BentoCheckboxType } from '@adyen/bento-vue2';\n\n const selectedValue = ref<BentoCheckboxType>(false);\n</script>\n"; 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 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(CheckboxDefault), }; export const Disabled: Story = { ...Basic, args: { default: 'I am disabled', disabled: true, }, parameters: storybookDocsParameter(CheckboxDisabled), }; export const Indeterminate = { ...Basic, args: { default: 'I am indeterminate', indeterminate: true, }, parameters: storybookDocsParameter(CheckboxIndeterminate), }; export const Error = { ...Basic, args: { default: 'I am in error state', hasError: true, }, parameters: storybookDocsParameter(CheckboxError), }; export const ErrorMessage = { ...Basic, args: { default: 'I have an error message', errorMessage: ERROR_MESSAGE, }, parameters: storybookDocsParameter(CheckboxErrorMessage), };
@@ -1 +1 @@
1
- import type { BentoTagProps } from '../tag/tag.types'; import { CheckboxInputState as BentoCheckboxState, type CheckboxInputType as BentoCheckboxType, type MultiCheckboxInputType as BentoMultiCheckboxType, } from '@/internal/checkbox-input/checkbox-input.types'; export { BentoCheckboxState, BentoCheckboxType, BentoMultiCheckboxType }; export enum BentoCheckboxEvent { UPDATE = 'update:model-value', INPUT = 'input', } export type BentoCheckboxTag = BentoTagProps;
1
+ import type { BentoTagProps } from '../tag/tag.types'; export enum BentoCheckboxEvent { UPDATE = 'update:model-value', INPUT = 'input', } export type BentoCheckboxTag = BentoTagProps;
@@ -1 +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" :aria-required="required" :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" >*</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 Since v2.0.0. Use `aria-label` 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" />
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" :aria-required="required" :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" >*</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, type BentoCheckboxTag } 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 '@/utils/ts/generate-uid'; import { getSlotText } from '@/utils/ts/get-slot-text'; import { useI18n } from '@/utils/ts/i18n'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { BentoCheckboxState, type BentoCheckboxType, type BentoMultiCheckboxType } from '@/types/checkbox'; 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 Since v3.0.0. Use `aria-label` 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" />