@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,217 @@
1
+ import { Canvas, Meta, Source } from '@storybook/blocks';
2
+ import * as ModalFullscreenStories from './modal-fullscreen.stories';
3
+
4
+ <Meta of={ModalFullscreenStories} />
5
+
6
+ # Modal Fullscreen
7
+
8
+ Fullscreen modal is another type of modal window that appears over any other page content, takes the full available
9
+ space (any content underneath is not visible) and focuses user's attention exclusively on one complex task or piece of
10
+ complex information at the moment.
11
+
12
+ <Canvas of={ModalFullscreenStories.Default} />
13
+
14
+ ## Use Cases
15
+
16
+ Modals interrupt user workflow by design. Users can not go back to previous workflow until they finish the modal task or
17
+ dismiss it.
18
+
19
+ - **Complex multi-step processes or forms**
20
+ Fullscreen modal can help maintain focus and guide the user through each step without distractions.
21
+
22
+ - **Space-heavy content or interactions**
23
+ This component is great for content or interactions that require a larger canvas for optimal presentation (data with
24
+ complex legend and controls, interactive maps, etc).
25
+
26
+ - **To present complex contextual, secondary or temporary information**
27
+ The modal provides clear separation from the main interface making it suitable for presenting secondary, contextual or
28
+ temporary content without cluttering primary view (detailed previews, configurations, etc).
29
+
30
+ - **To gather user feedback**
31
+ Getting user feedback or conducting surveys can be done more effectively using a fullscreen modal allowing user to
32
+ focus on providing responses without distractions
33
+
34
+ ### Do not use
35
+
36
+ - For repeating primary/main user workflows or pages that are permanent and are accessed by its own link - use full page
37
+ layout instead.
38
+
39
+ - For small confirmations or displaying non-critical information
40
+
41
+ - For context-specific actions that require users to reference information or interact with other parts of interface
42
+ simultaneously
43
+
44
+ ## Best practices
45
+
46
+ - Keep title and contextual label short and clear
47
+ - If the title is long or needs to have a bigger size consider placing it inside of the content container instead
48
+ - If you use header slot ensure that it works in different resolutions
49
+
50
+ - **Do not** use more than 2 actions in the header
51
+ - **Do not** use more than 1 primary action in the header
52
+
53
+ ## Navigation
54
+
55
+ Modal fullscreen component supports deeper level navigation (user can go deeper in hierarchy and go back).
56
+
57
+ To handle navigation, every `bento-modal-fullscreen-page` should have a `pageId`, which can then be passed to the
58
+ `activePage` prop in `bento-modal-fullscreen` component to handle which page should be shown.
59
+
60
+ The back button by default, will send the user to the previous page. If there is a use case where we don't want the user
61
+ to navigate to the previous page, we can set the `previousPage` to null, in order to hide the button, or to a different
62
+ `pageId`, to send the user to a different previous page.
63
+
64
+ <Canvas of={ModalFullscreenStories.MultiplePages} />
65
+
66
+ ## Properties
67
+
68
+ This is the list of properties applicable to `bento-modal-fullscreen`, when using a single page, or
69
+ `bento-modal-fullscreen-page`, when using multiple pages:
70
+
71
+ | Property Name | Description |
72
+ | ----------------- | ------------------------------------------------------- |
73
+ | `actions` | List of actions that will be used to render the buttons |
74
+ | `contextualLabel` | Text label that is displayed left to the action buttons |
75
+ | `title` | Title of the page |
76
+
77
+ Some properties can only be applied to `bento-modal-fullscreen`:
78
+
79
+ | Property Name | Description |
80
+ | ------------- | ------------------------------------------------------------------------------------------ |
81
+ | `activePage` | Used to determine the current page, matches with `bento-modal-fullscreen-page` pageId prop |
82
+ | `isOpen` | Property that controls if the fullscreen modal is open or closed |
83
+
84
+ Additionally, there are also properties applied only to `bento-modal-fullscreen-page`:
85
+
86
+ | Property Name | Description |
87
+ | -------------- | --------------------------------------------------------------------------------------------- |
88
+ | `pageId` | The id of the page, used with `bento-modal-fullscreen` activePage prop to control navigation |
89
+ | `previousPage` | Can be set to null to hide the back button, or to a `pageId` to override the normal behaviour |
90
+
91
+ ### Title
92
+
93
+ Title will appear on the left of the modal header. This property is **optional**, but it is recommended to always show a
94
+ title. Title should be short and clear.
95
+
96
+ ### Contextual Label
97
+
98
+ Contextual label will appear on the right side of the header, to the left of the actions. This could be used to display
99
+ the number of items selected, when it was last saved or anything that has contextual relevancy to the modal and/or
100
+ actions. Contextual label should be short and clear.
101
+
102
+ ### Actions
103
+
104
+ Actions will appear on the right side of the header. It is not allowed to pass more than two actions and there should
105
+ only be one primary action. Be mindful of the space the actions will take and keep the label on the buttons short and
106
+ concise.
107
+
108
+ ## Handling the Close Event
109
+
110
+ The modal offers two ways to handle being closed, giving developers either simplicity or greater control. The behavior
111
+ depends on whether a listener for the `before-close` event is present.
112
+
113
+ ### Default Behavior (No `before-close` listener)
114
+
115
+ By default, when the close button is clicked or `Esc` is pressed, the modal will play its closing animation and then
116
+ automatically emit the `update:is-open` event. This is the simplest approach and works seamlessly with `v-model` or the
117
+ `.sync` modifier.
118
+
119
+ **Example:**
120
+
121
+ <Source
122
+ dark
123
+ language="html"
124
+ code={`<template>
125
+ <div>
126
+ <bento-button @click="isOpen = true">Open Modal</bento-button>
127
+ <bento-modal-fullscreen :is-open.sync="isOpen" title="Default Behavior">
128
+ <p>The modal will close itself automatically.</p>
129
+ </bento-modal-fullscreen>
130
+ </div>
131
+ </template>
132
+ <script setup lang="ts">
133
+ import { ref } from 'vue';
134
+ import { BentoButton, BentoModalFullscreen } from '@adyen/bento-vue2';
135
+ const isOpen = ref(false);
136
+ </script>`}
137
+ />
138
+
139
+ ### Advanced Control (With `before-close` listener)
140
+
141
+ For cases where an action needs to be performed _before_ the modal starts its closing animation (e.g., to show a
142
+ confirmation or save data), you can listen for the `before-close` event.
143
+
144
+ When a `@before-close` listener is attached:
145
+
146
+ 1. The component emits `before-close` immediately.
147
+ 2. The closing animation is **not** played automatically.
148
+ 3. The parent component is now responsible for setting the `:is-open` prop to `false` to close the modal and trigger the
149
+ animation.
150
+
151
+ This gives you a window to perform asynchronous actions before the modal visually closes.
152
+
153
+ **Example:**
154
+
155
+ <Source
156
+ dark
157
+ language="html"
158
+ code={`<template>
159
+ <div>
160
+ <bento-button @click="isOpen = true">Open Modal with Confirmation</bento-button>
161
+ <bento-modal-fullscreen :is-open="isOpen" title="Advanced Control" @before-close="handleClose">
162
+ <p>This modal will ask for confirmation before closing.</p>
163
+ </bento-modal-fullscreen>
164
+ </div>
165
+ </template>
166
+ <script setup lang="ts">
167
+ import { ref } from 'vue';
168
+ import { BentoButton, BentoModalFullscreen } from '@adyen/bento-vue2';
169
+ const isOpen = ref(false);
170
+ function handleClose() {
171
+ if (window.confirm('Are you sure you want to close the modal?')) {
172
+ // Now we tell the modal to close
173
+ isOpen.value = false;
174
+ }
175
+ }
176
+ </script>`}
177
+ />
178
+
179
+ ## Slots
180
+
181
+ These are the slots applicable to `bento-modal-fullscreen`, when using a single page, or `bento-modal-fullscreen-page`,
182
+ when using multiple pages.
183
+
184
+ ### Header slot
185
+
186
+ Header slot will be rendered in the center of the header. As with other elements, be mindful of the space the elements
187
+ inside the slot will take on the header.
188
+
189
+ This slot is meant to be used with control or status awareness components, like `bento-segmented-control` or
190
+ `bento-stepper`.
191
+
192
+ ### Content/Default slot
193
+
194
+ The default slot is where the content of your modal is passed. Refer to the [use cases](#use-cases) section for examples
195
+ of the type of content that should be passed into a fullscreen modal.
196
+
197
+ ## Accessibility
198
+
199
+ Fullscreen Modal uses the native HTML `<dialog>` element.
200
+
201
+ It means that when Fullscreen Modal is opened, then everything other than the `<dialog>` and its contents is rendered
202
+ inert using the `inert` attribute.
203
+
204
+ It indicates the browser to ignore all the elements with `inert` attribute applied.
205
+
206
+ ### Keyboard interaction
207
+
208
+ - User can use `Tab` key to move focus to the trigger and use `Spacebar`/`Enter` to activate the sidepanel.
209
+ - The `Tab` should change focus between interactive elements in Fullscreen Modal (if there are few). This behaviour
210
+ should only exist when Fullscreen Modal is open and should not extend outside of the dialog (focus trap).
211
+ - When user uses `Esc` button, Fullscreen Modal should close and return focus to the trigger.
212
+ - _OR_ when user use tab key to move focus to the dismiss/close button and then use Spacebar/Enter key -> we close
213
+ the Fullscreen Modal and return focus to the trigger
214
+
215
+ ## Resources
216
+
217
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=20806-5484&m=dev)
@@ -0,0 +1 @@
1
+ import { ref, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { BentoSegmentedControl } from '@/components/segmented-control'; import { BentoTypography } from '@/components/typography'; import { BentoButton, type BentoButtonActionsList } from '@/components/button'; import BentoModalFullscreen from './modal-fullscreen.vue'; import BentoModalFullscreenPage from './components/modal-fullscreen-page.vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import type { Meta, StoryObj } from '@storybook/vue'; import BentoModal from '../modal/modal.vue'; const meta: Meta = { title: 'Modal fullscreen', component: BentoModalFullscreen, argTypes: { // Slots default: { name: 'default', description: 'Content of the fullscreen modal', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, header: { name: 'header', description: 'Header slot in the middle of the modal header', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoModalFullscreen>; const DEFAULT_PROPS = { title: 'Fullscreen modal title', contextualLabel: 'Aditional contextual label', isOpen: false, actions: [ { title: 'Primary', event: () => true, }, { title: 'Secondary', event: () => true, }, ], }; const SEGMENTED_CONTROL_ITEMS = [ { label: 'Segment 1', value: 1 }, { label: 'Segment 2', value: 2 }, { label: 'Segment 3', value: 3 }, ]; const LONG_CONTENT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum molestie gravida velit id fermentum. Sed posuere dapibus odio vel consectetur. Vivamus sapien erat, vulputate ut porttitor sit amet, congue vel enim. Nullam laoreet nulla elit, in elementum mauris hendrerit sit amet. Fusce a magna lobortis, fermentum sapien vel, hendrerit ex. Nam vel lectus quam. Ut non vulputate est. In hac habitasse platea dictumst. Integer at lectus vestibulum, pulvinar ante in, efficitur neque. Duis sodales libero a dui scelerisque, in auctor enim malesuada. Aenean vitae velit aliquet, viverra velit et, consectetur nulla. Nulla sagittis, enim nec lacinia sagittis, quam tortor dictum arcu, consequat sodales nisi turpis sit amet diam. Etiam varius porta pharetra. Curabitur vestibulum placerat purus. Aliquam erat volutpat.'; const defaultCode = ` <template> <bento-button :aria-expanded="isOpen" @click="isOpen = !isOpen" > Open modal fullscreen </bento-button> <bento-modal-fullscreen title="Fullscreen modal title" contextual-label="Aditional contextual label" :is-open="isOpen" @update:is-open="isOpen = $event" :actions="actions" > <template #header> <!-- Put content/component you want in the header slot here, for example, a Segmented Control --> <bento-segmented-control :items="SEGMENTED_CONTROL_ITEMS" :model-value="selectedValue" @update:model-value="selectedValue = $event"/> </template> <div> <!-- Put the content of your modal here --> </div> </bento-modal-fullscreen> </template> <script setup lang="ts"> import { ref } from 'vue'; import { BentoModalFullscreen, BentoButton } from '@adyen/bento-vue2'; const isOpen = ref(false); const actions = [ { title: 'Primary', event: () => true, }, { title: 'Secondary', event: () => true, }, ]; // Segmented control props const SEGMENTED_CONTROL_ITEMS = [ { label: 'Segment 1', value: 1 }, { label: 'Segment 2', value: 2 }, { label: 'Segment 3', value: 3 }, ]; const selectedValue = ref(SEGMENTED_CONTROL_ITEMS[0].value); </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoModalFullscreen, BentoButton, BentoSegmentedControl, BentoTypography }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = true" > Open modal fullscreen </bento-button> <bento-modal-fullscreen v-bind="args" :is-open="isOpen" @update:is-open="isOpen = $event"> <template #header> <template v-if='args.header'>{{ args.header }}</template> <bento-segmented-control v-else :items="SEGMENTED_CONTROL_ITEMS" :model-value="selectedValue" @update:model-value="selectedValue = $event"/> </template> <template v-if='args.default'>{{ args.default }}</template> <div v-else style="display: flex; height: 100%"> <div style="border-right: 1px solid #dbdee2; padding: 2em; min-height: 100%; height: max-content"> <bento-typography> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> </bento-typography> </div> <div style="padding: 2em"> <bento-typography> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> </bento-typography> </div> </div> </bento-modal-fullscreen> </div> `, setup(props) { const isOpen = ref(false); const args = isVue2 ? props : _args; const selectedValue = ref(SEGMENTED_CONTROL_ITEMS[0].value); watch( () => args.isOpen, () => { isOpen.value = args.isOpen; } ); return { args, isOpen, selectedValue, LONG_CONTENT, SEGMENTED_CONTROL_ITEMS, }; }, }), args: DEFAULT_PROPS, parameters: storybookDocsParameter(defaultCode), }; const multiplePagesCode = ` <template> <bento-button :aria-expanded="isOpen" @click="isOpen = !isOpen" > Open modal fullscreen </bento-button> <bento-modal-fullscreen :is-open="isOpen" :active-page="activePage" @update:active-page="activePage = $event" @update:is-open='isOpen = $event' > <bento-modal-fullscreen-page v-bind="page1"> <bento-typography> <!-- Put the content of your page here --> </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="page2" > <bento-typography> <!-- Put the content of your page here --> </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="page3" > <bento-typography> <!-- Put the content of your page here --> </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="page4"> <bento-typography> <!-- Put the content of your page here --> </bento-typography> </bento-modal-fullscreen> </template> <script setup lang="ts"> import { ref } from 'vue'; import { BentoModalFullscreen, BentoModalFullscreenPage, BentoButton, BentoTypography } from '@adyen/bento-vue2'; const isOpen = ref(false); const activePage = ref('intro'); const page1 = { pageId: 'page1', title: 'Page 1', actions: [ { title: 'Go to next page', event: () => { activePage.value = 'page2'; }, } ] }; const page2 = { pageId: 'page2', title: 'Page 2', actions: [ { title: 'Go to next page', event: () => { activePage.value = 'page3'; }, } ] }; const page3 = { pageId: 'page3', title: 'Page 3', actions: [ { title: 'Go to next page', event: () => { activePage.value = 'page4'; }, } ] }; const page4 = { pageId: 'page4', title: 'Page 4', actions: [ { title: 'Finish', event: () => { isOpen.value = false; }, } ], previousPage: null, }; </script> `; export const MultiplePages: Story = { render: (_args, { argTypes }) => ({ components: { BentoModalFullscreen, BentoModalFullscreenPage, BentoButton, BentoTypography }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = true" > Open modal fullscreen </bento-button> <bento-modal-fullscreen v-bind="args" :is-open="isOpen" :active-page="activePage" @update:active-page="activePage = $event" @update:is-open='isOpen = $event'> <bento-modal-fullscreen-page v-bind="intro"> <bento-typography style="padding: 24px"> {{ LONG_CONTENT }} <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> <p>{{LONG_CONTENT}}</p> </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="newProfile" > <bento-typography style="padding: 24px"> This page has some info about creating a new profile </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="editProfile" > <bento-typography style="padding: 24px"> This is where you edit your profile </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="newProfileForm"> <bento-typography style="padding: 24px"> This is where you create a new profile </bento-typography> </bento-modal-fullscreen-page> <bento-modal-fullscreen-page v-bind="success"> <bento-typography style="padding: 24px"> Your profile is created </bento-typography> </bento-modal-fullscreen-page> </bento-modal-fullscreen> </div> `, setup(props) { const isOpen = ref(false); const activePage = ref('intro'); const args = isVue2 ? props : _args; watch( () => args.isOpen, () => { isOpen.value = args.isOpen; } ); watch( () => args.activePage, () => { activePage.value = args.activePage; } ); const intro = { pageId: 'intro', title: 'Intro', actions: [ { title: 'New profile', event: () => { activePage.value = 'newProfile'; }, }, { title: 'Edit profile', event: () => { activePage.value = 'editProfile'; }, }, ], contextualLabel: 'What do you wanna do?', }; const newProfile = { pageId: 'newProfile', title: 'New profile', actions: [ { title: 'Go to form', event: () => { activePage.value = 'newProfileForm'; }, }, ], }; const editProfile = { pageId: 'editProfile', title: 'Edit profile', actions: [ { title: 'Save', event: () => { activePage.value = 'success'; }, }, { title: 'Cancel', event: () => { isOpen.value = false; }, }, ], }; const newProfileForm = { pageId: 'newProfileForm', title: 'New profile form', actions: [ { title: 'Save', event: () => { activePage.value = 'success'; }, }, { title: 'Cancel', event: () => { isOpen.value = false; }, }, ], }; const success = { pageId: 'success', title: 'Success!', actions: [ { title: 'Delete', critical: true, event: () => { isOpen.value = false; action('delete')(); }, }, { title: 'Cancel', event: () => { isOpen.value = false; }, }, ], previousPage: null, }; return { args, activePage, intro, newProfile, newProfileForm, success, editProfile, isOpen, LONG_CONTENT, }; }, }), args: { ...DEFAULT_PROPS, activePage: 'intro' }, parameters: storybookDocsParameter(multiplePagesCode), }; export const withBeforeClose: Story = { render: (_args, { argTypes }) => ({ components: { BentoModalFullscreen, BentoModalFullscreenPage, BentoButton, BentoTypography, BentoModal, BentoSegmentedControl, }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = !isOpen"> Open modal fullscreen </bento-button> <bento-modal-fullscreen title="Fullscreen modal title" contextual-label="Click on the 'X' to get the confirmation modal" :is-open="isOpen" @before-close="onClose" @click="onClose" @update:is-open="isOpen = $event" > <div> <!-- Put the content of your modal here --> <p>This is the content of the fullscreen modal.</p> <p>Click on the "X" (close) button to get the confirmation modal.</p> </div> </bento-modal-fullscreen> <bento-modal content="Are you sure you want to proceed with this action?" variant="dialog" dialog-type="confirmation" :actions="dialogActions" :is-open="isConfirmationOpened" @close-modal="closeModal" > Modal Title </bento-modal> </div> `, setup() { const isOpen = ref(false); const isConfirmationOpened = ref(false); const onClose = () => { openModal(); }; const dialogActions: BentoButtonActionsList = [ { title: 'Confirm', event: () => { isOpen.value = false; closeModal(); }, }, ]; const closeModal = () => { isConfirmationOpened.value = false; }; const openModal = () => { isConfirmationOpened.value = true; }; return { dialogActions, isConfirmationOpened, isOpen, onClose, closeModal, openModal, }; }, }), };
@@ -0,0 +1 @@
1
+ <template> <dialog :id="modalFullscreenId" ref="modal" class="b-modal-fullscreen" :class="conditionalClasses" data-dialog-element="modal-fullscreen" @animationend="closeModalAfterAnimation" @keydown.esc.prevent="closeModal" > <bento-focus-trap v-if="internalIsOpen" class="b-modal-fullscreen__wrapper"> <template v-if="props.activePage"> <slot /> </template> <bento-modal-fullscreen-page v-else :actions="actions" :contextual-label="contextualLabel" :title="title"> <template v-if="hasSlot('default')" #default> <slot name="default"></slot> </template> <template v-if="hasSlot('header')" #header> <slot name="header"></slot> </template> </bento-modal-fullscreen-page> <div class="b-modal-fullscreen__close-button"> <bento-button variant="tertiary" :aria-label="t('close')" @click="closeModal"> <template #iconLeft><cross-icon :aria-hidden="true" /></template> </bento-button> </div> </bento-focus-trap> <!-- Enable the toast inside the fullscreen-modal --> <template v-if="isOpen"> <bento-toast /> </template> </dialog> </template> <script setup lang="ts"> import { computed, nextTick, type PropType, provide, ref, toRefs, useSlots, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { BentoToast } from '@/components/toast'; import { BentoButton, type BentoButtonActionsList } from '@/components/button'; import { BentoFocusTrap } from '@/components/focus-trap'; import BentoModalFullscreenPage from './components/modal-fullscreen-page.vue'; import CrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; import { useI18n } from '@/utils/ts/i18n'; import { generateUid } from '@/core/utils/ts'; import { useHasSlot } from '@/composables'; import { useListeners } from '@/composables/use-listeners'; import { MODAL_FULLSCREEN_DIALOG_ID_INJECTION_KEY } from '@/components/modal-fullscreen/modal-fullscreen.keys'; import messages from './messages.json'; import { BentoDialogPageAnimation, type BentoDialogPageConfigData, type BentoDialogPageRegisterPage, DIALOG_PAGE_CONFIG_INJECTION_KEY, DIALOG_PAGE_REGISTER_PAGE_INJECTION_KEY, } from '@/internal'; type MessageSchema = (typeof messages)['en-US']; const props = defineProps({ /** * List of actions that will be used to render the buttons. * First element in the list will be rendered as "primary". * All elements after the first will be rendered as "secondary". * * Each object in the list should have a `title`, an `event` and * (optional) a Vue `icon` from the library `@adyen/ui-assets-icons-16`. * * @see BentoButton for a list of all the other props supported by each button action. */ actions: { type: Array as PropType<BentoButtonActionsList>, default: null, }, /** * The id of the current active page * Should be the same as the pageId of the BentoModalFullscreenPage to be displayed */ activePage: { type: String, default: null, }, /** * Label shown next to the modal actions */ contextualLabel: { type: String, default: null, }, /** * Controls the modal's state from the outside. Set it to true to open the modal */ isOpen: { type: Boolean, default: null, }, /** * Title of the modal */ title: { type: String, default: null, }, }); const emit = defineEmits<{ /** * Handles closing the modal. The behavior changes depending on whether a `before-close` event listener is present. * * - **When listening for the `before-close` event:** * The `before-close` event is emitted immediately, skipping the animation. This allows for logic to run before the modal closes. * The `isOpen` prop must then be set to `false` to trigger the closing animation. * * - **When not listening for the `before-close` event:** * The closing animation plays, then `update:is-open` is emitted with `false`. * This is the default behavior, compatible with `v-model` or `.sync` on the `isOpen` prop. */ (e: 'before-close'): void; /** * Emits update event when page is changed internally */ (e: 'update:active-page', page: string): void; /** * Emits 'update:is-open' event when the modal is closed */ (e: 'update:is-open', isOpen: boolean): void; }>(); // Refs const { activePage, isOpen } = toRefs(props); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const modal = ref(null); const isClosing = ref(false); const modalFullscreenId = generateUid('modal-fullscreen'); const internalIsOpen = ref(false); const listeners = useListeners(); const slots = useSlots(); const hasSlot = useHasSlot(slots); // Animation styling const conditionalClasses = computed(() => ({ 'b-modal-fullscreen--hide': isClosing.value, })); // Handle closing and closing animation for dialog element const closeModalAfterAnimation = () => { if (isClosing.value) { modal.value.close(); if (previousPages.value[0]) { emit('update:active-page', previousPages.value[0]); } previousPages.value = []; isClosing.value = false; internalIsOpen.value = false; emit('update:is-open', false); } }; /** * Emits the `beforeClose` event, if it is being listened to, * before closing the modal and omits the closing animation. * The animation will be triggered by the change of the `isOpen` prop instead. * * If no listener for the `before-close`, then the animation will be triggered, * which will close the modal and emit the `update:is-open` event instead, after it was closed. */ function closeModal() { /** * Vue3 appends the prefix "on" and changes to camelCase */ const eventName = isVue2 ? 'before-close' : 'onBeforeClose'; if (listeners[eventName]) { emit('before-close'); return; } startCloseModalAnimation(); } const startCloseModalAnimation = () => { if (!modal.value.open) { return; } isClosing.value = true; }; // Watching isOpen prop to toggle open/close the dialog element watch( () => isOpen.value, async () => { // Wait for modal ref to be created await nextTick(); if (!modal.value) { return; } if (isOpen.value) { internalIsOpen.value = true; modal.value.showModal(); if (!previousPages.value.length) { previousPages.value.push(activePage.value); } } else if (modal.value.open) { startCloseModalAnimation(); } }, { immediate: true } ); // lets children components know they are inside a Modal (dialog element) provide(MODAL_FULLSCREEN_DIALOG_ID_INJECTION_KEY, modalFullscreenId); // Pages const animation = ref(BentoDialogPageAnimation.TRANSITION_LEVEL_DEEPER); const pages = ref<Array<string>>([]); const previousPages = ref([]); const updatePage = page => emit('update:active-page', page); provide<BentoDialogPageConfigData>(DIALOG_PAGE_CONFIG_INJECTION_KEY, { activePage, animation, isOpen: internalIsOpen, pages, previousPages, updatePage, }); // unregister page if removed / destroyed const unregisterPage = (pageIdToRemove: string) => () => { pages.value = pages.value?.filter(pageId => pageId !== pageIdToRemove); }; // Provide data and function to register page to subcomponent const registerPage: BentoDialogPageRegisterPage = pageId => { if (pageId) { pages.value.push(pageId); } return { unregisterPage: unregisterPage(pageId) }; }; provide(DIALOG_PAGE_REGISTER_PAGE_INJECTION_KEY, registerPage); </script> <script lang="ts"> /** * Fullscreen modal takes full available space (no content underneath is visible) and focuses the user's attention exclusively on one complex task or piece of complex information. * * @example * import { BentoModalFullscreen } from '@adyen/bento-vue2'; * * export default { * components: { BentoModalFullscreen }, * template: ` * <bento-modal-fullscreen * :is-open="isOpen" * @update:is-open="isOpen = $event" * title='Fullscreen modal title' * contextual-label='Contextual label' * :actions="[{ title: 'Action', event: () => {}]" * > * <template #header>{{ header slot }}</template> * {{ content }} * </bento-modal-fullscreen> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./modal-fullscreen.scss" />
@@ -0,0 +1 @@
1
+ <template> <div class="b-pagination-context"> <bento-typography v-if="totalPages > 1" el="div"> <component :is="i18nComponent" v-bind="getI18nProps('pageNumber')" tag="div"> <template #page> <bento-dropdown :id="dropdownId" :aria-label="t('pageNumber', { page: n(page) })" class="b-pagination-context__dropdown" condensed :items="listOfAvailablePages" :model-value="page" :virtual-scroll="virtualScroll" @update:model-value="onPageSelectChange" /> </template> </component> </bento-typography> <bento-typography v-else-if="totalPages === 1" el="span"> {{ t('pageOneOfOne') }} </bento-typography> <bento-typography v-else el="span"> {{ t('pageNumber', { page: n(page) }) }} </bento-typography> </div> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoTypography } from '@/components/typography'; import { BentoDropdown } from '@/components/dropdown'; import { type BentoListboxOptionItem } from '@/types/listbox'; import { generateUid } from '@/core/utils/ts'; import messages from './messages.json'; import { getI18nComponent, getI18nProps, useI18n } from '@/utils/ts/i18n'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults( defineProps<{ /** * The current page number of the pager. */ page?: number; /** * The total number of items the pager is paging through. */ totalPages?: number; /** * Enables virtual scrolling on all dropdowns if set to true. */ virtualScroll?: boolean; }>(), { page: 1, totalPages: null, virtualScroll: false, } ); const emit = defineEmits<{ (e: 'page-selected', value: string | number); }>(); const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const dropdownId = generateUid('bento-pagination-context-current-page'); const onPageSelectChange = (selectedPage: string | number) => emit('page-selected', selectedPage); const i18nComponent = getI18nComponent(); // Generate a list of pages from 1 to "totalPages" const transformIntoOption = (value: number): BentoListboxOptionItem => ({ label: n(value), value, }); const transformToPageNumber = (_, index) => index + 1; const listOfAvailablePages = computed(() => Array.from(new Array(props.totalPages), transformToPageNumber).map(transformIntoOption) ); </script> <script lang="ts"> export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./pagination-context.scss" />
@@ -0,0 +1 @@
1
+ <template> <ul class="b-pagination-controls"> <li class="b-pagination-controls__item"> <!-- Navigate to the first page --> <bento-button :disabled="isCurrentPageFirst" variant="tertiary" condensed @click="navigate(1)"> <template #iconLeft> <skip-left :svg-title="t('navigateToTheFirstPage')" /> </template> </bento-button> </li> <li class="b-pagination-controls__item"> <!-- Navigate to the previous page --> <bento-button :disabled="isCurrentPageFirst" variant="tertiary" condensed @click="navigate(page - 1)"> <template #iconLeft> <chevron-left :svg-title="t('navigateToThePreviousPage')" /> </template> </bento-button> </li> <li class="b-pagination-controls__item"> <!-- Navigate to the next page --> <bento-button :disabled="isNextButtonDisabled" variant="tertiary" condensed @click="navigate(page + 1)"> <template #iconLeft> <chevron-right :svg-title="t('navigateToTheNextPage')" /> </template> </bento-button> </li> <li class="b-pagination-controls__item"> <!-- Navigate to the last page --> <bento-button :disabled="isLastPageButtonDisabled" variant="tertiary" condensed @click="navigate(totalPages)" > <template #iconLeft> <skip-right :svg-title="t('navigateToTheLastPage')" /> </template> </bento-button> </li> </ul> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoButton } from '@/components/button'; import { useI18n } from '@/utils/ts/i18n'; import SkipLeft from '@adyen/ui-assets-icons-16/vue/skip-left'; import SkipRight from '@adyen/ui-assets-icons-16/vue/skip-right'; import ChevronLeft from '@adyen/ui-assets-icons-16/vue/chevron-left-small'; import ChevronRight from '@adyen/ui-assets-icons-16/vue/chevron-right-small'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults( defineProps<{ /** * Use to determine if the pager has a "next page". */ hasNext?: boolean; /** * The current page number of the pager. */ page: number; /** * The current page number of the pager. */ totalPages?: number; }>(), { hasNext: null, totalPages: null, } ); const emit = defineEmits<{ (e: 'navigate', value: number); }>(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const isCurrentPageFirst = computed(() => props.page === 1); const isCurrentPageLast = computed(() => props.page === props.totalPages); const isNextButtonDisabled = computed(() => { const hasNoNextPage = !props.hasNext; return hasNoNextPage || isCurrentPageLast.value; }); const isLastPageButtonDisabled = computed(() => { const hasNoNextPage = !props.hasNext; const hasNoTotalPagesNumber = !props.totalPages; return hasNoNextPage || isCurrentPageLast.value || hasNoTotalPagesNumber; }); const navigate = (pageArg: number) => { emit('navigate', pageArg); }; </script> <script lang="ts"> export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./pagination-controls.scss" />
@@ -0,0 +1 @@
1
+ <template> <div class="b-pagination-results-per-page"> <bento-typography el="div"> <component :is="i18nComponent" v-bind="getI18nProps(translationPathKey)" class="b-pagination-results-per-page__dropdown-container" tag="div" :plural="totalCount" > <template #itemCount> <bento-dropdown :id="dropdownId" :aria-label="t(translationPathKey, { itemCount: itemsPerPage, totalCount })" class="b-pagination-results-per-page__dropdown" condensed :items="predefinedDropdownOptions" :model-value="itemsPerPage" @update:model-value="onSelectChange" /> </template> <template v-if="!!totalCount" #totalCount>{{ totalCount }}</template> </component> </bento-typography> </div> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoTypography } from '@/components/typography'; import { BentoDropdown } from '@/components/dropdown'; import { generateUid } from '@/core/utils/ts'; import { getI18nComponent, getI18nProps, useI18n } from '@/utils/ts/i18n'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const props = withDefaults( defineProps<{ /** * The amount of items the pager is paging through. */ itemsPerPage?: number; /** * The total number of items the pager is paging through. */ totalCount?: number; }>(), { itemsPerPage: 50, totalCount: null, } ); const emit = defineEmits<{ (e: 'select', value: number); }>(); const dropdownId = generateUid('b-pagination-context-number-elements'); const predefinedDropdownOptions = computed(() => [ { label: n(10), value: 10 }, { label: n(20), value: 20 }, { label: n(50), value: 50 }, { label: n(75), value: 75 }, { label: n(100), value: 100 }, ]); const i18nComponent = getI18nComponent(); const onSelectChange = (selectedNumberOfResults: number) => { emit('select', selectedNumberOfResults); }; const translationPathKey = computed(() => (props.totalCount ? 'showingItemsWithTotal' : 'showingItems')); </script> <script lang="ts"> export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./pagination-results-per-page.scss" />
@@ -0,0 +1,116 @@
1
+ import { Canvas, Story, Meta } from '@storybook/blocks';
2
+ import * as ComponentStories from './pagination.stories';
3
+
4
+ <Meta of={ComponentStories} />
5
+
6
+ # Pagination
7
+
8
+ The pagination component is used to navigate between a set of results, as well as to select how many to display per
9
+ page. It is also internally used in the data grid component.
10
+
11
+ <Canvas of={ComponentStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use a pagination component when there are too many results to show on one page. What constitutes “too many results”
16
+ varies case by case and can be influenced by:
17
+
18
+ - System load times
19
+ - Amount of data in each entry
20
+ - Screen space So speak to your product designer and decide accordingly based on your use case.
21
+
22
+ ### Customize your pagination
23
+
24
+ We provide a series of customizations for the pagination component that can better accommodate your use cases. These
25
+ are:
26
+
27
+ #### Number of elements per page
28
+
29
+ You can decide how many elements to show per page by passing a value to the `size` dropdown (`Showing .. of .. items`).
30
+ This dropdown comes with a predefined set of values, namely 10, 20, 50, 75 and 100 - with the default value being 20. On
31
+ smaller screens thisdropdown is not shown anymore. In this case, the number of results (`Page .. of ..`) is displayed.
32
+
33
+ ##### Hide Page Size
34
+
35
+ You can hide this element with this modifier property set to true.
36
+
37
+ #### Slot
38
+
39
+ You can use the default slot to add more content to the pagination on the left side. The slot will only be visible if
40
+ `hidePageSize` is true, as the slot takes up the same space in the component.
41
+
42
+ #### Total count dropdown
43
+
44
+ You can display a dedicated dropdown showing the total number of pages (`Page .. of ..`) by passing the `totalCount`
45
+ property in order to make the navigation for the user more easy.
46
+
47
+ <Canvas of={ComponentStories.Advanced} />
48
+
49
+ #### Navigating pages
50
+
51
+ You can navigate to a specific page either by using the dedicated dropdown (`Page .. of ..`), or by going page by page
52
+ using the arrows. There is also the possibility to go to the first and the last page with the dedicated arrows. When
53
+ it’s not possible to go further back or forth, the respective arrows get disabled. In case there is only one page to
54
+ display, all arrows are disabled and there is no dropdown shown.
55
+
56
+ #### Setting the current visible page
57
+
58
+ You can set the page to display when the component is mounted by passing an appropriate `page` value. Note that you need
59
+ to set this value accordingly given the range of valid values you have based on your total number of items.
60
+
61
+ ### Do not use
62
+
63
+ When using the pagination component, do not:
64
+
65
+ - Set it with a `hasNext` property if there is only one page, as this will display an active forward arrow which won't
66
+ do anything when clicked upon.
67
+ - Set it with a current page value which is not equal or greater than one and is greater than the maximum value
68
+ allowed for your use case.
69
+
70
+ ## Variations
71
+
72
+ ### Total count
73
+
74
+ if set, displays the pagination with a dropdown displaying the pages total count (`Page .. of ..`). It should be equal
75
+ to the total number of items the pager is paging through.
76
+
77
+ ## Modifiers
78
+
79
+ ### Has next
80
+
81
+ The `hasNext` property tells the pagination component that there is a next page and that the user can navigate forward.
82
+ You need to set this value yourself accordingly.
83
+
84
+ ## Accessibility 🚧
85
+
86
+ The full accessibility implementation for this component is still under development, so changes to this section are to
87
+ be expected. However, as for all the other `bento` components we are adhering to
88
+ [WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-1.2/) standard and plan on implementing a standard
89
+ [pagination pattern](https://design-system.w3.org/components/pagination.html) paired with a
90
+ [menubar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/).
91
+
92
+ ### Keyboard interaction
93
+
94
+ Since the pagination component is built using a `nav` tag and its basic building blocks are made up by `bento-buttons`,
95
+ the pagination is reachable via keyboard navigation and is placed in the normal flow of the page.
96
+
97
+ Note that the `bento-dropdown` component is still a work in progress and thus accessibility support is not provided for
98
+ it yet.
99
+
100
+ | Keyboard interaction | Output |
101
+ | -------------------- | ---------------------------------------------------------------------------------------------- |
102
+ | `Tab` | Moves focus to the pagination component and its children or away from it. |
103
+ | `Space` | When focus is on any child element within the pagination component, it activates said element. |
104
+
105
+ ### Roles, states and properties
106
+
107
+ | ARIA attribute | is automatic? | fallback used | manual override |
108
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------ |
109
+ | `aria-label` | No, for now you need to pass in a value through the dedicated prop. In the future, it will have an automatic value in the appropriate locale. | - | The `aria-label` prop can be passed to provide an accessible name. |
110
+ | `role` | Yes, through the semantic `nav` element. However, this will be revised in the near future given the official guidelines to make it fully accessible. | - | - |
111
+
112
+ ## Resources
113
+
114
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=1951-30261&t=NHLd9msx8lGq3NNF-0)
115
+ - [WAI-ARIA menubar pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/)
116
+ - [W3C accessible pagination](https://design-system.w3.org/components/pagination.html)
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { BentoPagination } from '@/components/pagination'; import { reactive, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { Meta, StoryObj } from '@storybook/vue'; import './pagination.stories.scss?module'; // Override viewports const INITIAL_VIEWPORTS = { cellphone: { name: 'Cellphone', styles: { height: '812px', width: '375px', }, type: 'mobile', }, tablet: { name: 'Tablet', styles: { height: '360px', width: '640px', }, type: 'tablet', }, }; const meta: Meta = { title: 'Pagination', component: BentoPagination, parameters: { viewport: { viewports: INITIAL_VIEWPORTS, }, }, argTypes: { // Enum size: { control: { disable: true } }, page: { control: { disable: true } }, // Event 'items-page': { table: { type: { summary: '(itemsPerPage: number) => void' }, }, control: { disable: true }, }, navigate: { table: { type: { summary: '(pageNumber: number, size?: number) => void' }, }, control: { disable: true }, }, 'update:page': { table: { type: { summary: '(pageNumber: number) => void' }, }, control: { disable: true }, }, 'update:size': { table: { type: { summary: '(itemsPerPage: number) => void' }, }, control: { disable: true }, }, // Slots default: { control: { type: 'text' }, }, }, }; export default meta; type Story = StoryObj<typeof BentoPagination>; const defaultCode = ` <template> <bento-pagination :size="itemsPerPage" :page="currentPage" @navigate="clickNavigate" /> </template> <script lang="ts" setup> const pageData = reactive({ page: 2, size: 20 }); const clickNavigate = (page: number, size: number) => { pageData.page = page; pageData.size = size; }; watch([() => pageData.page, () => pageData.size], () => { makeAPICall(pageData); }); </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoPagination }, props: Object.keys(argTypes), setup(props) { const pageData = reactive({ page: 2, size: 20 }); const clickNavigate = (page: number, size: number) => { action('navigate')(page, size); pageData.page = page; pageData.size = size; }; const updateItemsPerPage = (size: number) => { action('items-page')(size); }; watch([() => pageData.page, () => pageData.size], () => { action('watch-make-api-call')(pageData); }); return { // Values args: isVue2 ? props : _args, pageData, // Methods clickNavigate, updateItemsPerPage, }; }, template: ` <div class="b-pagination-story"> <bento-pagination v-bind="args" :size="pageData.size" :page="pageData.page" @navigate="clickNavigate" @items-page="updateItemsPerPage" > <template v-if="args.default">{{ args.default }}</template> </bento-pagination> </div> `, }), args: { hasNext: true, }, parameters: storybookDocsParameter(defaultCode), }; const advancedCode = ` <bento-pagination :size="itemsPerPage" :page="currentPage" :totalCount="100" @navigate="clickNavigate" /> `; export const Advanced: Story = { ...Default, args: { hasNext: true, size: 20, totalCount: 100, }, parameters: storybookDocsParameter(advancedCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-pagination"> <nav :aria-label="computedAriaLabel" class="b-pagination__navigation" :class="navigationConditionalClasses()"> <bento-pagination-results-per-page v-if="!hidePageSize" class="b-pagination__results-per-page" :items-per-page="size" :total-count="totalCount" @select="onItemsPerPageChange" /> <bento-typography v-else-if="hasSlot('default')"><slot /></bento-typography> <div class="b-pagination__page-navigator"> <bento-pagination-context class="b-pagination__context" :total-pages="totalPages" :page="page" :total-count="totalCount" :virtual-scroll="virtualScroll" @page-selected="onPageSelected" /> <div class="b-pagination__divider"></div> <bento-pagination-controls class="b-pagination__controls" :has-next="hasNext" :page="page" :total-pages="totalPages" @navigate="navigate" /> </div> </nav> </div> </template> <script setup lang="ts"> import { computed, ref, toRefs, useAttrs, useSlots } from 'vue'; import { BentoTypography } from '@/components/typography'; import BentoPaginationResultsPerPage from './components/pagination-results-per-page/pagination-results-per-page.vue'; import BentoPaginationContext from './components/pagination-context/pagination-context.vue'; import BentoPaginationControls from './components/pagination-controls/pagination-controls.vue'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { useHasSlot } from '@/composables'; import type { BentoPaginationProps } from './pagination.types'; const props = withDefaults(defineProps<BentoPaginationProps>(), { ariaLabel: null, hasNext: null, hidePageSize: false, page: 1, size: 20, totalCount: null, virtualScroll: false, }); const emit = defineEmits<{ /** * Triggered when the navigation controls are clicked or the items per page is changed (i.e. when the arrow buttons to the * rightmost of the screen or either dropdown is updated). It indicates to which page it should go next and also the page size. */ (e: 'navigate', page: number, items?: number): void; /** * Triggered when the number of items per page is changed from the left hand dropdown. * It indicates the new number of items that will be displayed. */ (e: 'items-page', size: number): void; /** * Triggered when the navigation controls are clicked, whether the arrow buttons to the * right most of the screen or the dropdown on the right hand is updated. * It indicates to which page it should go next * (easily achieved using the [sync modifier](https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)) * e.g. `:page.sync="currentPage"` */ (e: 'update:page', page: number): void; /** * Triggered when the number of items per page is changed from the left hand dropdown. * It indicates the new number of items that will be displayed. * (easily achieved using the [sync modifier](https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)) * e.g. `:size.sync="itemsPerPage"` */ (e: 'update:size', size: number): void; }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const attrs = useAttrs(); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel } = toRefs(props); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, }); const totalPages = computed(() => !props.totalCount || !props.size ? null : Math.ceil(props.totalCount / props.size) ); const navigate = (pageArg: number, itemsPerPage?: number) => { if (props.hidePageSize) { emit('navigate', pageArg); } else { emit('navigate', pageArg, itemsPerPage ?? props?.size); } emit('update:page', pageArg); }; const onPageSelected = (pageNumber: number) => { navigate(pageNumber); }; const onItemsPerPageChange = (elements: number) => { emit('items-page', elements); emit('update:size', elements); // Always go to first page when the // number of items page changes navigate(1, elements); }; const navigationConditionalClasses = () => ({ 'b-pagination__navigation--only-page-navigator': !hasSlot('default') && props.hidePageSize, }); </script> <script lang="ts"> /** * Paginiation component to help users page through data sets. * * There are two ways of doing navigation in this component: * 1. (Basic) You tell the component if there is a next page (hasNext) * 2. (Enhanced) You tell the component: * - How many total items exist (totalCount) * - The max number of items that you show/fetch per page (size) * - In this case you should not set the hasNext props * * If you have the data available and opt to use the Enhanced way you get extra controls: * - Page dropdown selector * - Last page button * - "Showing 10 of 200 items" text * * @usage * import { BentoPagination } from '@adyen/bento-vue2'; * * export default { * components: { BentoPagination }, * template: ` * <bento-pagination :has-next="isNextPage" /> * `, * } */ export default {}; </script> <style lang="scss" scoped src="./pagination.scss" />
@@ -0,0 +1,34 @@
1
+ import { Canvas, Story, Meta } from '@storybook/blocks';
2
+ import * as ComponentStories from './payment-method.stories';
3
+
4
+ <Meta of={ComponentStories} />
5
+
6
+ # PaymentMethod
7
+
8
+ A payment method component that displays payment method types including an icon and a label.
9
+
10
+ <Canvas of={ComponentStories.Default} />
11
+
12
+ ## Use Cases
13
+
14
+ Use a PaymentMethod if:
15
+
16
+ - Displaying a list of available paymenet method types for a user to pick from.
17
+ - Display payment method types in a table cell.
18
+
19
+ ### Do not use
20
+
21
+ - Use to display just the payment icon, instead the UI Assets library's
22
+ [payment icons](https://adyen-ui-assets.netlify.app/?path=/story/packages-ui-assets-payment-method-icons--icons) for
23
+ this.
24
+
25
+ ## Types
26
+
27
+ To determine which payment method icon and label to display the component accepts any string but you can use the
28
+ exported `IconsEnum` enum from the `@adyen/ui-assets-payment-method-icons`.
29
+
30
+ <Canvas of={ComponentStories.Default} />
31
+
32
+ ## Resources
33
+
34
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=1938%3A28450&t=hf2vOPqcagtdibwS-1)
@@ -0,0 +1 @@
1
+ import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoPaymentMethod from './payment-method.vue'; const meta: Meta = { title: 'Payment method', component: BentoPaymentMethod, }; export default meta; type Story = StoryObj<typeof BentoPaymentMethod>; const defaultCode = ` <bento-payment-method type="ideal" label="iDeal" /> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoPaymentMethod }, props: Object.keys(argTypes), template: ` <bento-payment-method v-bind="args"/> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { type: 'ideal', label: 'iDEAL', }, parameters: storybookDocsParameter(defaultCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-payment-method"> <div class="b-payment-method__icon-container"> <img v-if="type !== 'Unknowncard' && !hasErroredOnImage" aria-hidden="true" class="b-payment-method__icon" :class="conditionalIconClasses" :alt="label" :src="paymentMethodIconSrc" @error="onSrcError" /> <unknown-card-icon v-else :svg-title="label" aria-hidden="true" /> </div> <div v-if="label" class="b-payment-method__label"> <span v-if="isSummaryGridLayout">{{ label }}</span> <bento-typography v-else el="span">{{ label }}</bento-typography> </div> </div> </template> <script setup lang="ts"> import { computed, defineComponent, inject, ref, toRef, watch } from 'vue'; import { BentoTypography } from '@/components/typography'; import { SUMMARY_GRID_LAYOUT_INJECTION_KEY } from '@/components/summary-grid/summary-grid.keys'; import { type BentoSummaryGridLayout } from '@/components/summary-grid'; import { usePaymentIcon } from './composables/use-payment-icon/use-payment-icon'; import type { BentoPaymentMethodProps } from './payment-method.types'; import UnknownCardIcon from '@adyen/ui-assets-payment-method-icons/vue/unknowncard'; const props = withDefaults(defineProps<BentoPaymentMethodProps>(), { label: null, small: false, type: 'Unknowncard', }); const type = toRef(props, 'type'); const hasErroredOnImage = ref(false); const paymentMethodIconSrc = usePaymentIcon(type); const conditionalIconClasses = computed(() => ({ 'b-payment-method__icon--small': props.small, })); /* == Summary-grid styles == */ const registerPaymentMethodInSummaryGrid: BentoSummaryGridLayout = inject( SUMMARY_GRID_LAYOUT_INJECTION_KEY, () => ({ isSummaryGridLayout: false, }) ); const { isSummaryGridLayout } = registerPaymentMethodInSummaryGrid(); /** * This funciton is fired if the payment image source is not valid and has thrown an error. */ const onSrcError = () => { hasErroredOnImage.value = true; }; // Reset the image error to be able to load the new payment type watch( () => type.value, (newType, oldType) => { if (newType !== oldType) { hasErroredOnImage.value = false; } } ); </script> <script lang="ts"> /** * A payment method type component to display the type's icon and label. * * @example * import { BentoPaymentMethod} from '@adyen/bento-vue2'; * * export default { * components: { BentoPaymentMethod }, * template: ` * <bento-payment-method type="ideal" /> * ` * } */ export default defineComponent({ name: 'bento-payment-method', }); </script> <style lang="scss" scoped src="./payment-method.scss" />
@@ -0,0 +1 @@
1
+ <template> <bento-button :class="conditionalClass" variant="tertiary" @click="$emit('click')"> <template #iconLeft> <cross-icon svg-title="dismiss" /> </template> </bento-button> </template> <script lang="ts"> import { computed, defineComponent } from 'vue'; import BentoButton from '@/components/button/button.vue'; import CrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; export default defineComponent({ name: 'bento-popover-dismiss-button', components: { BentoButton, CrossIcon }, props: { onImage: { type: Boolean, default: false, }, }, setup(props) { const conditionalClass = computed(() => ({ 'b-popover-dismiss-button--on-image': props.onImage })); return { // Values conditionalClass, }; }, }); </script> <style lang="scss" scoped src="./popover-dismiss-button.scss" />
@@ -0,0 +1 @@
1
+ <template> <img class="b-popover-image" :src="src" :alt="alt" /> </template> <script lang="ts"> import { defineComponent } from 'vue'; export default defineComponent({ name: 'bento-popover-image', props: { /** * URL to image. * For more details, see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-src */ src: { type: String, required: true }, /** * Explanatory text for the image */ alt: { type: String, required: true }, }, }); </script> <style lang="scss" scoped> .b-popover-image { height: 100%; object-fit: cover; width: 100%; } </style>
@@ -0,0 +1 @@
1
+ <template> <bento-typography :strongest="isStrongest" :variant="titleVariant" el="span"> {{ title }} </bento-typography> </template> <script lang="ts"> import { computed, defineComponent } from 'vue'; import { BentoTypography, BentoTypographyVariant } from '@/components/typography'; export default defineComponent({ components: { BentoTypography }, props: { /** * Indicates if the popover contains an image. */ isImageTitle: { type: Boolean, default: false }, /** * Indicates the title you want to give the popover. */ title: { type: String, default: null }, }, setup(props) { // Use strongest only when it has no image const isStrongest = computed(() => !props.isImageTitle); // If an image is present, change the variant from BODY to SUBTITLE and remove the "strongest" property of the title const titleVariant = computed(() => props.isImageTitle ? BentoTypographyVariant.SUBTITLE : BentoTypographyVariant.BODY ); return { // Values isStrongest, titleVariant, }; }, }); </script>
@@ -0,0 +1,89 @@
1
+ import { Canvas, Story, Meta } from '@storybook/blocks';
2
+ import * as ComponentStories from './popover.stories';
3
+
4
+ <Meta of={ComponentStories} />
5
+
6
+ # Popover
7
+
8
+ Popover is a contextual overlay container that is shown above other content and is triggered by user’s interaction with
9
+ control or interactive area.
10
+
11
+ <Canvas of={ComponentStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use popover when you need:
16
+
17
+ - Provide a piece of non-critical extra information (informative, educative).
18
+ - Provide contextual to trigger functionality.
19
+
20
+ ### Do not use
21
+
22
+ If you need to display simple, concise and static text information, use a tooltip instead.
23
+
24
+ ## Modifiers
25
+
26
+ ### Title with icon
27
+
28
+ <Canvas of={ComponentStories.WithHeader} />
29
+
30
+ Using the `title` prop and `headerIcon` slot we can create a header for the Popover.
31
+
32
+ ### Actions
33
+
34
+ <Canvas of={ComponentStories.WithActions} />
35
+
36
+ Using the `actions` prop we can provide an array of action objects (see the Button Actions story). The `actionsLayout`
37
+ prop can be used to define the layout of buttons and can take up one of the following values:
38
+
39
+ - `'buttons-end'`
40
+ - `'fill-container'`
41
+ - `'space-between'`
42
+ - `'vertical-stack'`
43
+ - `'buttons-start'`
44
+
45
+ ### Image
46
+
47
+ <Canvas of={ComponentStories.WithImage} />
48
+
49
+ Using the `image` slot you can set a header image.
50
+
51
+ ## Accessibility 🚧
52
+
53
+ NOTE: The focus trap within the popover does not function as it should right now, this issue is currently being worked
54
+ on.
55
+
56
+ ### Keyboard Interaction
57
+
58
+ Being an interactive element, the button (that toggles the visibility of the Popover) is reachable through keyboard
59
+ navigation in the logical navigation flow of the page. The following keys trigger the following actions:
60
+
61
+ | Keyboard interaction | Action |
62
+ | -------------------- | -------------------------------------------------------------------------------------- |
63
+ | `Space` | Activates the disclosure control and toggles the visibility of the disclosure content. |
64
+ | `Enter` | Activates the disclosure control and toggles the visibility of the disclosure content. |
65
+
66
+ ### Roles, States, and Properties
67
+
68
+ Adhering to the [WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-1.2/) standard we follow the
69
+ [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/#wai-ariaroles,states,andproperties)
70
+ documentation provided.
71
+
72
+ #### Please note that:
73
+
74
+ - The element that shows and hides the content has role button, e.g. use the `BentoButton` component.
75
+ - When the Popover is visible, the element with role button has `aria-expanded` set to `true`. When the content area
76
+ is hidden, it is set to `false`.
77
+ - When the Popover is visible, the popover needs to be identified by an `id` and the trigger element with role button
78
+ needs to have `aria-controls` set to the popover `id`. When the popover is hidden, the `aria-controls` attribute
79
+ needs to be set to `null`.
80
+
81
+ | ARIA attribute | is automatic? | fallback used | manual override |
82
+ | --------------- | ------------------------------------------------------------------------ | ------------- | --------------- |
83
+ | `role` | Yes, `dialog` role is set | - | - |
84
+ | `id` | No, needs to be set manually on the `bento-popover` | - | - |
85
+ | `aria-controls` | No, needs to be set on the trigger and equal the `id` set on the popover | - | - |
86
+
87
+ ## Resources
88
+
89
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/branch/xxUamzpDM5NlukATVD9JFT/Bento---Components?type=design&node-id=1913%3A27764&mode=design&t=cY18II3hTxXGJeoI-1)
@@ -0,0 +1 @@
1
+ import { onMounted, ref, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '../../utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import { BentoButton, BentoButtonActionsLayoutBasic } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { BentoPopoverImage } from './components/popover-image'; import BentoPopover from './popover.vue'; import InfoFilledIcon from '@adyen/ui-assets-icons-16/vue/info-filled'; import { BentoPopoverPositions } from './popover.types'; import { popoverImageSrcStub } from '../../__tests__/img-stub.json'; import './popover.stories.scss?module'; const DEFAULT_TEXT = 'Add your text here'; const TITLE_TEXT = 'Title'; const IMAGE_SRC = popoverImageSrcStub; const IMAGE_ALT = 'Example image'; const ID = 'popover-id'; const ACTIONS = [ { title: 'Primary', event: () => action('Primary')(), }, { title: 'Secondary', event: () => action('Secondary')(), }, ]; const DEFAULT_PROPS = { position: 'bottom-start', disableFocusTrap: true, default: DEFAULT_TEXT, }; export default { title: 'Popover', component: BentoPopover, parameters: { layout: 'centered', }, argTypes: { // Slots default: { defaultValue: '', description: 'Popover content', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text' }, }, headerIcon: { defaultValue: '', description: 'Vue Icon from the library @adyen/ui-assets-icons-16.', table: { type: { summary: 'VNode[]' }, }, control: { disable: true }, }, image: { defaultValue: '', description: 'Popover image container', table: { type: { summary: 'BentoPopoverImage' }, }, control: { disable: true }, }, // Enum props actionsLayout: { options: Object.values(BentoButtonActionsLayoutBasic), mapping: BentoButtonActionsLayoutBasic, control: { type: 'select', }, }, position: { options: Object.values(BentoPopoverPositions), mapping: BentoPopoverPositions, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoPopover>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoPopover, BentoTypography, }, props: Object.keys(argTypes), template: ` <div class="b-popover-story--default"> <bento-popover v-if="targetElement" :open="isOpen" v-bind="args" :targetElement="targetElement" @placement="onPlacementUpdate" > <bento-typography el="span"> {{args.default}} </bento-typography> </bento-popover> <bento-button ref="targetElement" @click="togglePopover" :aria-expanded="isOpen" aria-controls="${ID}" > Open popover </bento-button> </div> `, setup(props) { const args = isVue2 ? props : _args; const isOpen = ref(false); const targetElement = ref(null); const togglePopover = () => { isOpen.value = !isOpen.value; }; watch( () => args.open, (val: boolean) => { isOpen.value = val; } ); const show = () => action('input'); onMounted(() => (isOpen.value = args.open === undefined ? true : args.open)); const onPlacementUpdate = action('placement'); return { // Values args, isOpen, targetElement, // Methods onPlacementUpdate, show, togglePopover, }; }, }), args: { ...DEFAULT_PROPS, id: ID }, parameters: storybookDocsParameter(` <div> <bento-popover v-if="targetElement" id="${ID}" :open="isOpen" position="bottom-start" :targetElement="targetElement" > <bento-typography el="span"> ${DEFAULT_TEXT} </bento-typography> </bento-popover> <bento-button ref="targetElement" :aria-expanded="isOpen" @click="togglePopover" aria-controls="${ID}" > Open popover </bento-button> </div> `), }; export const WithHeader: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoPopover, BentoTypography, InfoFilledIcon, }, props: Object.keys(argTypes), setup(props) { const args = isVue2 ? props : _args; const isOpen = ref(false); const targetElement = ref(null); const togglePopover = () => { isOpen.value = !isOpen.value; }; watch( () => args.open, (val: boolean) => { isOpen.value = val; } ); const show = () => action('input'); const dismissPopover = () => { action('dismiss')(); isOpen.value = false; }; onMounted(() => (isOpen.value = args.open === undefined ? true : args.open)); return { // Values args, isOpen, targetElement, // Methods togglePopover, show, dismissPopover, }; }, template: ` <div class="b-popover-story--with-header"> <bento-popover v-if="targetElement" :open="isOpen" v-bind="args" :targetElement="targetElement" @dismiss="dismissPopover" > <template #headerIcon> <info-filled-icon /> </template> <bento-typography el="span"> {{args.default}} </bento-typography> </bento-popover> <bento-button ref="targetElement" @click="togglePopover" :aria-expanded="isOpen" aria-controls="${ID}" > Open popover </bento-button> </div> `, }), args: { ...DEFAULT_PROPS, title: TITLE_TEXT, id: ID }, parameters: storybookDocsParameter(` <div> <bento-popover v-if="targetElement" id="${ID}" :open="isOpen" title="${TITLE_TEXT}" position="bottom-start" :targetElement="targetElement" @dismiss="dismissPopover" > <template #headerIcon> <info-filled-icon /> </template> <bento-typography el="span"> ${DEFAULT_TEXT} </bento-typography> </bento-popover> <bento-button ref="targetElement" :aria-expanded="isOpen" @click="togglePopover" aria-controls="${ID}" > Open popover </bento-button> </div> `), }; export const WithActions: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoPopover, BentoTypography, InfoFilledIcon, }, props: Object.keys(argTypes), setup(props) { const args = isVue2 ? props : _args; const isOpen = ref(false); const targetElement = ref(null); const togglePopover = () => { isOpen.value = !isOpen.value; }; watch( () => args.open, (val: boolean) => { isOpen.value = val; } ); const show = () => action('input'); const dismissPopover = () => { action('dismiss')(); isOpen.value = false; }; onMounted(() => (isOpen.value = args.open === undefined ? true : args.open)); return { // Values args, isOpen, targetElement, // Methods togglePopover, show, dismissPopover, }; }, template: ` <div class="b-popover-story--with-actions"> <bento-popover v-if="targetElement" :open="isOpen" v-bind="args" :targetElement="targetElement" @dismiss="dismissPopover" > <bento-typography el="span"> {{args.default}} </bento-typography> </bento-popover> <bento-button ref="targetElement" @click="togglePopover" :aria-expanded="isOpen" aria-controls="${ID}" > Open popover </bento-button> </div> `, }), args: { ...DEFAULT_PROPS, title: TITLE_TEXT, actions: ACTIONS, id: ID, }, parameters: storybookDocsParameter(` <div> <bento-popover v-if="targetElement" id="${ID}" :open="isOpen" title="${TITLE_TEXT}" :actions="[ { title: 'Primary', event: () => action('Primary')(), }, { title: 'Secondary', event: () => action('Secondary')(), }, ]" position="bottom-start" :targetElement="targetElement" @dismiss="dismissPopover" > <template #headerIcon> <info-filled-icon /> </template> <bento-typography el="span"> ${DEFAULT_TEXT} </bento-typography> </bento-popover> <bento-button ref="targetElement" :aria-expanded="isOpen" @click="togglePopover" aria-controls="${ID}" > Open popover </bento-button> </div> `), }; export const WithImage: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoPopover, BentoPopoverImage, BentoTypography, InfoFilledIcon, }, props: Object.keys(argTypes), setup(props) { const args = isVue2 ? props : _args; const isOpen = ref(false); const targetElement = ref(null); const togglePopover = () => { isOpen.value = !isOpen.value; }; watch( () => args.open, (val: boolean) => { isOpen.value = val; } ); const show = () => action('input'); const dismissPopover = () => { action('dismiss')(); isOpen.value = false; }; onMounted(() => (isOpen.value = args.open === undefined ? true : args.open)); return { // Values args, isOpen, targetElement, // Methods togglePopover, show, dismissPopover, }; }, template: ` <div class="b-popover-story--with-image"> <bento-popover v-if="targetElement" :open="isOpen" v-bind="args" :targetElement="targetElement" @dismiss="dismissPopover" > <template #image> <bento-popover-image src="${IMAGE_SRC}" alt="${IMAGE_ALT}" /> </template> <bento-typography el="span"> {{args.default}} </bento-typography> </bento-popover> <bento-button ref="targetElement" @click="togglePopover" :aria-expanded="isOpen" aria-controls="${ID}" > Open popover </bento-button> </div> `, }), args: { ...DEFAULT_PROPS, title: TITLE_TEXT, actions: ACTIONS, id: ID, }, parameters: storybookDocsParameter(` <div> <bento-popover v-if="targetElement" id="${ID}" :open="isOpen" title="${TITLE_TEXT}" :actions="[ { title: 'Primary', event: () => action('Primary')(), }, { title: 'Secondary', event: () => action('Secondary')(), }, ]" position="bottom-start" :targetElement="targetElement" @dismiss="dismissPopover" > <template #image> <bento-popover-image src="${IMAGE_SRC}" alt="${IMAGE_ALT}" /> </template> <bento-typography el="span"> ${DEFAULT_TEXT} </bento-typography> </bento-popover> <bento-button ref="targetElement" :aria-expanded="isOpen" @click="togglePopover" aria-controls="${ID}" > Open popover </bento-button> </div> `), };