@adyen/bento-mcp 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -0
  4. package/dist/assets/components/accordion/accordion.docs.mdx +77 -0
  5. package/dist/assets/components/accordion/accordion.stories.ts +1 -0
  6. package/dist/assets/components/accordion/accordion.vue +1 -0
  7. package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -0
  8. package/dist/assets/components/accordion/components/accordion-item.vue +1 -0
  9. package/dist/assets/components/action-bar/action-bar.docs.mdx +103 -0
  10. package/dist/assets/components/action-bar/action-bar.stories.ts +1 -0
  11. package/dist/assets/components/action-bar/action-bar.vue +1 -0
  12. package/dist/assets/components/alert/alert.docs.mdx +106 -0
  13. package/dist/assets/components/alert/alert.stories.ts +1 -0
  14. package/dist/assets/components/alert/alert.vue +1 -0
  15. package/dist/assets/components/avatar/avatar.docs.mdx +44 -0
  16. package/dist/assets/components/avatar/avatar.stories.ts +1 -0
  17. package/dist/assets/components/avatar/avatar.vue +1 -0
  18. package/dist/assets/components/avatar/components/avatar-image/avatar-image.vue +1 -0
  19. package/dist/assets/components/button/button.docs.mdx +137 -0
  20. package/dist/assets/components/button/button.stories.ts +1 -0
  21. package/dist/assets/components/button/button.vue +1 -0
  22. package/dist/assets/components/button/components/ai-button/ai-button.docs.mdx +40 -0
  23. package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -0
  24. package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -0
  25. package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +169 -0
  26. package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -0
  27. package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -0
  28. package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +106 -0
  29. package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -0
  30. package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -0
  31. package/dist/assets/components/button/components/toggle-button/toggle-button.docs.mdx +81 -0
  32. package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -0
  33. package/dist/assets/components/button/components/toggle-button/toggle-button.vue +1 -0
  34. package/dist/assets/components/card/card.docs.mdx +166 -0
  35. package/dist/assets/components/card/card.stories.ts +1 -0
  36. package/dist/assets/components/card/card.vue +1 -0
  37. package/dist/assets/components/checkbox/checkbox.docs.mdx +116 -0
  38. package/dist/assets/components/checkbox/checkbox.stories.ts +1 -0
  39. package/dist/assets/components/checkbox/checkbox.vue +1 -0
  40. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +134 -0
  41. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -0
  42. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -0
  43. package/dist/assets/components/chip/chip.docs.mdx +51 -0
  44. package/dist/assets/components/chip/chip.stories.ts +1 -0
  45. package/dist/assets/components/chip/chip.vue +1 -0
  46. package/dist/assets/components/click-outside/click-outside.docs.mdx +30 -0
  47. package/dist/assets/components/click-outside/click-outside.stories.ts +1 -0
  48. package/dist/assets/components/code-snippet/code-snippet.docs.mdx +73 -0
  49. package/dist/assets/components/code-snippet/code-snippet.stories.ts +1 -0
  50. package/dist/assets/components/code-snippet/code-snippet.vue +1 -0
  51. package/dist/assets/components/country/country.docs.mdx +61 -0
  52. package/dist/assets/components/country/country.stories.ts +1 -0
  53. package/dist/assets/components/country/country.vue +1 -0
  54. package/dist/assets/components/currency/currency.docs.mdx +38 -0
  55. package/dist/assets/components/currency/currency.stories.ts +1 -0
  56. package/dist/assets/components/currency/currency.vue +1 -0
  57. package/dist/assets/components/dashed-underline/dashed-underline.docs.mdx +57 -0
  58. package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -0
  59. package/dist/assets/components/dashed-underline/dashed-underline.vue +1 -0
  60. package/dist/assets/components/data-grid/components/data-grid-cell/data-grid-cell.vue +1 -0
  61. package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -0
  62. package/dist/assets/components/data-grid/components/data-grid-columns/data-grid-columns.vue +1 -0
  63. package/dist/assets/components/data-grid/components/data-grid-config-settings/components/data-grid-config-settings-column-row/data-grid-config-settings-column-row.vue +1 -0
  64. package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -0
  65. package/dist/assets/components/data-grid/components/data-grid-lazy-load/data-grid-lazy-load.vue +1 -0
  66. package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -0
  67. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.docs.mdx +61 -0
  68. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -0
  69. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.vue +1 -0
  70. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.docs.mdx +56 -0
  71. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -0
  72. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -0
  73. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.docs.mdx +48 -0
  74. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -0
  75. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -0
  76. package/dist/assets/components/data-grid/data-grid.docs.mdx +951 -0
  77. package/dist/assets/components/data-grid/data-grid.stories.ts +1 -0
  78. package/dist/assets/components/data-grid/data-grid.vue +1 -0
  79. package/dist/assets/components/date/date.docs.mdx +49 -0
  80. package/dist/assets/components/date/date.stories.ts +1 -0
  81. package/dist/assets/components/date/date.vue +1 -0
  82. package/dist/assets/components/date-picker/date-picker.docs.mdx +69 -0
  83. package/dist/assets/components/date-picker/date-picker.stories.ts +1 -0
  84. package/dist/assets/components/date-picker/date-picker.vue +1 -0
  85. package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -0
  86. package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +168 -0
  87. package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -0
  88. package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -0
  89. package/dist/assets/components/divider/divider.docs.mdx +27 -0
  90. package/dist/assets/components/divider/divider.stories.ts +1 -0
  91. package/dist/assets/components/divider/divider.vue +1 -0
  92. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -0
  93. package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -0
  94. package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -0
  95. package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -0
  96. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -0
  97. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -0
  98. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -0
  99. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -0
  100. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -0
  101. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -0
  102. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -0
  103. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -0
  104. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -0
  105. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -0
  106. package/dist/assets/components/dropdown/dropdown.docs.mdx +420 -0
  107. package/dist/assets/components/dropdown/dropdown.stories.ts +1 -0
  108. package/dist/assets/components/dropdown/dropdown.vue +1 -0
  109. package/dist/assets/components/empty-state/empty-state.docs.mdx +115 -0
  110. package/dist/assets/components/empty-state/empty-state.stories.ts +1 -0
  111. package/dist/assets/components/empty-state/empty-state.vue +1 -0
  112. package/dist/assets/components/file-uploader/components/file-uploader-file-card/components/file-uploader-file-card-animation/file-uploader-file-card-animation.vue +1 -0
  113. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -0
  114. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -0
  115. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.stories.ts +1 -0
  116. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -0
  117. package/dist/assets/components/file-uploader/file-uploader.docs.mdx +117 -0
  118. package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -0
  119. package/dist/assets/components/file-uploader/file-uploader.vue +1 -0
  120. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -0
  121. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -0
  122. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-button/all-filters-modal-button.vue +1 -0
  123. package/dist/assets/components/filter-bar/components/base-filter/base-filter.docs.mdx +90 -0
  124. package/dist/assets/components/filter-bar/components/base-filter/base-filter.stories.ts +1 -0
  125. package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -0
  126. package/dist/assets/components/filter-bar/components/base-filter-button/base-filter-button.vue +1 -0
  127. package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -0
  128. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.stories.ts +1 -0
  129. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.vue +1 -0
  130. package/dist/assets/components/filter-bar/components/date-filter/date-filter.stories.ts +1 -0
  131. package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -0
  132. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.stories.ts +1 -0
  133. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -0
  134. package/dist/assets/components/filter-bar/components/filter-bar-button/filter-bar-button.vue +1 -0
  135. package/dist/assets/components/filter-bar/components/filter-bar-button-group/filter-bar-button-group.vue +1 -0
  136. package/dist/assets/components/filter-bar/components/input-filter/input-filter.docs.mdx +18 -0
  137. package/dist/assets/components/filter-bar/components/input-filter/input-filter.stories.ts +1 -0
  138. package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -0
  139. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.docs.mdx +18 -0
  140. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -0
  141. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -0
  142. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.stories.ts +1 -0
  143. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.vue +1 -0
  144. package/dist/assets/components/filter-bar/components/range-filter/range-filter.vue +1 -0
  145. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -0
  146. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-dropdown/select-filter-dropdown.vue +1 -0
  147. package/dist/assets/components/filter-bar/components/select-filter/select-filter.stories.ts +1 -0
  148. package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -0
  149. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -0
  150. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.vue +1 -0
  151. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -0
  152. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -0
  153. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.stories.ts +1 -0
  154. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.vue +1 -0
  155. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.stories.ts +1 -0
  156. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.vue +1 -0
  157. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.stories.ts +1 -0
  158. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.vue +1 -0
  159. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.stories.ts +1 -0
  160. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -0
  161. package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1005 -0
  162. package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -0
  163. package/dist/assets/components/filter-bar/filter-bar.vue +1 -0
  164. package/dist/assets/components/focus-trap/focus-trap.docs.mdx +10 -0
  165. package/dist/assets/components/focus-trap/focus-trap.stories.ts +1 -0
  166. package/dist/assets/components/focus-trap/focus-trap.vue +1 -0
  167. package/dist/assets/components/form-layout/components/form-layout-field-tip/form-layout-field-tip.vue +1 -0
  168. package/dist/assets/components/form-layout/components/form-layout-group/form-layout-group.vue +1 -0
  169. package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -0
  170. package/dist/assets/components/form-layout/form-layout.docs.mdx +452 -0
  171. package/dist/assets/components/form-layout/form-layout.stories.ts +1 -0
  172. package/dist/assets/components/form-layout/form-layout.vue +1 -0
  173. package/dist/assets/components/header/header.docs.mdx +85 -0
  174. package/dist/assets/components/header/header.stories.ts +1 -0
  175. package/dist/assets/components/header/header.vue +1 -0
  176. package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -0
  177. package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -0
  178. package/dist/assets/components/header-with-views/header-with-views.docs.mdx +103 -0
  179. package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -0
  180. package/dist/assets/components/header-with-views/header-with-views.vue +1 -0
  181. package/dist/assets/components/image/image.docs.mdx +34 -0
  182. package/dist/assets/components/image/image.stories.ts +1 -0
  183. package/dist/assets/components/image/image.vue +1 -0
  184. package/dist/assets/components/info-icon/info-icon.docs.mdx +57 -0
  185. package/dist/assets/components/info-icon/info-icon.stories.ts +1 -0
  186. package/dist/assets/components/info-icon/info-icon.vue +1 -0
  187. package/dist/assets/components/input-field/input-field.docs.mdx +141 -0
  188. package/dist/assets/components/input-field/input-field.stories.ts +1 -0
  189. package/dist/assets/components/input-field/input-field.vue +1 -0
  190. package/dist/assets/components/input-field-password/input-field-password.docs.mdx +292 -0
  191. package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -0
  192. package/dist/assets/components/input-field-password/input-field-password.vue +1 -0
  193. package/dist/assets/components/input-field-phone-number/input-field-phone-number.docs.mdx +72 -0
  194. package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -0
  195. package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -0
  196. package/dist/assets/components/inspector/inspector.docs.mdx +118 -0
  197. package/dist/assets/components/inspector/inspector.stories.ts +1 -0
  198. package/dist/assets/components/inspector/inspector.vue +1 -0
  199. package/dist/assets/components/internal/base-button/base-button.vue +1 -0
  200. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +1 -0
  201. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +1 -0
  202. package/dist/assets/components/internal/calendar/calendar.stories.ts +1 -0
  203. package/dist/assets/components/internal/calendar/calendar.vue +1 -0
  204. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +1 -0
  205. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +1 -0
  206. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +1 -0
  207. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +1 -0
  208. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +1 -0
  209. package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +1 -0
  210. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +1 -0
  211. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +1 -0
  212. package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +1 -0
  213. package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +1 -0
  214. package/dist/assets/components/internal/controls-group/controls-group.vue +1 -0
  215. package/dist/assets/components/internal/copy/copy.docs.mdx +43 -0
  216. package/dist/assets/components/internal/copy/copy.stories.ts +1 -0
  217. package/dist/assets/components/internal/copy/copy.vue +1 -0
  218. package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +1 -0
  219. package/dist/assets/components/internal/dialog-page/dialog-page.vue +1 -0
  220. package/dist/assets/components/internal/error-message/error-message.vue +1 -0
  221. package/dist/assets/components/internal/field-label/field-label.vue +1 -0
  222. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +21 -0
  223. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +1 -0
  224. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +1 -0
  225. package/dist/assets/components/internal/footer-actions/footer-actions.vue +1 -0
  226. package/dist/assets/components/internal/grid-layout/grid-layout.vue +1 -0
  227. package/dist/assets/components/internal/header-meta/header-meta.vue +1 -0
  228. package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +1 -0
  229. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-category/listbox-multi-select-category.vue +1 -0
  230. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +1 -0
  231. package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +1 -0
  232. package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +1 -0
  233. package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +1 -0
  234. package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +1 -0
  235. package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +1 -0
  236. package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +1 -0
  237. package/dist/assets/components/internal/listbox/listbox.stories.ts +1 -0
  238. package/dist/assets/components/internal/listbox/listbox.vue +1 -0
  239. package/dist/assets/components/internal/radio-input/radio-input.stories.ts +1 -0
  240. package/dist/assets/components/internal/radio-input/radio-input.vue +1 -0
  241. package/dist/assets/components/internal/teleport/teleport.vue +1 -0
  242. package/dist/assets/components/internal/tooltip/tooltip.test.vue +1 -0
  243. package/dist/assets/components/internal/tooltip/tooltip.vue +1 -0
  244. package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -0
  245. package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -0
  246. package/dist/assets/components/layout/layout.docs.mdx +108 -0
  247. package/dist/assets/components/layout/layout.stories.ts +1 -0
  248. package/dist/assets/components/layout/layout.vue +1 -0
  249. package/dist/assets/components/link/link.docs.mdx +119 -0
  250. package/dist/assets/components/link/link.stories.ts +1 -0
  251. package/dist/assets/components/link/link.vue +1 -0
  252. package/dist/assets/components/loading-indicator/loading-indicator.docs.mdx +65 -0
  253. package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -0
  254. package/dist/assets/components/loading-indicator/loading-indicator.vue +1 -0
  255. package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -0
  256. package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -0
  257. package/dist/assets/components/menu/components/menu-list-container/menu-list-container.vue +1 -0
  258. package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -0
  259. package/dist/assets/components/menu/menu.docs.mdx +121 -0
  260. package/dist/assets/components/menu/menu.stories.ts +1 -0
  261. package/dist/assets/components/menu/menu.vue +1 -0
  262. package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -0
  263. package/dist/assets/components/modal/components/modal-default/modal-default.vue +1 -0
  264. package/dist/assets/components/modal/components/modal-dialog/modal-dialog.vue +1 -0
  265. package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -0
  266. package/dist/assets/components/modal/modal.docs.mdx +215 -0
  267. package/dist/assets/components/modal/modal.stories.ts +1 -0
  268. package/dist/assets/components/modal/modal.vue +1 -0
  269. package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -0
  270. package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +217 -0
  271. package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -0
  272. package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -0
  273. package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -0
  274. package/dist/assets/components/pagination/components/pagination-controls/pagination-controls.vue +1 -0
  275. package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -0
  276. package/dist/assets/components/pagination/pagination.docs.mdx +116 -0
  277. package/dist/assets/components/pagination/pagination.stories.ts +1 -0
  278. package/dist/assets/components/pagination/pagination.vue +1 -0
  279. package/dist/assets/components/payment-method/payment-method.docs.mdx +34 -0
  280. package/dist/assets/components/payment-method/payment-method.stories.ts +1 -0
  281. package/dist/assets/components/payment-method/payment-method.vue +1 -0
  282. package/dist/assets/components/popover/components/popover-dismiss-button/popover-dismiss-button.vue +1 -0
  283. package/dist/assets/components/popover/components/popover-image/popover-image.vue +1 -0
  284. package/dist/assets/components/popover/components/popover-title/popover-title.vue +1 -0
  285. package/dist/assets/components/popover/popover.docs.mdx +89 -0
  286. package/dist/assets/components/popover/popover.stories.ts +1 -0
  287. package/dist/assets/components/popover/popover.vue +1 -0
  288. package/dist/assets/components/popper-container/popper-container.docs.mdx +11 -0
  289. package/dist/assets/components/popper-container/popper-container.stories.ts +1 -0
  290. package/dist/assets/components/popper-container/popper-container.vue +1 -0
  291. package/dist/assets/components/promo-banner/promo-banner.docs.mdx +94 -0
  292. package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -0
  293. package/dist/assets/components/promo-banner/promo-banner.vue +1 -0
  294. package/dist/assets/components/radio-group/components/radio-button/radio-button.docs.mdx +76 -0
  295. package/dist/assets/components/radio-group/components/radio-button/radio-button.stories.ts +1 -0
  296. package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -0
  297. package/dist/assets/components/radio-group/radio-group.docs.mdx +154 -0
  298. package/dist/assets/components/radio-group/radio-group.stories.ts +1 -0
  299. package/dist/assets/components/radio-group/radio-group.vue +1 -0
  300. package/dist/assets/components/rich-text-editor/components/rich-text-editor-content-area/rich-text-editor-content-area.vue +1 -0
  301. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-code-block/rich-text-editor-code-block.vue +1 -0
  302. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-inline-code/rich-text-editor-inline-code.vue +1 -0
  303. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-link/rich-text-editor-link.vue +1 -0
  304. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-list-item/rich-text-editor-list-item.vue +1 -0
  305. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-table/rich-text-editor-table.vue +1 -0
  306. package/dist/assets/components/rich-text-editor/rich-text-editor.docs.mdx +52 -0
  307. package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -0
  308. package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -0
  309. package/dist/assets/components/search-bar/search-bar.docs.mdx +117 -0
  310. package/dist/assets/components/search-bar/search-bar.stories.ts +1 -0
  311. package/dist/assets/components/search-bar/search-bar.vue +1 -0
  312. package/dist/assets/components/secondary-nav/components/secondary-nav-category/secondary-nav-category.vue +1 -0
  313. package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -0
  314. package/dist/assets/components/secondary-nav/secondary-nav.docs.mdx +40 -0
  315. package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -0
  316. package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -0
  317. package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -0
  318. package/dist/assets/components/segmented-control/segmented-control.docs.mdx +90 -0
  319. package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -0
  320. package/dist/assets/components/segmented-control/segmented-control.vue +1 -0
  321. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +119 -0
  322. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -0
  323. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -0
  324. package/dist/assets/components/selection-card/selection-card.docs.mdx +108 -0
  325. package/dist/assets/components/selection-card/selection-card.stories.ts +1 -0
  326. package/dist/assets/components/selection-card/selection-card.vue +1 -0
  327. package/dist/assets/components/sidepanel/components/sidepanel-page.stories.ts +1 -0
  328. package/dist/assets/components/sidepanel/components/sidepanel-page.vue +1 -0
  329. package/dist/assets/components/sidepanel/sidepanel.docs.mdx +129 -0
  330. package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -0
  331. package/dist/assets/components/sidepanel/sidepanel.vue +1 -0
  332. package/dist/assets/components/status/status.docs.mdx +62 -0
  333. package/dist/assets/components/status/status.stories.ts +1 -0
  334. package/dist/assets/components/status/status.vue +1 -0
  335. package/dist/assets/components/stepper/components/step/step.stories.ts +1 -0
  336. package/dist/assets/components/stepper/components/step/step.vue +1 -0
  337. package/dist/assets/components/stepper/stepper.docs.mdx +127 -0
  338. package/dist/assets/components/stepper/stepper.stories.ts +1 -0
  339. package/dist/assets/components/stepper/stepper.vue +1 -0
  340. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.stories.ts +1 -0
  341. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.vue +1 -0
  342. package/dist/assets/components/structured-list/structured-list.docs.mdx +68 -0
  343. package/dist/assets/components/structured-list/structured-list.stories.ts +1 -0
  344. package/dist/assets/components/structured-list/structured-list.vue +1 -0
  345. package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.vue +1 -0
  346. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.stories.ts +1 -0
  347. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.vue +1 -0
  348. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.stories.ts +1 -0
  349. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.vue +1 -0
  350. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.stories.ts +1 -0
  351. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.vue +1 -0
  352. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.stories.ts +1 -0
  353. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.vue +1 -0
  354. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.stories.ts +1 -0
  355. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.vue +1 -0
  356. package/dist/assets/components/summary-grid/summary-grid.docs.mdx +152 -0
  357. package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -0
  358. package/dist/assets/components/summary-grid/summary-grid.vue +1 -0
  359. package/dist/assets/components/tabs/components/tab.vue +1 -0
  360. package/dist/assets/components/tabs/tabs.docs.mdx +98 -0
  361. package/dist/assets/components/tabs/tabs.stories.ts +1 -0
  362. package/dist/assets/components/tabs/tabs.vue +1 -0
  363. package/dist/assets/components/tag/tag.docs.mdx +137 -0
  364. package/dist/assets/components/tag/tag.stories.ts +1 -0
  365. package/dist/assets/components/tag/tag.vue +1 -0
  366. package/dist/assets/components/textarea/textarea.docs.mdx +53 -0
  367. package/dist/assets/components/textarea/textarea.stories.ts +1 -0
  368. package/dist/assets/components/textarea/textarea.vue +1 -0
  369. package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -0
  370. package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -0
  371. package/dist/assets/components/timeline/timeline.docs.mdx +131 -0
  372. package/dist/assets/components/timeline/timeline.stories.ts +1 -0
  373. package/dist/assets/components/timeline/timeline.vue +1 -0
  374. package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -0
  375. package/dist/assets/components/toast/components/toast-shadow-stack/toast-shadow-stack.vue +1 -0
  376. package/dist/assets/components/toast/toast.docs.mdx +151 -0
  377. package/dist/assets/components/toast/toast.stories.ts +1 -0
  378. package/dist/assets/components/toast/toast.vue +1 -0
  379. package/dist/assets/components/toggle/toggle.docs.mdx +81 -0
  380. package/dist/assets/components/toggle/toggle.stories.ts +1 -0
  381. package/dist/assets/components/toggle/toggle.vue +1 -0
  382. package/dist/assets/components/tutorial/components/tutorial-popover/components/tutorial-indicator/tutorial-indicator.vue +1 -0
  383. package/dist/assets/components/tutorial/components/tutorial-popover/tutorial-popover.vue +1 -0
  384. package/dist/assets/components/tutorial/tutorial.docs.mdx +85 -0
  385. package/dist/assets/components/tutorial/tutorial.stories.ts +1 -0
  386. package/dist/assets/components/tutorial/tutorial.vue +1 -0
  387. package/dist/assets/components/typography/typography.docs.mdx +124 -0
  388. package/dist/assets/components/typography/typography.stories.ts +1 -0
  389. package/dist/assets/components/typography/typography.vue +1 -0
  390. package/dist/assets/composables/use-bento-currency/use-bento-currency.docs.mdx +28 -0
  391. package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -0
  392. package/dist/assets/composables/use-click-outside/use-click-outside.docs.mdx +10 -0
  393. package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -0
  394. package/dist/assets/directives/highlight/highlight.docs.mdx +64 -0
  395. package/dist/assets/directives/highlight/highlight.stories.ts +1 -0
  396. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.docs.mdx +41 -0
  397. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -0
  398. package/dist/assets/directives/tooltip/tooltip.docs.mdx +86 -0
  399. package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -0
  400. package/dist/assets/index.ts +1 -0
  401. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -0
  402. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.vue +1 -0
  403. package/dist/assets/templates/dashboard-template/dashboard-template.docs.mdx +148 -0
  404. package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -0
  405. package/dist/assets/templates/dashboard-template/dashboard-template.vue +1 -0
  406. package/dist/assets/templates/data-grid-template/data-grid-template.docs.mdx +117 -0
  407. package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -0
  408. package/dist/assets/templates/data-grid-template/data-grid-template.vue +1 -0
  409. package/dist/main.js +30031 -0
  410. package/package.json +45 -3
@@ -0,0 +1 @@
1
+ <template> <Transition name="b-popover__animation"> <popper-container v-if="open" :id="computedAttributes['id']" :aria-role="computedAttributes.role" :aria-label="computedAriaLabel" :arrow="{ classes: ['b-popover__arrow'] }" :disabled-focus-trap="disableFocusTrap" :trap-all="trapAll" :trap-all-options="trapAllOptions" :class="conditionalClasses" :fallback-position="fallbackPosition" :fixed-positioning="fixedPositioning" :offset="[0, offset]" :position="position" :target-element="targetElement" class="b-popover" @placement="$emit('placement', $event)" > <div v-if="hasSlot('image')" class="b-popover__image"> <div v-if="dismissible" class="b-popover__image-dismiss-button"> <bento-popover-dismiss-button on-image @click="dismissPopover" /> </div> <slot name="image"></slot> </div> <div v-if="title" class="b-popover__header"> <div class="b-popover__header-title"> <slot v-if="hasSlot('headerIcon')" name="headerIcon"></slot> <bento-popover-title :title="title" :is-image-title="!!hasSlot('image')" /> </div> <div v-if="dismissible && !hasSlot('image')"> <bento-popover-dismiss-button @click="dismissPopover" /> </div> </div> <div v-if="hasSlot('default')" class="b-popover__content" :class="contentConditionalClasses"> <slot></slot> </div> <div v-if="actions" class="b-popover__footer"> <bento-button-actions :actions="actions" :layout="actionsLayout"></bento-button-actions> </div> </popper-container> </Transition> </template> <script setup lang="ts"> import { computed, inject, ref, toRefs, useAttrs, useSlots } from 'vue'; import BentoPopoverDismissButton from './components/popover-dismiss-button/popover-dismiss-button.vue'; import BentoPopoverTitle from './components/popover-title/popover-title.vue'; import BentoButtonActions from '@/components/button/components/button-actions/button-actions.vue'; import { PopperContainer, PopperContainerAriaRole } from '@/components/popper-container'; import { useHasSlot } from '@/composables/use-has-slot/use-has-slot'; import { INPUT_FILTER_POPOVER_INJECTION_KEY } from '@/components/filter-bar/components/input-filter/input-filter.keys'; import { INPUT_FILTER_WITH_DROPDOWN_POPOVER_INJECTION_KEY } from '@/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.keys'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { POPOVER_OFFSET_INJECTION_KEY } from './popover.keys'; import type { Placement } from '@floating-ui/vue'; import type { BentoPopoverProps } from './popover.types'; const DEFAULT_OFFSET = 2; const props = withDefaults(defineProps<BentoPopoverProps>(), { actions: undefined, actionsLayout: 'buttons-end', ariaLabel: undefined, divider: false, disableFocusTrap: false, dismissible: false, fallbackPosition: () => ['bottom-start', 'right-start', 'top-start', 'left-start'], fitContent: false, fixedPositioning: false, open: false, large: false, modifiers: undefined, position: 'bottom-start', small: false, title: undefined, trapAll: false, trapAllOptions: undefined, withoutSpace: false, overflowVisible: false, }); const emit = defineEmits<{ /** * Triggered when the `dismiss` button is clicked */ (e: 'dismiss'): void; /** * Triggered when the popover `placement` changes */ (e: 'placement', placement: Placement): void; }>(); const slots = useSlots(); const attrs = useAttrs(); const computedAttributes = computed(() => ({ id: attrs.id?.toString() as string, role: attrs?.['role'] === null ? undefined : PopperContainerAriaRole.POPOVER, })); const offset = inject<number>(POPOVER_OFFSET_INJECTION_KEY, DEFAULT_OFFSET); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel, title } = toRefs(props); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, defaultFallback: title, }); const isOverflowVisible = inject(INPUT_FILTER_WITH_DROPDOWN_POPOVER_INJECTION_KEY, false) || inject(INPUT_FILTER_POPOVER_INJECTION_KEY, false); const conditionalClasses = computed(() => ({ 'b-popover--small': props.small && !props.large, 'b-popover--with-divider': !!props.divider, 'b-popover--large': props.large && !props.small, 'b-popover--fit-content': props.fitContent, 'b-popover--without-space': props.withoutSpace, 'b-popover--overflow-visible': props.overflowVisible || isOverflowVisible, })); const contentConditionalClasses = computed(() => ({ 'b-popover__content--with-title': !!props.title, })); const dismissPopover = () => emit('dismiss'); const hasSlot = useHasSlot(slots); </script> <script lang="ts"> /** * Example description of the component... * * @example * import { BentoPopover } from '@adyen/adl-vue2'; * * export default { * components: { BentoPopover }, * template: ` * <bento-popover id="your-popover-id"> * ...Example code of the component * </bento-popover> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./popover.scss" />
@@ -0,0 +1,11 @@
1
+ import { Canvas, Story, Meta } from '@storybook/blocks';
2
+ import * as ComponentStories from './popper-container.stories';
3
+
4
+ <Meta of={ComponentStories} />
5
+
6
+ # Popper Container / Popover
7
+
8
+ A popover is a window that opens when a user clicks on an element. A popover can contain other components like buttons
9
+ or lists
10
+
11
+ <Canvas of={ComponentStories.Default} />
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { PopperContainerPosition } from './popper-container.types'; import { isVue2 } from 'vue-demi'; import type { Meta, StoryObj } from '@storybook/vue'; import PopperContainer from './popper-container.vue'; const meta: Meta = { title: 'Popper Container', component: PopperContainer, argTypes: { fixedPositioning: { control: 'boolean' }, position: { options: Object.values(PopperContainerPosition), mapping: PopperContainerPosition, control: { type: 'select', }, }, fallbackPosition: { options: Object.values(PopperContainerPosition), mapping: PopperContainerPosition, control: { type: 'select', }, }, withArrow: { control: 'boolean' }, }, args: { fixedPositioning: false }, }; export default meta; type Story = StoryObj<typeof PopperContainer>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { PopperContainer }, props: Object.keys(argTypes), data: () => ({ isOpen: false }), setup(props) { const onPlacementUpdate = action('placement'); return { // Values args: isVue2 ? props : _args, // Methods onPlacementUpdate, }; }, template: ` <div style="margin: 150px"> <button ref="target" @click="isOpen = !isOpen">Toggle popover</button> <popper-container v-if="isOpen" v-bind="args" :target-element="$refs.target" :arrow="args.withArrow ? { strokeClass: this.arrowStrokeClass, fillClass: this.arrowFillClass, padding: this.arrowPadding } : false" :offset="[args.offsetSkidding, args.offsetDistance]" @placement="onPlacementUpdate" > <div class="adl2-card__body"> <p class="adl2-text">I'm a popping card!</p> </div> </popper-container> </div> `, }), args: { position: 'auto', withArrow: false, arrowStrokeClass: 'arrow-stroke-class', arrowFillClass: 'arrow-fill-class', arrowPadding: 0, offsetSkidding: 0, offsetDistance: 0, matchWidth: false, }, };
@@ -0,0 +1 @@
1
+ <template> <bento-focus-trap v-bind="$attrs" :id="id" ref="popperContainerRef" class="b-popper-container" :data-popper-placement="placement" :disabled="disabledFocusTrap" :style="floatingStyles" :role="ariaRole" :trap-all="trapAll" :trap-all-options="trapAllOptions" > <slot></slot> <span v-if="props.arrow" v-bind="arrowAttributes" ref="floatingArrow" class="b-popper-container__arrow" data-testid="arrow" ></span> </bento-focus-trap> </template> <script setup lang="ts"> import type {} from '@floating-ui/core'; import { computed, type PropType, type Ref, ref, toRefs, watch } from 'vue'; import { arrow as arrowMiddleware, autoPlacement, autoUpdate, flip, // eslint-disable-next-line vue/no-dupe-keys offset, shift, useFloating, } from '@floating-ui/vue'; import { BentoFocusTrap } from '@/components/focus-trap'; import { type FallbackPositionProp, PopperContainerAriaRole, type PopperContainerArrowProp, PopperContainerPosition, type PopperContainerPositionBasic, } from './popper-container.types'; import type { VueNodeElement } from '@/types/elements'; import type { AutoPlacementOptions, Middleware, Placement, Strategy } from '@floating-ui/vue'; import { getFlipModifier, getOffsetModifier, sameWidthMiddleware } from './popper-container.utils'; import type { CSSProperties } from 'vue/types/jsx.d.ts'; import { type Options as FocusTrapOptions } from 'focus-trap'; const props = defineProps({ /** * The aria role assigned to the container. Defaults to null, which means it's not set if nothing is passed. */ ariaRole: { type: String as PropType<PopperContainerAriaRole>, default: null, validator: value => Object.values(PopperContainerAriaRole).includes(value as PopperContainerAriaRole), }, /** * Indicates if container should have additional arrow, pointing at target element * Can be just a boolean flag, or an object with additional configuration: * @property {Array<string>} arrow.classes – apply any additional classes to the arrow element. * @property {string} arrow.padding – useful when content has rounded corners, @see {@link https://floating-ui.com/docs/arrow#padding} */ arrow: { type: Object as PropType<PopperContainerArrowProp | boolean>, default: undefined }, /** * Disables wrapping focus-trap component. * Mostly useful for the storybook demo. */ disabledFocusTrap: { type: Boolean, default: false }, /** * Allows to configure to fallback position to the one specified, it could be 'auto' or ['right', 'top', ...]. * Useful when not using auto as starting placement. * @see {@link https://floating-ui.com/docs/flip#fallbackplacements} */ fallbackPosition: { type: [Array, String] as PropType<FallbackPositionProp>, default: null, }, /** * Instructs popover to be visible outside of the containing container. * Useful when component would be used in limited containers like Modal, Side panel, etc. * @see {@link https://floating-ui.com/docs/usefloating#strategy} */ fixedPositioning: { type: Boolean, default: false }, /** * The id to be set on the component. Use it for accessibility purposes. */ id: { type: String, default: null }, /** * If hiding / showing popper element then use this property to tell the popper when to update it's position. * This will drastically help improve performance, as we will not need to track hidden elements. */ isHidden: { type: Boolean, default: null }, /** * Instructs popper container to have the same width as target element. */ matchWidth: { type: Boolean, default: false }, /** * Custom set of middleware. Useful for low-level control over Popper behavior in some really complex use cases. * @see {@link https://floating-ui.com/docs/middleware} */ middleware: { type: Array as PropType<Array<Middleware>>, default: null }, /** * Allows to displace a popper container from its target element. * @see {@link https://floating-ui.com/docs/offset#options} */ offset: { type: Array as PropType<Array<number>>, default: () => [0, 0], validator: (value: Array<number>) => value?.length === 2, }, /** * Position relative to the target element * @see {@link https://floating-ui.com/docs/computeposition#placement} */ position: { type: String as PropType<PopperContainerPosition | `${PopperContainerPosition}`>, default: PopperContainerPosition.AUTO, validator: value => Object.values(PopperContainerPosition).includes(value as PopperContainerPositionBasic), }, /** * Reference to the DOM element or Vue component for positioning */ targetElement: { type: [Element, Object] as PropType<VueNodeElement>, required: true }, /** * Activates focus trap for both arrow and tab keys. */ trapAll: { type: Boolean, default: false }, /** * Configurations for the focus-trap when trap-all is active. */ trapAllOptions: { type: Object as PropType<FocusTrapOptions>, default: undefined, }, }); const emit = defineEmits<{ /** * Emits new placement of popper container when it's placement is updated. */ (e: 'placement', value: Placement): void; }>(); // Popper / floating ui logic const { position } = toRefs(props); const popperContainerRef = ref(); const floatingArrow = ref(null); const emittedPosition = ref(props.position); const targetElement = computed(() => (props.targetElement as any).$el ? ((props.targetElement as any).$el as Element) : (props.targetElement as Element) ); const popperContainerElement = computed(() => (popperContainerRef.value as any).$el ? ((popperContainerRef.value as any).$el as HTMLElement) : (popperContainerRef.value as HTMLElement) ); const isFlipMiddleware = computed(() => props?.position !== 'auto' && props?.fallbackPosition !== 'auto'); const getAutoPlacementProps = computed<AutoPlacementOptions>(() => { switch (props.position) { case 'auto-start': return { alignment: 'start', crossAxis: true }; case 'auto-end': return { alignment: 'end', crossAxis: true }; default: return undefined; } }); const strategy = computed<Strategy>(() => (props.fixedPositioning ? 'fixed' : 'absolute')); const { floatingStyles, middlewareData, placement, update } = useFloating(targetElement, popperContainerRef, { placement: position as Ref<Placement>, middleware: [ // Adds flip modifier if fallbackPosition is not set to auto ...(isFlipMiddleware.value ? [ flip({ fallbackPlacements: getFlipModifier(props.fallbackPosition).fallbackPlacements, }), ] : []), // Adds autoposition middlelare if position is set to any auto variants ...(!isFlipMiddleware.value ? [autoPlacement(getAutoPlacementProps.value)] : []), // Add shifting middle in case popper is still not fully visible in view port shift(), // Adds offset middlelare if offset property is set ...(props.offset ? [offset(getOffsetModifier(props.arrow, props.offset))] : []), // Adds arrow middlelare if arrow property is set, can set additional padding property ...(props.arrow ? [arrowMiddleware({ element: floatingArrow, padding: props.arrow.padding || 0 })] : []), // Adds same width middleware to keep popper width same as referenced element's width ...(props.matchWidth ? [sameWidthMiddleware] : []), // If there is additional custom middleware to add via the property then add it ...(props.middleware?.length > 0 ? props.middleware : []), ], strategy, // If we're mounting the element with a `v-if` then we need to auto update it while it's mounted ...(props.isHidden === null ? { whileElementsMounted: autoUpdate } : []), }); // If we're hiding the floating element, then we need to manually update once it's visible let cleanupAutoUpdate: undefined | (() => void); watch( () => props.isHidden, () => { if (props.isHidden !== null && !props.isHidden) { cleanupAutoUpdate = autoUpdate(targetElement?.value, popperContainerElement?.value, update); } else if (props.isHidden !== null && props.isHidden && typeof cleanupAutoUpdate === 'function') { cleanupAutoUpdate(); } } ); // Watch placement for changes, if changed then emit new placemenet watch( () => placement.value, () => { if (emittedPosition.value !== placement.value) { emittedPosition.value = placement.value; emit('placement', emittedPosition.value); } } ); // Arrow attributes and styles const arrowStyles = computed<CSSProperties>(() => ({ position: 'absolute', left: middlewareData.value.arrow?.x != null ? `${middlewareData.value.arrow.x}px` : '', top: middlewareData.value.arrow?.y != null ? `${middlewareData.value.arrow.y}px` : '', })); const arrowAttributes = computed(() => ({ class: props.arrow.classes, style: arrowStyles.value, // Applies a data-hide attribute to arrow element depending on center offset. ...(middlewareData.value.arrow?.centerOffset !== 0 ? { 'data-hide': '' } : {}), })); </script> <script lang="ts"> import type {} from '@floating-ui/vue'; /** * A wrapper for the 'popper/core' package that overlays a small piece of content near a target element. * * NOTE: This component should not be used by itself but more implemented by other exported components * e.g. Tooltip * More info here on the package: https://floating-ui.com/docs/vue * * * @usage * import { PopperContainer } from '@adyen/bento-vue2'; * * export default { * components: { PopperContainer }, * computed: { * targetElement() { * return this.$refs.tooltipTarget; * }, * }, * template: ` * <popper-container * :target-element="targetElement" * :position="position" * > * {{ content }} * </popper-container>`, * } */ export default {}; </script> <style lang="scss" scoped src="./popper-container.scss" />
@@ -0,0 +1,94 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as PromoBannerStories from './promo-banner.stories';
3
+
4
+ <Meta of={PromoBannerStories} />
5
+
6
+ # PromoBanner
7
+
8
+ The promotional banner, or promo-banner, is used to highlight important information, announcements, or promotions.
9
+
10
+ <Canvas of={PromoBannerStories.Default} />
11
+
12
+ ## Use Cases
13
+
14
+ Use this component to:
15
+
16
+ - Communicate time-sensitive announcements (e.g., limited-time offers, updates).
17
+ - Highlight key actions or promotions.
18
+
19
+ ### Do not use
20
+
21
+ Avoid using this component to:
22
+
23
+ - Display persistent messages that require ongoing visibility. Consider alternative components like alerts for those.
24
+
25
+ ## Variations
26
+
27
+ - `horizontal` (default) - Ideal for large spaces. Changes to vertical layout in smaller containers depending on the
28
+ image's aspect-ratio.
29
+ - `vertical` - Suitable for smaller spaces, such as half a page or side panels. Does not change with container size.
30
+
31
+ ## Modifiers
32
+
33
+ ### Actions
34
+
35
+ The promotional banner incorporates the `BentoButton` component.
36
+
37
+ We support only `primary` or `secondary` button variants. They are optional.
38
+
39
+ ### Color
40
+
41
+ We have three available colors - `green`, `grey` and `blue`.
42
+
43
+ ### Image
44
+
45
+ The promotional banner incorporates the `BentoImage` component.
46
+
47
+ It only allows aspect-ratio of `16:9`, `2:1` and `1:1`. This is based on the ratio that brand uses. To change the
48
+ illustration/image go the brand website to download a `png` of the illustration and set the `image.src` property to the
49
+ downloaded image path. Choose a format based on the illustration and the width of your banner.
50
+
51
+ Depending on the image's `aspect-ratio`, the promotional banner respond at different containers sizes. Refer to
52
+ [here](#responsive) for more information.
53
+
54
+ ### Dismissible
55
+
56
+ Emits an event that allows users to remove the banner if it is not critical. This is set to `true` by default.
57
+
58
+ ## Accessibility
59
+
60
+ ### Keyboard interaction
61
+
62
+ | Keyboard interaction | Output |
63
+ | -------------------- | ------------------------------------------------------ |
64
+ | `Tab` | Focus visibly moves to the first button on the banner. |
65
+ | `Enter` | Selects the button. |
66
+ | `Space` | Selects the button. |
67
+
68
+ ### Roles, states and properties
69
+
70
+ | ARIA attribute | is automatic? | fallback used | manual override |
71
+ | ----------------- | ------------------------------ | ------------- | --------------- |
72
+ | `role` | Yes, has the `region` value. | - | - |
73
+ | `aria-labelledby` | Yes, it uses the `title` text. | - | - |
74
+
75
+ ### Responsive
76
+
77
+ The promotional banner transitions between horizontal and vertical layouts based on container width. Additionally,
78
+ illustration aspect ratios adapt dynamically at specific breakpoints to maintain optimal visual balance.
79
+
80
+ - ≤ 850px → Landscape (2:1) shifts to vertical layout.
81
+ - ≤ 800px → Landscape (16:9) shifts to vertical layout.
82
+ - ≤ 623px → Square (1:1) shifts to vertical layout.
83
+
84
+ If the promotional banner is given `layout="vertical"`, it will not be responsive i.e. always remain vertical regardless
85
+ of container width.
86
+
87
+ > _Note_: The responsiveness of the promotional banner is not determined by the screen size but is instead based on the
88
+ > size of the container in which it is placed. It is important to note that while screen size can affect layout
89
+ > elements, the banner's responsiveness specifically adapts to the dimensions of its containing element, ensuring
90
+ > flexibility within that context.
91
+
92
+ ## Resources
93
+
94
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=38849-7952)
@@ -0,0 +1 @@
1
+ import { storybookDocsParameter } from '@/utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { BentoImageAspectRatio } from '../image'; import { BentoTypography } from '../typography'; import { BentoPromoBannerColor, BentoPromoBannerLayout } from './promo-banner.types'; import BentoPromoBanner from './promo-banner.vue'; const meta: Meta = { title: 'Promo banner', component: BentoPromoBanner, argTypes: { // Slots description: { name: 'description', description: "Provides supplementary information that reinforces the banner's title", table: { type: { summary: 'VNode[]' }, category: 'slots', }, control: { type: 'text', }, }, color: { options: Object.values(BentoPromoBannerColor), mapping: BentoPromoBannerColor, control: { type: 'select', }, }, layout: { options: Object.values(BentoPromoBannerLayout), mapping: BentoPromoBannerLayout, defaultValue: BentoPromoBannerLayout.HORIZONTAL, control: { type: 'select', }, }, // Events dismiss: { table: { type: { summary: '() => void' }, category: 'events', }, control: { disable: true }, }, }, }; export default meta; type Story = StoryObj<typeof BentoPromoBanner>; const DEFAULT_TITLE = 'Title'; const DEFAULT_DESCRIPTION = 'Brief description explaining and giving more information about the title'; const DEFAULT_PROPS = { actions: [ { title: 'Primary', event: () => action('onClick'), }, { title: 'Secondary', event: () => action('onClick'), variant: 'secondary', }, ], color: BentoPromoBannerColor.GREY, description: DEFAULT_DESCRIPTION, image: { src: './16-9.png', alt: '', aspectRatio: BentoImageAspectRatio.P16_9, }, isDismissible: true, layout: 'horizontal', title: DEFAULT_TITLE, }; const defaultCode = ` <bento-promo-banner :actions="[ { title: 'Primary', event: () => action('onClick'), }, { title: 'Secondary', event: () => action('onClick'), variant: 'secondary', } " :image="{ src: './16-9.png', alt: '', aspectRatio: '16:9', }" :title="${DEFAULT_TITLE}" @dismiss="dismissAction" > <template #description> <bento-typography variant="body"> ${DEFAULT_DESCRIPTION} </bento-typography> </template> </bento-promo-banner> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoPromoBanner, BentoTypography }, props: Object.keys(argTypes), template: ` <bento-promo-banner v-bind="args" @dismiss="dismissAction"> <template #description> <bento-typography variant="body"> ${DEFAULT_DESCRIPTION} </bento-typography> </template> </bento-promo-banner> `, setup(props) { return { // Values args: isVue2 ? props : _args, // Events dismissAction: action('dismiss'), }; }, }), args: DEFAULT_PROPS, parameters: storybookDocsParameter(defaultCode), };
@@ -0,0 +1 @@
1
+ <template> <section ref="promoBannerRef" class="b-promo-banner" :class="bannerConditionalClass" :aria-labelledby="promoBannerTitleId" > <div class="b-promo-banner__content"> <div class="b-promo-banner__content-header"> <bento-typography :id="promoBannerTitleId" variant="title" large> {{ title }} </bento-typography> <slot name="description" /> </div> <div class="b-promo-banner__content-actions"> <template v-if="actions"> <bento-button v-for="(button, index) in actions" :key="index" :variant="button.variant" :critical="button.critical" :disabled="button.disabled" :inverse="button.inverse" :icon-only="button.iconOnly" :link="button.link" @click="button.event" > <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> <bento-button v-if="isDismissible" variant="secondary" @click="emit('dismiss')"> {{ computedDismissButtonText }} </bento-button> </div> </div> <div class="b-promo-banner__image"> <bento-image class="b-promo-banner__image-item" :src="image.src" :alt="image.alt ?? ''" :aspect-ratio="image.aspectRatio" :object-fit="BentoImageObjectFit.CONTAIN" ></bento-image> </div> </section> </template> <script setup lang="ts"> import { useElementSize } from '@vueuse/core'; import { computed, ref } from 'vue'; // Components import { BentoButton, type BentoButtonActionObject } from '@/components/button'; import { BentoImage, BentoImageAspectRatio, BentoImageObjectFit } from '@/components/image'; import { BentoTypography } from '@/components/typography'; import { BentoPromoBannerColor, BentoPromoBannerLayout, type BentoPromoBannerProps } from './promo-banner.types'; // Utils import { useI18n } from '@/utils/ts/i18n'; import { generateUid } from '@/core/utils/ts'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = withDefaults(defineProps<BentoPromoBannerProps>(), { actions: null, color: BentoPromoBannerColor.GREY, isDismissible: true, dismissButtonText: null, layout: BentoPromoBannerLayout.HORIZONTAL, }); const emit = defineEmits<{ /** * Emits a dismiss event when dismiss button on the promotional banner is pressed */ (e: 'dismiss'); }>(); const promoBannerRef = ref(null); const promoBannerSize = useElementSize(promoBannerRef); const promoBannerTitleId = generateUid('promoBannerTitle'); const horizontalLayoutBreakpoint = computed(() => { if (props.image.aspectRatio === BentoImageAspectRatio.P16_9) { return 800; } else if (props.image.aspectRatio === BentoImageAspectRatio.P2_1) { return 850; } else if (props.image.aspectRatio === BentoImageAspectRatio.P1_1) { return 623; } return 0; }); const bannerLayoutClass = computed(() => { // only horizontal layout is responsive and, by design, follows the container size if ( props.layout === BentoPromoBannerLayout.HORIZONTAL && promoBannerSize.width.value <= horizontalLayoutBreakpoint.value ) { return BentoPromoBannerLayout.VERTICAL; } return props.layout; }); const bannerConditionalClass = computed(() => [`b-promo-banner--${bannerLayoutClass.value}`, `b-promo-banner--${props.color}`].join(' ') ); const isIconOnly = (button: BentoButtonActionObject) => button.iconOnly && (button.iconLeft || button.iconRight || button.icon); const computedDismissButtonText = computed(() => props.dismissButtonText || t('dismiss')); </script> <script lang="ts"> /** * Promotional Banner highlights important information, announcements, or promotions. * * @example * import { BentoPromoBanner } from '@adyen/bento-vue2'; * * export default { * components: { BentoPromoBanner }, * template: ` * <bento-promo-banner * title="Title" * description="Brief description explaining and giving more information about the title" * :image="{ src: '16-1.png', aspectRatio: '16:9' }" * :actions="[{ title: 'Primary', event: () => {} }]" * > * </bento-promo-banner> * ` * }; */ export default { i18n: { messages }, name: 'bento-promo-banner', }; </script> <style lang="scss" scoped src="./promo-banner.scss" />
@@ -0,0 +1,76 @@
1
+ import { Canvas, Meta, Story } from '@storybook/blocks';
2
+ import * as RadioButtonStories from './radio-button.stories';
3
+
4
+ <Meta title="Radio button" of={RadioButtonStories} />
5
+
6
+ # Radio button
7
+
8
+ Radio buttons are used when two or more options are mutually exclusive. The user can only select a single option from a
9
+ list of pre-defined options.
10
+
11
+ <Canvas of={RadioButtonStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ **Only** use this component when:
16
+
17
+ - Creating a new component that is not provided by Bento
18
+
19
+ ### Do not use
20
+
21
+ Don't use a radio button in the following cases:
22
+
23
+ - Multiple choices: Use the `bento-radio-group` as it is built with accessibility in mind.
24
+ - Building a form: Use `bento-radio-group`, `bento-checkbox` or `bento-toggle`.
25
+
26
+ ### Guidelines
27
+
28
+ When using the `bento-radio-button` component, it is advised:
29
+
30
+ - Accessibility (a11y) must always come first.
31
+ - Create a group with all the individual `bento-radio-button` using a `fieldset` with a `legend` inside for
32
+ accessibility.
33
+ - Follow
34
+ [Bento guidelines](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=25221-4758).
35
+ - When creating a group the following accessibility keyboard interactions **must** be supported according to the
36
+ official [W3 aria specifications](https://www.w3.org/WAI/ARIA/apg/patterns/radio/examples/radio-rating/#kbd_label)
37
+ - `Tab`: Moves focus to the radio-button or away from it.
38
+ - `Space`: When focus is on the radio-button, selects the value.
39
+ - `Up`: When focus is on the radio-button, selects the next value.
40
+ - `Left`: When focus is on the radio-button, selects the next value.
41
+ - `Down`: When focus is on the radio-button, selects the previous value.
42
+ - `Right`: When focus is on the radio-button, selects the previous value.
43
+
44
+ ## Variations
45
+
46
+ There are no variations available for the Radio button component
47
+
48
+ ## Modifiers
49
+
50
+ ### Disabled
51
+
52
+ Renders the radio button in a disabled state. In practice this makes the component non-interactive and renders a
53
+ `not-allowed` cursor on hover.
54
+
55
+ ### Adding tags to items
56
+
57
+ If you would like to grab a user's attention or guide them in their choice on which item to select then you may want to
58
+ use `tag` property. This property accepts all properties a `BentoTag` accepts.
59
+
60
+ ## Accessibility
61
+
62
+ The `label` and `description` areas are clickable. A click on these elements selects the radio input value.
63
+
64
+ Screen readers convey the state of the radio button automatically.
65
+
66
+ ### Roles, states and properties
67
+
68
+ | ARIA attribute | is automatic? | fallback used | manual override |
69
+ | -------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------- |
70
+ | `aria-label` | Yes, corresponds to the `default` slot text | If no text is passed to the `default` slot, then the `value` property will be used. | - |
71
+ | `role` | Yes, through the semantic `input[type="radio"]` element with `role="radio"` set. | - | - |
72
+ | `aria-checked` | Yes, set when the radio button gets selected through the `input`'s `checked` property. | - | - |
73
+
74
+ ## Resources
75
+
76
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/ADL-2.0---Components?node-id=211-4397&t=MzNjIT4cgO21YCtH-0)
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { ref } from 'vue'; import { isVue2 } from 'vue-demi'; import type { Meta, StoryObj } from '@storybook/vue'; import { BentoRadioButton } from './index'; const meta: Meta = { title: 'Radio group/Radio button', component: BentoRadioButton, argTypes: { // Props hasError: { name: 'has-error', }, modelValue: { name: 'model-value', control: { disable: true }, }, // Slots content: { type: { name: 'string', required: false }, description: 'Conditional content slot that is shown when the radio button is checked', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, default: { type: { name: 'string', required: false }, description: 'The default content slot', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, description: { type: { name: 'string', required: false }, description: 'Description slot', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoRadioButton>; const LABEL = 'Label'; const DESCRIPTION = 'Optional description'; const defaultCode = ` <template> <bento-radio-button :value="value" v-model="modelValue" @update:model-value="onInput" > Label <template #description>Description text</template> <template #content>Conditional content (optional)</template> </bento-radio-button> </template> <script setup lang="ts"> import { BentoButtonRadioButton, type BentoRadioButtonValue } from '@adyen/bento-vue2' const value = 'option-value-id' const modelValue = ref<BentoRadioButtonValue>(null) const onInput = (value: BentoRadioButtonValue) => {} </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ props: Object.keys(argTypes), components: { BentoRadioButton }, template: ` <bento-radio-button v-bind="args" :model-value.sync="modelValue" @input="input" > {{args.default}} <template #description>{{args.description}}</template> <template #content>{{args.content}}</template> </bento-radio-button> `, setup(props) { const modelValue = ref('any value'); return { // Values args: isVue2 ? props : _args, modelValue, // Methods update: action('update:model-value'), input: action('input'), }; }, }), args: { default: LABEL, description: DESCRIPTION, value: 'option', content: 'Conditional content', }, parameters: storybookDocsParameter(defaultCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-radio-button" data-testid="radio-button-container" :class="conditionalClassContainer"> <radio-input :id="radioButtonId" ref="radioInputRef" :aria-label="ariaLabel" :aria-describedby="hasSlot('description') ? descriptionId : ''" :checked="isChecked" :disabled="disabled" :readonly="readonly" :has-error="hasError" :value="value" :model-value="modelValue" :name="name" @update:model-value="onChange" /> <div> <label v-if="hasSlot('default')" :for="radioButtonId" class="b-radio-button__label-container"> <span v-if="hasSlot('default')" class="b-radio-button__primary-label"> <bento-typography class="b-radio-button__label" el="span" variant="body"><slot /></bento-typography> <bento-tag v-if="tag" class="b-radio-button__label-tag" v-bind="tag" /> </span> <bento-typography v-if="hasSlot('default') && hasSlot('description')" :id="descriptionId" class="b-radio-button__description" el="span" variant="body" > <slot name="description"></slot> </bento-typography> </label> <bento-typography v-if="hasSlot('content') && isChecked" class="b-radio-button__content" el="div"> <slot name="content"></slot> </bento-typography> </div> </div> </template> <script setup lang="ts"> import { BentoTag } from '@/components/tag'; import { BentoTypography } from '@/components/typography'; import { RadioInput } from '../../../internal/radio-input'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { generateUid } from '@/core/utils/ts'; import { computed, type ComputedRef, type PropType, ref, toRefs, useSlots } from 'vue'; import { useDefaultSlotValidator, useHasSlot } from '@/composables'; import { BentoRadioButtonStateClass, type BentoRadioButtonTag, type BentoRadioButtonValue } from './radio-button.types'; import { getSlotText } from '@/utils/ts/get-slot-text'; const slots = useSlots(); const hasSlot = useHasSlot(slots); const props = defineProps({ /** * Disable the radio button (optional) */ disabled: { type: Boolean, default: false }, /** * Shows bento-radio-button in error state (optional) */ hasError: { type: Boolean, default: false }, /** * This property should not be used directly. * It will be used by v-model directive on radio component. */ modelValue: { type: [String, Number] as PropType<BentoRadioButtonValue>, default: null, }, /** * Sets the radio button to readonly (optional) */ readonly: { 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<BentoRadioButtonTag>, default: undefined }, /** * Indicates the value of the input that will be returned when it's checked */ value: { type: [String, Number] as PropType<BentoRadioButtonValue>, required: true, }, /** * Sets the name of the radio input. * Used for differentiating radio groups or accessing input via form submission. */ name: { type: String, default: null, }, }); const emit = defineEmits<{ /** * Emitted when the radio button is clicked. Enables Vue2 "v-model" */ (e: 'input', value: BentoRadioButtonValue); /** * Emitted when the radio button is clicked. Enables Vue3 "v-model" */ (e: 'update:model-value', value: BentoRadioButtonValue); }>(); const { value } = toRefs(props); const radioButtonId = generateUid('bento-radio-button'); const descriptionId = generateUid('description'); const radioInputRef = ref(null); useDefaultSlotValidator('bento-radio-button', slots.default, slots.description); const conditionalClassContainer = computed(() => ({ [`b-radio-button--${BentoRadioButtonStateClass.DISABLED}`]: props.disabled, [`b-radio-button--${BentoRadioButtonStateClass.READONLY}`]: props.readonly, })); const ariaLabel = useAriaLabel({ ariaLabel: getSlotText(slots)('default'), label: value.value as string, }); const isChecked: ComputedRef<boolean> = computed(() => props.value === props.modelValue); const onChange = () => { if (!props.disabled && !props.readonly) { emit('update:model-value', props.value); emit('input', props.value); } }; const focus = () => { radioInputRef.value.focus(); }; defineExpose({ focus, }); </script> <script lang="ts"> /** * Radio buttons are used when two or more options are mutually exclusive, * the user can only select a single option from a list of pre-defined options. * The label tag area is also clickable. * Only to be used internally. * * @example * import { BentoRadioButton } from '@adyen/bento-vue2'; * * export default { * components: { BentoRadioButton }, * template: ` * <bento-radio-button * disabled * value="inputRadio" * model-value.sync="modelValue" * > * Label text * <template #description>Description text below label</template> * <template #content>Content shown when checked</template> * </bento-radio-button> * `, * setup() { * const modelValue = ref('anyValue') * const value = 'option-value' * * return { * modelValue, * value, * } * } * } */ export default { name: 'bento-radio-button', model: { prop: 'modelValue' }, }; </script> <style lang="scss" scoped src="./radio-button.scss" />
@@ -0,0 +1,154 @@
1
+ import { Canvas, Story, Source, Meta } from '@storybook/blocks';
2
+ import * as RadioGroupStories from './radio-group.stories';
3
+
4
+ <Meta of={RadioGroupStories} />
5
+
6
+ # Radio group
7
+
8
+ Radio buttons are used when two or more options are mutually exclusive. The user can only select a single option from a
9
+ list of pre-defined options.
10
+
11
+ <Canvas of={RadioGroupStories.Default} />
12
+
13
+ > IMPORTANT
14
+ >
15
+ > Labels without a description are hidden by default. You must set a `label` and a `description` (via a prop or slot) to
16
+ > display both. If only a `label` (without `description`) is meant to be displayed, then set `hideLabel` to `false`. See
17
+ > more details in [hide label](#hide-label).
18
+
19
+ ## Use Cases
20
+
21
+ Here are some examples:
22
+
23
+ - Choosing a subscription plan for a service, when the user can select only one plan from a list of available plans.
24
+ - Payment Methods selection from various options, such as credit card, PayPal, or bank transfer.
25
+
26
+ ### Do not use
27
+
28
+ Don't use a radio button group in the following cases:
29
+
30
+ - Multiple selections - the `radio-group` component is designed for selecting a single option from a group of mutually
31
+ exclusive choices. If you need to allow users to select multiple options simultaneously, consider using a
32
+ `checkbox-group` instead.
33
+ - If you have a large amount of options (e.g. more than four), then consider using a dropdown as it takes up less screen
34
+ space.
35
+
36
+ ### Guidelines
37
+
38
+ When using the `radio-group` component, it is advised to:
39
+
40
+ - Align the choices vertically when possible as it is easier to scan and localize. If you still need to display them
41
+ horizontally, make sure you have enough space between the options so that it’s absolutely clear which choice goes with
42
+ which label.
43
+ - Make sure the options are both comprehensive and clearly distinctive from each other.
44
+
45
+ ## Variations
46
+
47
+ ### Variant
48
+
49
+ The `variant` property defines the layout in which the radio buttons are displayed. It can be one of the following:
50
+
51
+ - `'vertical'`: displays the radio buttons stacked on top of each other. It's the default value.
52
+ - `'horizontal'`: displays the radio buttons one next to the other. If not enough horizontal space is present, the items
53
+ that don't fit overflow on a new line.
54
+
55
+ ## Modifiers
56
+
57
+ ### Disabled
58
+
59
+ Renders the `radio-group` in a disabled state. In practice this makes the component non-interactive and renders a
60
+ `not-allowed` cursor on hover.
61
+
62
+ ### Readonly
63
+
64
+ Renders the `radio-group` in a readonly state. In terms of functionality, read only states are focusable and submittable
65
+ in a form. They're considered to be 'active' from an application's point of view.
66
+
67
+ ### Tooltip
68
+
69
+ A tooltip with extra information for the field can be rendered by passing the desired information to the `tooltipText`
70
+ prop.
71
+
72
+ ### Error
73
+
74
+ There are two properties that control the error state of all rendered `bento-radio-button`'s within `bento-radio-group`:
75
+
76
+ - `errorMessage`: renders all radio buttons with an error state and displays an error message beneath the buttons
77
+ - `hasError`: renders all radio buttons with an error state
78
+
79
+ `hasError` is deprecated and will be removed in the near future, so please use `errorMessage`.
80
+
81
+ ### Hide Label
82
+
83
+ The `label` is not displayed by default in Bento `1.x` if passed without a `description` property set at the same time.
84
+
85
+ To display a `label` without a `description` set, the property `hideLabel` must be set to `false` (`true` by default).
86
+
87
+ Displaying a `label` (without a `description`) will be the default in Bento `2.x` and the property `hideLabel` will be
88
+ removed.
89
+
90
+ <Source
91
+ dark
92
+ language="html"
93
+ code={`<bento-radio-group
94
+ :items="[
95
+ { value: 1, label: 'Label text' },
96
+ { value: 2, label: 'Label text 2', description: 'Description text' },
97
+ ]"
98
+ label="Group label"
99
+ hide-label="false"
100
+ />`}
101
+ />
102
+
103
+ ### Custom slots
104
+
105
+ The `RadioGroup` component offers a `description` slot to add a description to the group, and a `content` slot for the
106
+ vertical layout, which will only display the content for the selected radio option.
107
+
108
+ <Canvas sourceState="hidden">
109
+ <Story of={RadioGroupStories.Slots} />
110
+ </Canvas>
111
+
112
+ ## Adding tags to items
113
+
114
+ If you would like to grab a user's attention or guide them in their choice on which item to select then you may want to
115
+ use `tag` property on each `BentoRadioGroupItem`. This property accepts all properties a `BentoTag` accepts.
116
+
117
+ <Canvas of={RadioGroupStories.WithTag} />
118
+
119
+ ## Accessibility
120
+
121
+ The `bento-radio-group` component has been implemented using WAI-ARIA's radio group pattern and grouping controls
122
+ pattern.
123
+
124
+ The radio button itself is built using a semantically accessible structure which leverages the native `input` tag with a
125
+ role of `radio` and which is associated to its corresponding `label`. Both the `label` and `description` areas of each
126
+ radio button are clickable. A click on these elements selects the corresponding radio input value. Since both the the
127
+ radio button and the group are built using semantic elements, screen readers convey the state and grouping of these
128
+ components automatically.
129
+
130
+ ### Keyboard interaction
131
+
132
+ Since the `bento-radio-group` component is implemented via a `fieldset` tag, it is reachable through keyboard navigation
133
+ and follows the logical navigation flow of the page. A detailed list of keyboard interactions is provided in the table
134
+ below.
135
+
136
+ | Keyboard interaction | Output |
137
+ | -------------------- | -------------------------------------------------------------------------------------------- |
138
+ | `Tab` | Moves focus to the first radio button in the group or away from the group. |
139
+ | `Arrow keys` | Moves focus to the different radio buttons in the group and selects the corresponding value. |
140
+
141
+ ### Roles, states and properties
142
+
143
+ | ARIA attribute | is automatic? | fallback used | manual override |
144
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
145
+ | `role` | Yes. In the group, through the semantic `fieldset` element, which groups all its child elements. In the buttons, through the `input` tag with `[role="radio"]` set. | - | - |
146
+ | `label` | Yes. In the group it's set via the `label` prop, whereas in the buttons it's set via the `slot` content. | In each radio button, the radio's value gets set in the unlikely scenario no slot is present. | Yes, the consumer is required to set both values. |
147
+ | `form` | No, it needs to be manually set via the `form` property. | - | The user can pass the form id with which the group needs to be associated with. This allows to use such component also externally from the form and have the value submitted along with the form. |
148
+ | `disabled` | No, it needs to be set by setting the `disabled` prop to `true`. | - | If set to reu, it disables all the elements contained in the group. |
149
+
150
+ ## Resources
151
+
152
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/ADL-2.0---Components?node-id=211-4397&t=MzNjIT4cgO21YCtH-0)
153
+ - [WAI-ARIA guidelines for grouping controls](https://www.w3.org/WAI/tutorials/forms/grouping/)
154
+ - [WAI-ARIA radio group pattern](https://www.w3.org/WAI/ARIA/apg/patterns/radio/)
@@ -0,0 +1 @@
1
+ import BentoRadioGroup from './radio-group.vue'; import { BentoInputField } from '@/components/input-field'; import { BentoRadioGroupVariant } from './radio-group.types'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { ref } from 'vue'; import { isVue2 } from 'vue-demi'; import RadioGroupSlotsExample from './__tests__/radio-group-slots-example.vue?raw'; import type { Meta, StoryObj } from '@storybook/vue'; const meta: Meta = { title: 'Radio Group', component: BentoRadioGroup, argTypes: { variant: { options: Object.values(BentoRadioGroupVariant), mapping: BentoRadioGroupVariant, control: { type: 'select', }, }, items: { table: { type: { summary: 'BentoRadioGroupItems' }, }, }, disabled: { control: { type: 'boolean', }, }, hasError: { control: { type: 'boolean' }, table: { category: 'Deprecated' }, }, 'update:model-value': { name: 'update:model-value', defaultValue: '', description: 'Input event handler.', table: { type: { summary: '(newValue: number | string | null) => void' }, }, control: { disable: true }, }, input: { description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value` instead', table: { category: 'Deprecated', }, control: { disable: true }, }, }, }; export default meta; type Story = StoryObj<typeof BentoRadioGroup>; const RADIO_DEFAULT_LABEL = 'Label text'; const DEFAULT_PROPS = { label: 'Group label', description: 'Optional description', tooltipText: 'Tooltip text', items: [ { value: 1, label: RADIO_DEFAULT_LABEL }, { value: 2, label: `${RADIO_DEFAULT_LABEL} 2`, description: 'Descriptive sub label' }, { value: 3, label: `${RADIO_DEFAULT_LABEL} readonly`, readonly: true }, ], }; const defaultCode = ` <template> <!-- With model-value --> <bento-radio-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Description text' }, ]" :model-value="yourModelValueVariable" @update:model-value="yourUpdateHandler" label="Group label" description="Optional description" variant="horizontal" /> <!-- With v-model Vue2/Vue3 --> <bento-radio-group :items="[ { value: 1, label: 'Label text' }, { value: 2, label: 'Label text 2', description: 'Description text' }, ]" v-model="yourModelValueVariable" label="Group label" description="Optional description" variant="horizontal" /> </template> <script setup lang="ts"> import { BentoRadioGroup } from '@adyen/bento-vue2'; const yourModelValueVariable = ref<number>(1); const yourUpdateHandler = (newValue: number) => { yourModelValueVariable.value = newValue; } </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoRadioGroup }, props: Object.keys(argTypes), template: ` <bento-radio-group v-bind="args" :model-value="selectedValue" @update:model-value="clickedAction" /> `, setup(props) { const selectedValue = ref(undefined); const clickedAction = value => { selectedValue.value = value; action('update:model-value')(value); }; return { // Values selectedValue, args: isVue2 ? props : _args, // Events clickedAction, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(defaultCode), }; export const Slots: Story = { render: (_args, { argTypes }) => ({ components: { BentoRadioGroup, BentoInputField }, props: Object.keys(argTypes), template: ` <bento-radio-group v-bind="args" :model-value="selectedValue" @update:model-value="clickedAction" > <template #description> Description slot text. </template> <template #content> <bento-input-field /> </template> </bento-radio-group> `, setup(props) { const selectedValue = ref(undefined); const clickedAction = value => { selectedValue.value = value; action('update:model-value')(value); }; return { // Values selectedValue, args: isVue2 ? props : _args, // Events clickedAction, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(RadioGroupSlotsExample), }; const withTagCode = ` <template> <!-- With model-value --> <bento-radio-group :items="[ { value: 1, label: 'Label text', tag: { label: 'Recommended' } }, { value: 2, label: 'Label text 2', description: 'Description text' }, ]" :model-value="yourModelValueVariable" @update:model-value="yourUpdateHandler" label="Group label" description="Optional description" variant="horizontal" /> <!-- With v-model Vue2/Vue3 --> <bento-radio-group :items="[ { value: 1, label: 'Label text', tag: { label: 'Recommended' } }, { value: 2, label: 'Label text 2', description: 'Description text' }, ]" v-model="yourModelValueVariable" label="Group label" description="Optional description" variant="horizontal" /> </template> <script setup lang="ts"> import { BentoRadioGroup } from '@adyen/bento-vue2'; const yourModelValueVariable = ref<number>(1); const yourUpdateHandler = (newValue: number) => { yourModelValueVariable.value = newValue; } </script> `; export const WithTag = { ...Default, args: { ...Default.args, items: [ { value: 1, label: RADIO_DEFAULT_LABEL, tag: { label: 'Recommended' } }, { value: 2, label: `${RADIO_DEFAULT_LABEL} 2`, description: 'Descriptive sub label', }, { value: 3, label: `${RADIO_DEFAULT_LABEL} readonly`, readonly: true }, ], }, parameters: storybookDocsParameter(withTagCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-radio-group"> <controls-group class="b-radio-group__buttons" :aria-describedby="shouldShowErrorMessage ? errorId : ''" :component-name="COMPONENT_NAME" :class="conditionalClasses" :description="description" :disabled="disabled" :form="formId" :label="label" :optional="optional" :required="required" :hide-label="hideLabel" :tooltip-text="tooltipText" role="radiogroup" :aria-readonly="isReadOnly" > <bento-radio-button v-for="item in items" ref="radioButton" :key="item.value" :model-value="inputValue" :value="item.value" :disabled="disabled || item.disabled" :readonly="isReadOnly || item.readonly" :has-error="shouldShowError || item.hasError" :tag="item.tag" :name="formId || label" @update:model-value="onUpdateModelValue" > <template #default>{{ item.label }}</template> <template v-if="item.description" #description>{{ item.description }}</template> <template v-if="hasSlot('content') && isVertical" #content> <slot name="content"></slot> </template> </bento-radio-button> <template v-if="hasSlot('description')" #description> <slot name="description" /> </template> </controls-group> <error-message v-if="shouldShowErrorMessage" :id="errorId" :error-message="errorMessage" class="b-radio-group__error-message" /> </div> </template> <script setup lang="ts"> import { computed, ref, toRef, useSlots, watch } from 'vue'; import { ControlsGroup, ErrorMessage } from '@/components/internal'; import { BentoRadioButton } from './components/radio-button'; import { useHasSlot } from '@/composables'; import { generateUid } from '@/core/utils/ts'; import { deprecate } from '@/utils/ts/deprecate'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; import { type BentoRadioGroupEmits, type BentoRadioGroupProps, BentoRadioGroupVariant } from './radio-group.types'; import { useFormLayoutFieldLoading } from '@/composables/use-form-layout-loading/use-form-layout-loading'; /** * The name of the component to be used in the Vue devtools and console warnings. */ const COMPONENT_NAME = 'bento-radio-group'; const slots = useSlots(); const hasSlot = useHasSlot(slots); const emit = defineEmits<BentoRadioGroupEmits>(); const props = withDefaults(defineProps<BentoRadioGroupProps>(), { description: null, disabled: false, errorMessage: null, formId: null, hasError: false, hideLabel: null, modelValue: null, optional: false, readonly: false, required: false, tooltipText: null, variant: BentoRadioGroupVariant.VERTICAL, }); const inputValue = toRef(props, 'modelValue'); const errorId = generateUid('error'); const { isReadOnly } = useFormLayoutFieldLoading(toRef(props, 'readonly')); const { emitValue } = useFormFieldEmits(emit); const radioButton = ref(null); const shouldShowError = computed( () => !props.disabled && !isReadOnly.value && (!!props.errorMessage || props.hasError) ); const shouldShowErrorMessage = computed(() => !props.disabled && !isReadOnly.value && !!props.errorMessage); const conditionalClasses = computed(() => ({ 'b-radio-group__buttons--horizontal': props.variant === BentoRadioGroupVariant.HORIZONTAL, })); // Content slot logic const isVertical = computed(() => props.variant === 'vertical'); const shouldShowSlotError = computed(() => hasSlot('content') && !isVertical?.value); const onUpdateModelValue = value => { emitValue(value); }; if (props.hasError) { deprecate( 'BentoRadioGroup "hasError" property', `Use the BentoRadioGroup "errorMessage" property instead to indicate that there is a modifier error. Having the field as "undefined" will hide/remove the error. <bento-radio-group errorMessage="Error message text" />`, '2.0.0' ); } watch( shouldShowSlotError, newVal => { if (newVal) { throw new Error('The content slot is only available for the vertical radio group layout'); } }, { immediate: true, } ); const focus = () => { radioButton.value[0].focus(); }; defineExpose({ focus, }); </script> <script lang="ts"> /** * A radio group component should be used whenever it is necessary to use a set of radio buttons * This component conforms to the official WAI-ARIA guidelines for grouping controls: https://www.w3.org/WAI/tutorials/forms/grouping/ * * import { BentoRadioGroup } from '@adyen/bento-vue2/'; * * export default { * components: { BentoRadioGroup }, * template: ` * <bento-radio-group * label="Your group label" * :items="[ * { value: 1, label: 'Your label' }, * { value: 2, label: 'Another label', description: 'Your descriptive sub label' }, * ]" * variant="horizontal" * /> * ` * }; */ export default { name: 'bento-radio-group', model: { prop: 'modelValue' }, }; </script> <style lang="scss" scoped src="./radio-group.scss" />
@@ -0,0 +1 @@
1
+ <template> <div ref="editableRef" contenteditable="true" tabindex="0" :disabled="disabled" class="b-rich-text-editor-content-area" @input="onInput" @keydown="preserveCursor" > </div> </template> <script setup lang="ts"> import { onMounted, ref, watch } from 'vue'; import { type BentoRichTextEditorContentAreaProps } from './rich-text-editor-content-area.types'; const props = withDefaults(defineProps<BentoRichTextEditorContentAreaProps>(), { disabled: false, value: null, }); const emit = defineEmits<{ (e: 'input', value: BentoRichTextEditorContentAreaProps['value']): void; }>(); const editableRef = ref<HTMLDivElement>(null); const onInput = (event: InputEvent) => { emit('input', (event.target as HTMLElement).innerText); }; const preserveCursor = () => { // Do nothing here. This prevents unnecessary handling that can disturb user input }; // Update the contenteditable div after the component is mounted onMounted(() => { if (editableRef.value && editableRef.value.innerText !== props.value) { editableRef.value.innerText = props.value; // Initial content sync } }); watch( () => props.value, newVal => { if (editableRef.value && editableRef.value.innerText !== newVal) { editableRef.value.innerText = newVal; } } ); </script> <style lang="scss" scoped src="./rich-text-editor-content-area.scss" />
@@ -0,0 +1 @@
1
+ <template> <pre>{{ code }}</pre> </template> <script setup lang="ts"> import { type RichTextEditorCodeBlockProps } from './rich-text-editor-code-block.types'; defineProps<RichTextEditorCodeBlockProps>(); </script>
@@ -0,0 +1 @@
1
+ <template> <code>{{ code }}</code> </template> <script setup lang="ts"> import { type RichTextEditorInlineCodeProps } from './rich-text-editor-inline-code.types'; defineProps<RichTextEditorInlineCodeProps>(); </script>
@@ -0,0 +1 @@
1
+ <template> <bento-link :to="url" external is-not-routing> <template #default> <slot></slot> </template> </bento-link> </template> <script setup lang="ts"> import { BentoLink } from '@/components/link'; import { type RichTextEditorLinkProps } from './rich-text-editor-link.types'; withDefaults(defineProps<RichTextEditorLinkProps>(), { url: '', }); </script>
@@ -0,0 +1 @@
1
+ <template> <li :style="noBullet"> <bento-checkbox v-if="isTaskList" v-model="isChecked"> <slot></slot> </bento-checkbox> <div v-else> <slot></slot> </div> </li> </template> <script setup lang="ts"> import { computed, ref } from 'vue'; import BentoCheckbox from '@/components/checkbox/checkbox.vue'; import { type RichTextEditorListItemProps } from './rich-text-editor-list-item.types'; const props = withDefaults(defineProps<RichTextEditorListItemProps>(), { checked: null, }); const isChecked = ref(props.checked); const isTaskList = computed(() => props.checked !== null); const noBullet = computed(() => (isTaskList.value ? { listStyleType: 'none', marginLeft: '-20px' } : {})); </script>
@@ -0,0 +1 @@
1
+ <template> <table> <slot> <thead><slot name="head"></slot></thead> <tbody><slot name="body"></slot></tbody> </slot> </table> </template>
@@ -0,0 +1,52 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as RichTextEditorStories from './rich-text-editor.stories';
3
+
4
+ <Meta of={RichTextEditorStories} />
5
+
6
+ # RichTextEditor
7
+
8
+ An editable content area with additional functionality that allows to change text styling and add links and tables.
9
+
10
+ <Canvas of={RichTextEditorStories.Default} />
11
+
12
+ ## Use Cases
13
+
14
+ Use a RichTextEditor if:
15
+
16
+ - The text count exceeds 100 characters
17
+ - You need content with rich text formats (bold, italics, list, table)
18
+
19
+ ### Do not use
20
+
21
+ - Don’t use when you need user to enter shorter text content. Use input field or text area instead.
22
+ - Don’t use when you need user to enter non-sentence content (like URLs). Use input field instead.
23
+
24
+ ## Variations
25
+
26
+ When providing the value `visual` to the `mode` property, the editor will activate in visual mode when rendered. The
27
+ default value is `markdown`.
28
+
29
+ ## Modifiers
30
+
31
+ ### Disabled
32
+
33
+ The disabled property determines whether the component is enabled or disabled.
34
+
35
+ When a `rich-text-editor` component is disabled, it will not allow user interaction and the value of the input cannot be
36
+ changed.
37
+
38
+ The visual representation of a disabled `rich-text-editor` component is as a grayed-out color or and lack of hover
39
+ effect.
40
+
41
+ ### Error message
42
+
43
+ The `errorMessage` controls the error state of `rich-text-editor`:
44
+
45
+ ### Tooltip
46
+
47
+ A tooltip with extra information for the label of the `rich-text-editor` can be rendered by passing the desired
48
+ information to the `tooltipText` prop.
49
+
50
+ ## Resources
51
+
52
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=32951-148311&p=f&t=oKNt5rFb69AFPWN0-0)
@@ -0,0 +1 @@
1
+ import { isVue2 } from 'vue-demi'; import { ref, watch } from 'vue'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoRichTextEditor from './rich-text-editor.vue'; import type { Meta, StoryObj } from '@storybook/vue'; const meta: Meta = { title: 'Rich text editor', component: BentoRichTextEditor, argTypes: { mode: { name: 'mode', type: { name: 'string', required: false }, description: 'switch between Markdown or Visual mode', options: ['markdown', 'visual'], mapping: { markdown: 'markdown', visual: 'visual' }, control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoRichTextEditor>; const DEFAULT_LABEL = 'Label'; const DEFAULT_DESCRIPTION = 'Supporting text'; const DEFAULT_MARKDOWN_VALUE = ` # All rendering elements ## Renders a paragraph This is just a plain paragraph ## Renders a table | Month | Savings | | -------- | ------- | | January | $250 | | February | $80 | | March | $420 | ## Renders a link [Adyen.com](https://adyen.com) ## Renders lists ### An unordered list * An unordered * List of things * To test how it renders ### An ordered list 1. An ordered 2. List of things 3. To test how it renders ### A tasks list - [ ] a task list item - [ ] list syntax required ## Renders html as plain text <html> <body> <p>This is html content</p> <script>alert('This should be rendered as text')</script> </body> </html> ## Renders code \`\`\` git status git add git commit \`\`\` ## Renders quotes > Text that is a quote `; const defaultProps = { label: DEFAULT_LABEL, hideLabel: false, disabled: false, description: DEFAULT_DESCRIPTION, value: DEFAULT_MARKDOWN_VALUE, }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoRichTextEditor }, props: Object.keys(argTypes), template: ` <bento-rich-text-editor v-bind="args" :label="args.label" :hideLabel="args.hideLabel" :description="args.description" :value="inputValue" @input="inputAction" /> `, setup(props) { const args = isVue2 ? props : _args; const inputValue = ref(args.value); const inputAction = (value: string) => { inputValue.value = value; action('input')(value); }; watch( () => args.value, () => { inputValue.value = args.value; } ); return { args, inputValue, inputAction, }; }, }), args: { ...defaultProps, }, parameters: storybookDocsParameter(` <bento-rich-text-editor :value="inputValue" @input="inputValue" /> `), }; export const VisualMode: Story = { render: (_args, { argTypes }) => ({ components: { BentoRichTextEditor }, props: Object.keys(argTypes), template: ` <bento-rich-text-editor v-bind="args" :label="args.label" :hideLabel="args.hideLabel" :description="args.description" mode="visual" :value="inputValue" @input="inputAction" /> `, setup(props) { const args = isVue2 ? props : _args; const inputValue = ref(args.value); const inputAction = (value: string) => { inputValue.value = value; action('input')(value); }; watch( () => args.value, () => { inputValue.value = args.value; } ); return { args, inputValue, inputAction, }; }, }), args: { ...defaultProps, mode: 'visual', }, parameters: storybookDocsParameter(` <bento-rich-text-editor :value="inputValue" mode="visual" @input="inputValue" /> `), };
@@ -0,0 +1 @@
1
+ <template> <bento-typography el="div" class="b-rich-text-editor" rich-text> <field-label v-if="!hideLabel" :label="label" :tooltip-text="tooltipText" :required="required" /> <div v-bind="attrs" class="b-rich-text-editor__container" data-testid="rich-text-editor-container" :aria-disabled="disabled" :class="conditionalClasses" > <div class="b-rich-text-editor__toolbar"> <bento-segmented-control v-model="selectedMode" :items="richTextEditorModeOptions" /> </div> <keep-alive> <rich-text-editor-content-area v-if="selectedMode === 'markdown'" :value="internalValue" role="textbox" aria-multiline="true" :disabled="disabled" :aria-label="computedAriaLabel" :aria-describedby="ariaDescribedBy" @input="onInput" /> <bento-alert v-else-if="error" type="critical"> <template #default> {{ t('errorProcessingMarkdown', { value: error }) }} </template> </bento-alert> <rich-text-editor-renderer v-else :node="node" class="b-rich-text-editor__renderer" :class="rendererConditionalClasses" /> </keep-alive> </div> <error-message v-if="!!errorMessage" :id="errorId" :error-message="errorMessage" class="b-rich-text-editor__error-message" /> <span v-if="description" :id="descriptionId" class="b-rich-text-editor__description"> <bento-typography el="span" variant="body">{{ description }}</bento-typography> </span> </bento-typography> </template> <script setup lang="ts"> import { computed, ref, useAttrs, watch } from 'vue'; import type { Root } from 'mdast'; // Components import BentoTypography from '@/components/typography/typography.vue'; import BentoSegmentedControl from '@/components/segmented-control/segmented-control.vue'; import BentoAlert from '@/components/alert/alert.vue'; import { ErrorMessage, FieldLabel } from '@/internal'; import RichTextEditorContentArea from './components/rich-text-editor-content-area/rich-text-editor-content-area.vue'; import RichTextEditorRenderer from './components/rich-text-editor-renderer/rich-text-editor-renderer'; import type { BentoRichTextEditorMode, BentoRichTextEditorProps } from './rich-text-editor.types'; // Utils import { useI18n } from '@/utils/ts/i18n'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { generateUid } from '@/core/utils/ts'; import { parseMarkdown } from './utils'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const errorId = generateUid('rich-text-editor-error'); const descriptionId = generateUid('rich-text-editor-description'); const attrs = useAttrs(); const props = withDefaults(defineProps<BentoRichTextEditorProps>(), { description: null, disabled: false, errorMessage: null, hideLabel: null, label: null, mode: 'markdown', required: false, tooltipText: null, value: null, }); const emit = defineEmits<{ /** * Emmited when the content of the editor changes */ (e: 'input', value: string): void; }>(); const selectedMode = ref<BentoRichTextEditorMode>(props.mode); const internalValue = ref(props.value); const node = ref<Root>(null); const error = ref<Error>(null); const richTextEditorModeOptions = computed< Array<{ label: string; value: BentoRichTextEditorMode; disabled: boolean; }> >(() => [ { label: t('visual'), value: 'visual', disabled: props.disabled, }, { label: t('markdown'), value: 'markdown', disabled: props.disabled, }, ]); const conditionalClasses = computed(() => ({ 'b-rich-text-editor__container--disabled': props.disabled, 'b-rich-text-editor__container--error': props.errorMessage, })); const rendererConditionalClasses = computed(() => ({ 'b-rich-text-editor__renderer--disabled': props.disabled, })); // ARIA const computedAriaLabelFallbackMessage = computed(() => t('ariaLabelFallback')); const ariaLabelAttribute = ref(attrs['aria-label']); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, defaultFallback: computedAriaLabelFallbackMessage, }); const ariaDescribedBy = computed( () => `${props.description ? descriptionId : ''} ${props.errorMessage ? errorId : ''}`.trim() || null ); /** * Turns internal value into an AST Node */ function updateNodeValue() { try { node.value = parseMarkdown(internalValue.value); error.value = null; } catch (e) { node.value = null; error.value = e.message.split(':')[1]; } } const onInput = (newContentValue: string) => { internalValue.value = newContentValue; }; watch(internalValue, (newValue: string) => { node.value = null; // Invalidate previously parsed node emit('input', newValue); }); watch( selectedMode, newVal => { if (newVal === 'visual' && !node.value) { updateNodeValue(); } }, { immediate: true } ); </script> <script lang="ts"> /** * An editable content area with additional functionality * that allows to change text styling and add links and tables. * * @example * import { BentoRichTextEditor } from '@adyen/bento-vue2'; * * export default { * components: { BentoRichTextEditor }, * template: ` * <bento-rich-text-editor * v-model="value" * :label="label" * :description="description" * :disabled="false" * /> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./rich-text-editor.scss" />