@adyen/bento-mcp 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/assets/components/accordion/accordion.docs.mdx +77 -0
- package/dist/assets/components/accordion/accordion.stories.ts +1 -0
- package/dist/assets/components/accordion/accordion.vue +1 -0
- package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -0
- package/dist/assets/components/accordion/components/accordion-item.vue +1 -0
- package/dist/assets/components/action-bar/action-bar.docs.mdx +103 -0
- package/dist/assets/components/action-bar/action-bar.stories.ts +1 -0
- package/dist/assets/components/action-bar/action-bar.vue +1 -0
- package/dist/assets/components/ai-tag/ai-tag.docs.mdx +75 -0
- package/dist/assets/components/ai-tag/ai-tag.stories.ts +1 -0
- package/dist/assets/components/ai-tag/ai-tag.vue +1 -0
- package/dist/assets/components/alert/alert.docs.mdx +106 -0
- package/dist/assets/components/alert/alert.stories.ts +1 -0
- package/dist/assets/components/alert/alert.vue +1 -0
- package/dist/assets/components/avatar/avatar.docs.mdx +44 -0
- package/dist/assets/components/avatar/avatar.stories.ts +1 -0
- package/dist/assets/components/avatar/avatar.vue +1 -0
- package/dist/assets/components/avatar/components/avatar-image/avatar-image.vue +1 -0
- package/dist/assets/components/button/button.docs.mdx +137 -0
- package/dist/assets/components/button/button.stories.ts +1 -0
- package/dist/assets/components/button/button.vue +1 -0
- package/dist/assets/components/button/components/ai-button/ai-button.docs.mdx +40 -0
- package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -0
- package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -0
- package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +169 -0
- package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -0
- package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -0
- package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +106 -0
- package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -0
- package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -0
- package/dist/assets/components/button/components/toggle-button/toggle-button.docs.mdx +81 -0
- package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -0
- package/dist/assets/components/button/components/toggle-button/toggle-button.vue +1 -0
- package/dist/assets/components/card/card.docs.mdx +166 -0
- package/dist/assets/components/card/card.stories.ts +1 -0
- package/dist/assets/components/card/card.vue +1 -0
- package/dist/assets/components/checkbox/checkbox.docs.mdx +116 -0
- package/dist/assets/components/checkbox/checkbox.stories.ts +1 -0
- package/dist/assets/components/checkbox/checkbox.vue +1 -0
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +134 -0
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -0
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -0
- package/dist/assets/components/chip/chip.docs.mdx +51 -0
- package/dist/assets/components/chip/chip.stories.ts +1 -0
- package/dist/assets/components/chip/chip.vue +1 -0
- package/dist/assets/components/click-outside/click-outside.docs.mdx +30 -0
- package/dist/assets/components/click-outside/click-outside.stories.ts +1 -0
- package/dist/assets/components/code-snippet/code-snippet.docs.mdx +73 -0
- package/dist/assets/components/code-snippet/code-snippet.stories.ts +1 -0
- package/dist/assets/components/code-snippet/code-snippet.vue +1 -0
- package/dist/assets/components/country/country.docs.mdx +61 -0
- package/dist/assets/components/country/country.stories.ts +1 -0
- package/dist/assets/components/country/country.vue +1 -0
- package/dist/assets/components/currency/currency.docs.mdx +38 -0
- package/dist/assets/components/currency/currency.stories.ts +1 -0
- package/dist/assets/components/currency/currency.vue +1 -0
- package/dist/assets/components/dashed-underline/dashed-underline.docs.mdx +57 -0
- package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -0
- package/dist/assets/components/dashed-underline/dashed-underline.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-cell/data-grid-cell.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-columns/data-grid-columns.vue +1 -0
- 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
- package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-lazy-load/data-grid-lazy-load.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.docs.mdx +61 -0
- package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -0
- package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.docs.mdx +56 -0
- package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -0
- package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.docs.mdx +35 -0
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.stories.ts +1 -0
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.vue +1 -0
- package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.docs.mdx +48 -0
- package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -0
- package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -0
- package/dist/assets/components/data-grid/data-grid.docs.mdx +963 -0
- package/dist/assets/components/data-grid/data-grid.stories.ts +1 -0
- package/dist/assets/components/data-grid/data-grid.vue +1 -0
- package/dist/assets/components/date/date.docs.mdx +49 -0
- package/dist/assets/components/date/date.stories.ts +1 -0
- package/dist/assets/components/date/date.vue +1 -0
- package/dist/assets/components/date-picker/date-picker.docs.mdx +69 -0
- package/dist/assets/components/date-picker/date-picker.stories.ts +1 -0
- package/dist/assets/components/date-picker/date-picker.vue +1 -0
- package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -0
- package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +168 -0
- package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -0
- package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -0
- package/dist/assets/components/divider/divider.docs.mdx +27 -0
- package/dist/assets/components/divider/divider.stories.ts +1 -0
- package/dist/assets/components/divider/divider.vue +1 -0
- package/dist/assets/components/draggable-handle/draggable-handle.docs.mdx +61 -0
- package/dist/assets/components/draggable-handle/draggable-handle.stories.ts +1 -0
- package/dist/assets/components/draggable-handle/draggable-handle.vue +1 -0
- package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -0
- package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -0
- package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -0
- package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -0
- package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -0
- package/dist/assets/components/dropdown/dropdown.docs.mdx +420 -0
- package/dist/assets/components/dropdown/dropdown.stories.ts +1 -0
- package/dist/assets/components/dropdown/dropdown.vue +1 -0
- package/dist/assets/components/empty-state/empty-state.docs.mdx +115 -0
- package/dist/assets/components/empty-state/empty-state.stories.ts +1 -0
- package/dist/assets/components/empty-state/empty-state.vue +1 -0
- 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
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -0
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -0
- package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.stories.ts +1 -0
- package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -0
- package/dist/assets/components/file-uploader/file-uploader.docs.mdx +117 -0
- package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -0
- package/dist/assets/components/file-uploader/file-uploader.vue +1 -0
- package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -0
- package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-button/all-filters-modal-button.vue +1 -0
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.docs.mdx +90 -0
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/base-filter-button/base-filter-button.vue +1 -0
- package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/date-filter/date-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/filter-bar-button/filter-bar-button.vue +1 -0
- package/dist/assets/components/filter-bar/components/filter-bar-button-group/filter-bar-button-group.vue +1 -0
- package/dist/assets/components/filter-bar/components/input-filter/input-filter.docs.mdx +18 -0
- package/dist/assets/components/filter-bar/components/input-filter/input-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.docs.mdx +18 -0
- package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/range-filter/range-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-dropdown/select-filter-dropdown.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/select-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.stories.ts +1 -0
- package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -0
- package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1005 -0
- package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -0
- package/dist/assets/components/filter-bar/filter-bar.vue +1 -0
- package/dist/assets/components/focus-trap/focus-trap.docs.mdx +10 -0
- package/dist/assets/components/focus-trap/focus-trap.stories.ts +1 -0
- package/dist/assets/components/focus-trap/focus-trap.vue +1 -0
- package/dist/assets/components/form-layout/components/form-layout-field-tip/form-layout-field-tip.vue +1 -0
- package/dist/assets/components/form-layout/components/form-layout-group/form-layout-group.vue +1 -0
- package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -0
- package/dist/assets/components/form-layout/form-layout.docs.mdx +452 -0
- package/dist/assets/components/form-layout/form-layout.stories.ts +1 -0
- package/dist/assets/components/form-layout/form-layout.vue +1 -0
- package/dist/assets/components/header/header.docs.mdx +85 -0
- package/dist/assets/components/header/header.stories.ts +1 -0
- package/dist/assets/components/header/header.vue +1 -0
- package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -0
- package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -0
- package/dist/assets/components/header-with-views/header-with-views.docs.mdx +103 -0
- package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -0
- package/dist/assets/components/header-with-views/header-with-views.vue +1 -0
- package/dist/assets/components/image/image.docs.mdx +34 -0
- package/dist/assets/components/image/image.stories.ts +1 -0
- package/dist/assets/components/image/image.vue +1 -0
- package/dist/assets/components/info-icon/info-icon.docs.mdx +57 -0
- package/dist/assets/components/info-icon/info-icon.stories.ts +1 -0
- package/dist/assets/components/info-icon/info-icon.vue +1 -0
- package/dist/assets/components/input-field/input-field.docs.mdx +141 -0
- package/dist/assets/components/input-field/input-field.stories.ts +1 -0
- package/dist/assets/components/input-field/input-field.vue +1 -0
- package/dist/assets/components/input-field-password/input-field-password.docs.mdx +292 -0
- package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -0
- package/dist/assets/components/input-field-password/input-field-password.vue +1 -0
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.docs.mdx +72 -0
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -0
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -0
- package/dist/assets/components/inspector/inspector.docs.mdx +118 -0
- package/dist/assets/components/inspector/inspector.stories.ts +1 -0
- package/dist/assets/components/inspector/inspector.vue +1 -0
- package/dist/assets/components/internal/base-button/base-button.vue +1 -0
- package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +1 -0
- package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +1 -0
- package/dist/assets/components/internal/calendar/calendar.stories.ts +1 -0
- package/dist/assets/components/internal/calendar/calendar.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +1 -0
- package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +1 -0
- package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +1 -0
- package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +1 -0
- package/dist/assets/components/internal/controls-group/controls-group.vue +1 -0
- package/dist/assets/components/internal/copy/copy.docs.mdx +43 -0
- package/dist/assets/components/internal/copy/copy.stories.ts +1 -0
- package/dist/assets/components/internal/copy/copy.vue +1 -0
- package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +1 -0
- package/dist/assets/components/internal/dialog-page/dialog-page.vue +1 -0
- package/dist/assets/components/internal/error-message/error-message.vue +1 -0
- package/dist/assets/components/internal/field-label/field-label.vue +1 -0
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +21 -0
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +1 -0
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +1 -0
- package/dist/assets/components/internal/footer-actions/footer-actions.vue +1 -0
- package/dist/assets/components/internal/grid-layout/grid-layout.vue +1 -0
- package/dist/assets/components/internal/header-meta/header-meta.vue +1 -0
- package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +1 -0
- 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
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +1 -0
- package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +1 -0
- package/dist/assets/components/internal/listbox/listbox.stories.ts +1 -0
- package/dist/assets/components/internal/listbox/listbox.vue +1 -0
- package/dist/assets/components/internal/radio-input/radio-input.stories.ts +1 -0
- package/dist/assets/components/internal/radio-input/radio-input.vue +1 -0
- package/dist/assets/components/internal/teleport/teleport.vue +1 -0
- package/dist/assets/components/internal/tooltip/tooltip.test.vue +1 -0
- package/dist/assets/components/internal/tooltip/tooltip.vue +1 -0
- package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -0
- package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -0
- package/dist/assets/components/layout/layout.docs.mdx +108 -0
- package/dist/assets/components/layout/layout.stories.ts +1 -0
- package/dist/assets/components/layout/layout.vue +1 -0
- package/dist/assets/components/link/link.docs.mdx +119 -0
- package/dist/assets/components/link/link.stories.ts +1 -0
- package/dist/assets/components/link/link.vue +1 -0
- package/dist/assets/components/loading-indicator/loading-indicator.docs.mdx +65 -0
- package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -0
- package/dist/assets/components/loading-indicator/loading-indicator.vue +1 -0
- package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -0
- package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -0
- package/dist/assets/components/menu/components/menu-list-container/menu-list-container.vue +1 -0
- package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -0
- package/dist/assets/components/menu/menu.docs.mdx +121 -0
- package/dist/assets/components/menu/menu.stories.ts +1 -0
- package/dist/assets/components/menu/menu.vue +1 -0
- package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -0
- package/dist/assets/components/modal/components/modal-default/modal-default.vue +1 -0
- package/dist/assets/components/modal/components/modal-dialog/modal-dialog.vue +1 -0
- package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -0
- package/dist/assets/components/modal/modal.docs.mdx +215 -0
- package/dist/assets/components/modal/modal.stories.ts +1 -0
- package/dist/assets/components/modal/modal.vue +1 -0
- package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -0
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +217 -0
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -0
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -0
- package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -0
- package/dist/assets/components/pagination/components/pagination-controls/pagination-controls.vue +1 -0
- package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -0
- package/dist/assets/components/pagination/pagination.docs.mdx +116 -0
- package/dist/assets/components/pagination/pagination.stories.ts +1 -0
- package/dist/assets/components/pagination/pagination.vue +1 -0
- package/dist/assets/components/payment-method/payment-method.docs.mdx +34 -0
- package/dist/assets/components/payment-method/payment-method.stories.ts +1 -0
- package/dist/assets/components/payment-method/payment-method.vue +1 -0
- package/dist/assets/components/popover/components/popover-dismiss-button/popover-dismiss-button.vue +1 -0
- package/dist/assets/components/popover/components/popover-image/popover-image.vue +1 -0
- package/dist/assets/components/popover/components/popover-title/popover-title.vue +1 -0
- package/dist/assets/components/popover/popover.docs.mdx +89 -0
- package/dist/assets/components/popover/popover.stories.ts +1 -0
- package/dist/assets/components/popover/popover.vue +1 -0
- package/dist/assets/components/popper-container/popper-container.docs.mdx +11 -0
- package/dist/assets/components/popper-container/popper-container.stories.ts +1 -0
- package/dist/assets/components/popper-container/popper-container.vue +1 -0
- package/dist/assets/components/promo-banner/promo-banner.docs.mdx +94 -0
- package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -0
- package/dist/assets/components/promo-banner/promo-banner.vue +1 -0
- package/dist/assets/components/radio-group/components/radio-button/radio-button.docs.mdx +76 -0
- package/dist/assets/components/radio-group/components/radio-button/radio-button.stories.ts +1 -0
- package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -0
- package/dist/assets/components/radio-group/radio-group.docs.mdx +154 -0
- package/dist/assets/components/radio-group/radio-group.stories.ts +1 -0
- package/dist/assets/components/radio-group/radio-group.vue +1 -0
- package/dist/assets/components/rich-text-editor/components/rich-text-editor-content-area/rich-text-editor-content-area.vue +1 -0
- 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
- 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
- package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-link/rich-text-editor-link.vue +1 -0
- 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
- package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-table/rich-text-editor-table.vue +1 -0
- package/dist/assets/components/rich-text-editor/rich-text-editor.docs.mdx +52 -0
- package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -0
- package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -0
- package/dist/assets/components/search-bar/search-bar.docs.mdx +117 -0
- package/dist/assets/components/search-bar/search-bar.stories.ts +1 -0
- package/dist/assets/components/search-bar/search-bar.vue +1 -0
- package/dist/assets/components/secondary-nav/components/secondary-nav-category/secondary-nav-category.vue +1 -0
- package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -0
- package/dist/assets/components/secondary-nav/secondary-nav.docs.mdx +40 -0
- package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -0
- package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -0
- package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -0
- package/dist/assets/components/segmented-control/segmented-control.docs.mdx +90 -0
- package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -0
- package/dist/assets/components/segmented-control/segmented-control.vue +1 -0
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +119 -0
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -0
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -0
- package/dist/assets/components/selection-card/selection-card.docs.mdx +108 -0
- package/dist/assets/components/selection-card/selection-card.stories.ts +1 -0
- package/dist/assets/components/selection-card/selection-card.vue +1 -0
- package/dist/assets/components/sidepanel/components/sidepanel-page.stories.ts +1 -0
- package/dist/assets/components/sidepanel/components/sidepanel-page.vue +1 -0
- package/dist/assets/components/sidepanel/sidepanel.docs.mdx +129 -0
- package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -0
- package/dist/assets/components/sidepanel/sidepanel.vue +1 -0
- package/dist/assets/components/status/status.docs.mdx +62 -0
- package/dist/assets/components/status/status.stories.ts +1 -0
- package/dist/assets/components/status/status.vue +1 -0
- package/dist/assets/components/stepper/components/step/step.stories.ts +1 -0
- package/dist/assets/components/stepper/components/step/step.vue +1 -0
- package/dist/assets/components/stepper/stepper.docs.mdx +127 -0
- package/dist/assets/components/stepper/stepper.stories.ts +1 -0
- package/dist/assets/components/stepper/stepper.vue +1 -0
- package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.stories.ts +1 -0
- package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.vue +1 -0
- package/dist/assets/components/structured-list/structured-list.docs.mdx +68 -0
- package/dist/assets/components/structured-list/structured-list.stories.ts +1 -0
- package/dist/assets/components/structured-list/structured-list.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.stories.ts +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.stories.ts +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.stories.ts +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.stories.ts +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.vue +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.stories.ts +1 -0
- package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.vue +1 -0
- package/dist/assets/components/summary-grid/summary-grid.docs.mdx +152 -0
- package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -0
- package/dist/assets/components/summary-grid/summary-grid.vue +1 -0
- package/dist/assets/components/tabs/components/tab.vue +1 -0
- package/dist/assets/components/tabs/tabs.docs.mdx +98 -0
- package/dist/assets/components/tabs/tabs.stories.ts +1 -0
- package/dist/assets/components/tabs/tabs.vue +1 -0
- package/dist/assets/components/tag/tag.docs.mdx +137 -0
- package/dist/assets/components/tag/tag.stories.ts +1 -0
- package/dist/assets/components/tag/tag.vue +1 -0
- package/dist/assets/components/textarea/textarea.docs.mdx +53 -0
- package/dist/assets/components/textarea/textarea.stories.ts +1 -0
- package/dist/assets/components/textarea/textarea.vue +1 -0
- package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -0
- package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -0
- package/dist/assets/components/timeline/timeline.docs.mdx +131 -0
- package/dist/assets/components/timeline/timeline.stories.ts +1 -0
- package/dist/assets/components/timeline/timeline.vue +1 -0
- package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -0
- package/dist/assets/components/toast/components/toast-shadow-stack/toast-shadow-stack.vue +1 -0
- package/dist/assets/components/toast/toast.docs.mdx +151 -0
- package/dist/assets/components/toast/toast.stories.ts +1 -0
- package/dist/assets/components/toast/toast.vue +1 -0
- package/dist/assets/components/toggle/toggle.docs.mdx +81 -0
- package/dist/assets/components/toggle/toggle.stories.ts +1 -0
- package/dist/assets/components/toggle/toggle.vue +1 -0
- package/dist/assets/components/tutorial/components/tutorial-popover/components/tutorial-indicator/tutorial-indicator.vue +1 -0
- package/dist/assets/components/tutorial/components/tutorial-popover/tutorial-popover.vue +1 -0
- package/dist/assets/components/tutorial/tutorial.docs.mdx +85 -0
- package/dist/assets/components/tutorial/tutorial.stories.ts +1 -0
- package/dist/assets/components/tutorial/tutorial.vue +1 -0
- package/dist/assets/components/typography/typography.docs.mdx +124 -0
- package/dist/assets/components/typography/typography.stories.ts +1 -0
- package/dist/assets/components/typography/typography.vue +1 -0
- package/dist/assets/composables/use-bento-currency/use-bento-currency.docs.mdx +28 -0
- package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -0
- package/dist/assets/composables/use-click-outside/use-click-outside.docs.mdx +10 -0
- package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -0
- package/dist/assets/directives/highlight/highlight.docs.mdx +64 -0
- package/dist/assets/directives/highlight/highlight.stories.ts +1 -0
- package/dist/assets/directives/keyboard-navigation/keyboard-navigation.docs.mdx +41 -0
- package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -0
- package/dist/assets/directives/tooltip/tooltip.docs.mdx +86 -0
- package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -0
- package/dist/assets/index.ts +1 -0
- package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -0
- package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.vue +1 -0
- package/dist/assets/templates/dashboard-template/dashboard-template.docs.mdx +148 -0
- package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -0
- package/dist/assets/templates/dashboard-template/dashboard-template.vue +1 -0
- package/dist/assets/templates/data-grid-template/data-grid-template.docs.mdx +117 -0
- package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -0
- package/dist/assets/templates/data-grid-template/data-grid-template.vue +1 -0
- package/dist/main.js +30051 -0
- package/package.json +46 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div class="b-selection-card" :class="conditionalClasses" @click="handleCardClick"> <div class="b-selection-card__container" :class="containerConditionalClasses"> <div class="b-selection-card__checkbox-container" :class="checkboxContainerConditionalClasses"> <div class="b-selection-card__checkbox-container-text" :class="checkboxContainerTextConditionalClasses"> <div v-if="hasSlot('icon')" class="b-selection-card__icon" role="presentation" aria-hidden="true"> <slot name="icon" /> </div> <label :for="inputId" class="b-selection-card__text" @click.stop.prevent="handleCardClick"> <bento-typography v-if="subtitle" class="b-selection-card__description" el="span" variant="caption" > {{ subtitle }} </bento-typography> <bento-typography el="span" variant="title">{{ title }}</bento-typography> <bento-typography v-if="description" id="descriptionId" class="b-selection-card__description" el="span" variant="body" > {{ description }} </bento-typography> </label> </div> <radio-input v-if="isRadioInput" :id="inputId" ref="inputRef" :aria-label="ariaLabel" :aria-describedby="description ? descriptionId : undefined" :disabled="disabled" :has-error="hasError" :aria-readonly="readonly ?? undefined" :value="value" :readonly="readonly" :model-value="computedRadioInputModelValue" /> <checkbox-input v-else :id="inputId" ref="inputRef" :aria-label="ariaLabel" :aria-describedby="description ? descriptionId : undefined" :disabled="disabled" :has-error="hasError" :aria-readonly="readonly ?? undefined" :value="value" :readonly="readonly" :model-value="computedCheckboxInputModelValue" /> </div> <bento-typography v-if="hasSlot('content')" ref="slotRef" el="div" class="b-selection-card__slot" :class="slotConditionalClasses" > <slot name="content" /> </bento-typography> <bento-typography v-if="hasSlot('dynamicContent') && isChecked" ref="dynamicSlotRef" el="div" class="b-selection-card__slot" :class="slotConditionalClasses" > <slot name="dynamicContent" /> </bento-typography> </div> </div> </template> <script setup lang="ts"> import { BentoTypography } from '@/components/typography'; import { type BentoCheckboxType, type BentoMultiCheckboxType, CheckboxInput } from '../internal/checkbox-input'; import { RadioInput, type RadioInputValue } from '../internal/radio-input'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { generateUid } from '@/core/utils/ts'; import { computed, type ComputedRef, inject, type PropType, type Ref, ref, useSlots } from 'vue'; import { useHasSlot } from '@/composables'; import type { BentoSelectionCardModelValue, BentoSelectionCardValue } from './selection-card.types'; import { BentoSelectionCardState } from './selection-card.types'; import { SELECTION_CARD_RADIO_INPUT_INJECTION_KEY } from './selection-card.keys'; const props = defineProps({ /** * The description of the selection card */ description: { type: String, default: undefined, }, /** * Disables the selection card */ disabled: { type: Boolean, default: false }, /** * Makes the entire selection card clickable. Clicking over interactive elements inside the card with this prop enabled will not be possible. */ fullyClickable: { type: Boolean, default: false }, /** * Shows the selection card in error state */ hasError: { type: Boolean, default: false }, /** * This property should not be used directly. * It will be used by v-model directive on selection card component. * * When handling a single input, use a string or a number. * * When handling multiple inputs, use an array of keys. * These keys exist as a set "value" prop for each input. */ modelValue: { type: [Boolean, String, Array] as PropType<BentoSelectionCardModelValue>, default: null, }, /** * Sets the selection card to readonly */ readonly: { type: Boolean, default: false }, /** * The subtitle of the selection card */ subtitle: { type: String, default: undefined, }, /** * The title of the selection card */ title: { type: String, required: true, }, /** * Indicates the value of the input that will be returned when it's checked. */ value: { type: [String, Number] as PropType<BentoSelectionCardValue>, required: true, }, }); const emit = defineEmits<{ /** * Emitted when the selection card is clicked. Enables Vue2 "v-model" */ (e: 'input', value: BentoSelectionCardValue); /** * Emitted when the selection card is clicked. Enables Vue3 "v-model" */ (e: 'update:model-value', value: BentoSelectionCardValue); }>(); const slots = useSlots(); const hasSlot = useHasSlot(slots); const inputId = generateUid('selection-card-input-id'); const descriptionId = generateUid('selection-card-description'); const computedRadioInputModelValue: ComputedRef<RadioInputValue> = computed( () => props.modelValue as RadioInputValue ); const computedCheckboxInputModelValue: ComputedRef<BentoCheckboxType> = computed( () => props.modelValue as BentoCheckboxType ); const conditionalClasses = computed(() => ({ 'b-selection-card--checked': isChecked.value, 'b-selection-card--fully-clickable': props.fullyClickable, })); const containerConditionalClasses = computed(() => ({ [`b-selection-card--${BentoSelectionCardState.DISABLED}`]: props.disabled, [`b-selection-card--${BentoSelectionCardState.ERROR}`]: props.hasError, [`b-selection-card--${BentoSelectionCardState.READONLY}`]: props.readonly, 'b-selection-card--center-aligned': isTitleOnly.value, })); const checkboxContainerConditionalClasses = computed(() => ({ 'b-selection-card__checkbox-container--center-aligned': isTitleOnly.value, })); const checkboxContainerTextConditionalClasses = computed(() => ({ 'b-selection-card__checkbox-container-text--center-aligned': isTitleOnly.value, })); const slotConditionalClasses = computed(() => ({ 'b-selection-card__slot--with-icon': hasSlot('icon'), })); const ariaLabel = useAriaLabel({ ariaLabel: props.title, label: props.value as string, }); const isCheckboxGroup = computed(() => Array.isArray(props.modelValue)); const isChecked: ComputedRef<boolean> = computed(() => { if (isRadioInput.value) { return props.value === props.modelValue; } if (!isCheckboxGroup.value) { return props.modelValue as boolean; } return (props.modelValue as BentoMultiCheckboxType).some(item => item === props.value); }); const isRadioInput: Ref<boolean> = inject(SELECTION_CARD_RADIO_INPUT_INJECTION_KEY, ref(false)); const isTitleOnly = computed(() => !props.subtitle && !props.description); const inputRef = ref<InstanceType<typeof RadioInput> | InstanceType<typeof CheckboxInput>>(null); const slotRef = ref(null); const dynamicSlotRef = ref(null); // Handle click on the whole card area const handleCardClick = (event: Event) => { if (inputRef.value.$refs.inputRef === event.target) { const newValue = toggleInput(); onChange(newValue); return; } // Do nothing if disabled or readonly if (props.disabled && props.readonly) { return; } // If it's not fully clickable prevent selecting the card if the click // originated in a slot if (!props.fullyClickable) { // Check if the click originated from the slot - in that case, do nothng if (slotRef.value && slotRef.value?.$el?.contains(event.target)) { return; } // Check if the click originated from the dynamic slot - in that case, do nothng if (dynamicSlotRef.value && dynamicSlotRef.value?.$el?.contains(event.target)) { return; } // Check if the click originated from the input - in that case do nothing, as the input will emit the event already if (inputRef.value && inputRef.value?.$el?.contains(event.target as Node)) { return; } } // If the click originated outside of these two areas, emit the change event const newValue = toggleInput(); onChange(newValue); }; const toggleInput = (): BentoSelectionCardModelValue => { const currentValue: boolean = (inputRef.value?.$refs.inputRef as HTMLInputElement).checked; if (isCheckboxGroup.value) { const newCheckedList = [...(props.modelValue as BentoMultiCheckboxType)]; const index = newCheckedList.indexOf(props.value); if (index === -1) { newCheckedList.push(props.value); } else { newCheckedList.splice(index, 1); } return newCheckedList; } if (isRadioInput.value) { return props.value; } return currentValue; }; const onChange = newValue => { if (!props.disabled && !props.readonly) { emit('update:model-value', newValue); emit('input', newValue); } }; </script> <script lang="ts"> /** * Selection card component. If used standalone, only the checkbox variant can be used. If the checkbox variant is needed, please use the bento-selection-card-group component. * * @example * import { BentoSelectionCard } from '@adyen/bento-vue2'; * import EmoticonHappyIcon from '@adyen/ui-assets-icons-16/vue/emoticon-happy'; * * export default { * components: { BentoSelectionCard, EmoticonHappyIcon }, * template: ` * <bento-selection-card title="Card title" :model-value.sync="yourModelValue"> * <template #icon> * <emoticon-happy-icon /> * </template> * </bento-selection-card> * ` * } */ export default { name: 'bento-selection-card', }; </script> <style lang="scss" scoped src="./selection-card.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import BentoSidepanel from '../sidepanel.vue'; import BentoSidepanelPage from './sidepanel-page.vue'; import { BentoButton } from '@/components/button'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { ref, watch } from 'vue'; import { action } from '@storybook/addon-actions'; const meta: Meta = { title: 'Sidepanel/Sidepanel Page', component: BentoSidepanelPage, argTypes: { default: { name: 'default', type: { name: 'string', required: false }, description: 'Content of the sidepanel page', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoSidepanelPage>; const BUTTON_ACTIONS = [ { title: 'Save', event: () => action('save')(), }, { title: 'Cancel', event: () => action('cancel')(), }, ]; const DEFAULT_PROPS = { title: 'Intro', actions: BUTTON_ACTIONS, actionsLabel: 'Label', default: 'This is the content', destructiveActions: false, }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoSidepanel, BentoSidepanelPage }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = true" > Open sidepanel </bento-button> <bento-sidepanel @update:active-page="updatePage" :is-open="isOpen" @update:is-open='isOpen = $event'> <bento-sidepanel-page v-bind="args">{{ args.default }}</bento-sidepanel-page> </bento-sidepanel> </div> `, setup(props) { const isOpen = ref(false); const args = isVue2 ? props : _args; const updatePage = page => action('update:active-page')(page); watch( () => args.isOpen, () => { isOpen.value = args.isOpen; }, { immediate: true } ); return { args, isOpen, updatePage, }; }, }), args: { ...DEFAULT_PROPS }, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <dialog-page class="b-sidepanel-page" :page-id="pageId" :previous-page="previousPage"> <template #header> <div class="b-sidepanel-page__header"> <bento-typography class="b-sidepanel-page__title" el="h2" variant="title"> {{ title }} </bento-typography> </div> </template> <template #content> <div class="b-sidepanel-page__content"> <bento-typography el="div" variant="body"> <slot /> </bento-typography> </div> </template> <template v-if="actions && actions.length > 0" #footer> <div class="b-sidepanel-page__actions"> <footer-actions :actions="actions" :actions-label="actionsLabel" :destructive-actions="destructiveActions" /> </div> </template> </dialog-page> </template> <script setup lang="ts"> import { type PropType } from 'vue'; import { type BentoButtonActionsList } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { FooterActions } from '@/components/internal/footer-actions'; import { DialogPage } from '@/internal'; 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". * * ``` * [{ * title: 'Primary', * event: () => { ... }, * icon: FourPeopleIcon * }, { * title: 'Secondary', * event: () => { ... } * }] * ``` */ actions: { type: Array as PropType<BentoButtonActionsList>, default: null, }, /** * Text label that is displayed left to the action buttons */ actionsLabel: { type: String, default: null, }, /** * If passed - sets first button color to red */ destructiveActions: { type: Boolean, default: false, }, /** * Identifier of the page, used with activePage prop to handle sidepanel navigation */ pageId: { type: String, default: undefined, }, /** * ID of the page for the back button to navigate to. Only use it to overwrite the default back behavior * Used when the back button should not take the user to the natural previous page * Set to `null` to hide back button in the page */ previousPage: { type: String, default: undefined, }, /** * Title of the sidepanel page */ title: { type: String, default: null, }, }); </script> <script lang="ts"> /** * SidepanelPage is used to render different pages of Sidepanel component * * @example * import { BentoSidepanel, BentoSidepanelPage } from '@adyen/bento-vue2'; * * export default { * components: { BentoSidepanel, BentoSidepanelPage }, * template: ` * <bento-sidepanel active-page='step1' is-open='true'> * <bento-sidepanel-page * title='Step 1' * pageId='step1' * :actions="[ * { * title: 'Next step', * event: () => { * activePage.value = 'step2'; * }, * }]"> * Step 1 info * </bento-sidepanel-page> * <bento-sidepanel-page * title='Step 2' * pageId='step2' * :actions="[ * { * title: 'Save', * event: () => {}, * }]"> * Step 2 info * </bento-sidepanel-page> * </bento-sidepanel> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./sidepanel-page.scss" />
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Canvas, Meta } from '@storybook/blocks';
|
|
2
|
+
import * as SidepanelStories from './sidepanel.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={SidepanelStories} />
|
|
5
|
+
|
|
6
|
+
# Sidepanel
|
|
7
|
+
|
|
8
|
+
Sidepanel component is a slide-in panel that should be used for presenting actions, settings or features that are
|
|
9
|
+
directly associated with a specific task or interaction on the main screen.
|
|
10
|
+
|
|
11
|
+
It should always be triggered by button or icon-only button.
|
|
12
|
+
|
|
13
|
+
<Canvas of={SidepanelStories.OnePage} />
|
|
14
|
+
|
|
15
|
+
## Use Cases
|
|
16
|
+
|
|
17
|
+
Use Sidepanel component for presenting actions or settings of small to medium complexity that are directly associated
|
|
18
|
+
with the main screen.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
|
|
22
|
+
- Create new object
|
|
23
|
+
- Edit existing object
|
|
24
|
+
- Settings/Configuration
|
|
25
|
+
- Context related learning article
|
|
26
|
+
- Other similar use cases
|
|
27
|
+
|
|
28
|
+
### Do not use
|
|
29
|
+
|
|
30
|
+
For a small complexity tasks
|
|
31
|
+
|
|
32
|
+
- Rename
|
|
33
|
+
- Changing of only one setting
|
|
34
|
+
|
|
35
|
+
In those cases, use Modal component instead.
|
|
36
|
+
|
|
37
|
+
## Navigation
|
|
38
|
+
|
|
39
|
+
Sidepanel component supports deeper level navigation (user can go deeper in hierarchy and go back).
|
|
40
|
+
|
|
41
|
+
To handle navigation, every `sidepanel-page` should have a `pageId`, which can then be passed to the `activePage` prop
|
|
42
|
+
in `sidepanel` component to handle which page should be shown next.
|
|
43
|
+
|
|
44
|
+
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
|
|
45
|
+
to navigate to the previous page, we can set the `previousPage` to null, in order to hide the button, or to a different
|
|
46
|
+
`pageId`, to send the user to a different previous page.
|
|
47
|
+
|
|
48
|
+
<Canvas of={SidepanelStories.MultiplePages} />
|
|
49
|
+
|
|
50
|
+
## Anatomy
|
|
51
|
+
|
|
52
|
+
Each `sidepanel-page` component has three different sections:
|
|
53
|
+
|
|
54
|
+
### Header
|
|
55
|
+
|
|
56
|
+
The header sections can contain three different elements:
|
|
57
|
+
|
|
58
|
+
- Title (optional), this can be passed with the `title` prop.
|
|
59
|
+
- Back button (optional), if the user has navigated deeper in the hierarchy and `previousPage` is not set to null.
|
|
60
|
+
- Close button (optional), shows on the right top corner of the page if `isDismissible` is set to `true`.
|
|
61
|
+
|
|
62
|
+
### Content area
|
|
63
|
+
|
|
64
|
+
This is a slot and can be anything. Look up the [Use Cases](#use-cases) section to better understand the content that
|
|
65
|
+
should be present in this section.
|
|
66
|
+
|
|
67
|
+
### Footer
|
|
68
|
+
|
|
69
|
+
The footer will only show if `actions` prop is passed. Additionally, it can also contain a `actionLabel` and the actions
|
|
70
|
+
can be set to destructive (in case, there's, for example, a `Delete` button) with the `destructiveActions` prop.
|
|
71
|
+
|
|
72
|
+
## Properties
|
|
73
|
+
|
|
74
|
+
This is the list of properties, applicable to `bento-sidepanel`
|
|
75
|
+
|
|
76
|
+
| Property Name | Description |
|
|
77
|
+
| ----------------- | -------------------------------------------------------------------------------------- |
|
|
78
|
+
| `isOpen` | Property that controls if the sidepanel is open or closed |
|
|
79
|
+
| `activePage` | The id of the current active page, the `bento-sidepanel-page` the user wants displayed |
|
|
80
|
+
| `isDismissible` | If passed - makes it possible to close the sidepanel by clicking outside |
|
|
81
|
+
| `hideCloseButton` | Controls the visibility of close button on the sidepanel. |
|
|
82
|
+
|
|
83
|
+
This is the list of properties, applicable to `bento-sidepanel-page`
|
|
84
|
+
|
|
85
|
+
| Property Name | Description |
|
|
86
|
+
| -------------------- | -------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `pageId` | The id of the page, used to control navigation |
|
|
88
|
+
| `previousPage` | Can be set to null to hide the back button, or to a `pageId` to override the normal behavior |
|
|
89
|
+
| `title` | Title of the page |
|
|
90
|
+
| `actions` | List of actions that will be used to render the buttons |
|
|
91
|
+
| `actionsLabel` | Text label that is displayed left to the action buttons |
|
|
92
|
+
| `destructiveActions` | If passed - sets first button color to red |
|
|
93
|
+
|
|
94
|
+
### isDismissible vs hideCloseButton
|
|
95
|
+
|
|
96
|
+
The `isDismissible` property determines whether the sidepanel can be closed by clicking outside of the panel.
|
|
97
|
+
|
|
98
|
+
- When `isDismissible=true`, a close button is displayed by default.
|
|
99
|
+
- When `isDismissible=false`, a close button is hidden by default.
|
|
100
|
+
|
|
101
|
+
To independently control the visibility of the close button while `isDismissible` is set, use the `hideCloseButton`
|
|
102
|
+
property.
|
|
103
|
+
|
|
104
|
+
**Upcoming in v2.0.0:** The appearance of the close button will no longer depend on the `isDismissible` property.
|
|
105
|
+
Instead, you will have full control over its visibility using the `hideCloseButton` property. By default,
|
|
106
|
+
`hideCloseButton` will be set to `false`, meaning the close button will be shown regardless of the value of
|
|
107
|
+
`isDismissible`.
|
|
108
|
+
|
|
109
|
+
## Accessibility
|
|
110
|
+
|
|
111
|
+
Sidepanel uses the native HTML `<dialog>` element.
|
|
112
|
+
|
|
113
|
+
It means that when Sidepanel is opened, then everything other than the `<dialog>` and its contents is rendered inert
|
|
114
|
+
using the `inert` attribute.
|
|
115
|
+
|
|
116
|
+
It indicates the browser to ignore all the elements with `inert` attribute applied.
|
|
117
|
+
|
|
118
|
+
### Keyboard interaction
|
|
119
|
+
|
|
120
|
+
- User can use `Tab` key to move focus to the trigger and use `Spacebar`/`Enter` to activate the sidepanel.
|
|
121
|
+
- The `Tab` should change focus between interactive elements in Sidepanel (if there are few). This behavior should only
|
|
122
|
+
exist when Sidepanel is open and should not extend outside of the Sidepanel window (focus trap).
|
|
123
|
+
- When user uses `Esc` button, Sidepanel should close and return focus to the trigger.
|
|
124
|
+
- _OR_ when user use tab key to move focus to the dismiss/close button and then use Spacebar/Enter key -> we close
|
|
125
|
+
the sidepanel and return focus to the trigger
|
|
126
|
+
|
|
127
|
+
## Resources
|
|
128
|
+
|
|
129
|
+
- [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=16389-6643&mode=design&t=2CFK9xp3h3zaSfYF-0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { ref, watch } from 'vue'; import { BentoButton } from '@/components/button'; import BentoSidepanel from './sidepanel.vue'; import BentoSidepanelPage from './components/sidepanel-page.vue'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import { type Meta, type StoryObj } from '@storybook/vue'; export default { title: 'Sidepanel', component: BentoSidepanel, argTypes: { default: { name: 'default', type: { name: 'string', required: false }, description: 'BentoSidepanelPage components', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoSidepanel>; const DEFAULT_PROPS = { isOpen: false, isDismissible: true, }; const onePageCode = ` <bento-sidepanel :is-open="isOpen" @update:is-open="isOpen = $event"> <bento-sidepanel-page title="One page" :actions="actionsArray"> This is the content of the sidepanel. </bento-sidepanel-page> </bento-sidepanel> `; export const OnePage: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoSidepanel, BentoSidepanelPage }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = true" > Open sidepanel </bento-button> <bento-sidepanel v-bind="args" :is-open="isOpen" @update:is-open='isOpen = false'> <bento-sidepanel-page title="Intro">This is my only page</bento-sidepanel-page> </bento-sidepanel> </div> `, setup(props) { const isOpen = ref(false); const args = isVue2 ? props : _args; watch( () => args.isOpen, () => { isOpen.value = args.isOpen; } ); return { args, isOpen, }; }, }), args: { ...DEFAULT_PROPS }, parameters: storybookDocsParameter(onePageCode), }; const multiplePagesCode = ` <bento-sidepanel :is-open="isOpen" :active-page="activePage" @update:is-open="isOpen = $event" @update:active-page="activePage = $event" > <bento-sidepanel-page pageId="step1" title="Step 1" :actions="actionsArray" actions-label="Label"> This is the content of step 1 of the sidepanel. With an label next to the actions. </bento-sidepanel-page> <bento-sidepanel-page pageId="step2" title="Step 2" :actions="actionsArray"> This is the content of step 2 of the sidepanel. </bento-sidepanel-page> <bento-sidepanel-page pageId="step3" title="Step 3" :actions="actionsArray" destructive-actions> This is the content of step 3 of the sidepanel. With the primary action as a destructive action. </bento-sidepanel-page> <bento-sidepanel-page pageId="step4" title="Step 4" :actions="actionsArray" :previous-page="null"> This is the content of step 4 of the sidepanel. With no back arrow showing. </bento-sidepanel-page> </bento-sidepanel> `; export const MultiplePages: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoSidepanel, BentoSidepanelPage }, props: Object.keys(argTypes), template: ` <div> <bento-button :aria-expanded="isOpen" @click="isOpen = true" > Open sidepanel </bento-button> <bento-sidepanel v-bind="args" :is-open="isOpen" :active-page="activePage" @update:active-page="activePage = $event" @update:is-open='isOpen = $event'> <bento-sidepanel-page v-bind="intro"> <p style="margin-top: 0;">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> </bento-sidepanel-page> <bento-sidepanel-page v-bind="newProfile" > This page has some info about creating a new profile </bento-sidepanel-page> <bento-sidepanel-page v-bind="editProfile" > This is where you edit your profile </bento-sidepanel-page> <bento-sidepanel-page v-bind="newProfileForm"> This is where you create a new profile </bento-sidepanel-page> <bento-sidepanel-page v-bind="success"> Your profile is created </bento-sidepanel-page> </bento-sidepanel> </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'; }, }, ], actionsLabel: '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; action('cancel')(); }, }, ], }; const newProfileForm = { pageId: 'newProfileForm', title: 'New profile form', actions: [ { title: 'Save', event: () => { activePage.value = 'success'; }, }, { title: 'Cancel', event: () => { isOpen.value = false; action('cancel')(); }, }, ], }; const success = { pageId: 'success', title: 'Success!', destructiveActions: true, actions: [ { title: 'Delete', event: () => { isOpen.value = false; action('delete')(); }, }, { title: 'Cancel', event: () => { isOpen.value = false; action('cancel')(); }, }, ], previousPage: null, }; return { args, activePage, intro, newProfile, newProfileForm, success, editProfile, isOpen, }; }, }), args: { ...DEFAULT_PROPS, activePage: 'intro' }, parameters: storybookDocsParameter(multiplePagesCode), };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <dialog ref="sidepanel" class="b-sidepanel" :class="conditionalClasses" @animationend="closeSidepanelAfterAnimation" @keydown.esc.prevent="isDismissible && startCloseSidepanelAnimation" > <bento-focus-trap :id="sidepanelId" v-bento-click-outside-directive="closeSidepanelOnClickOutside"> <div v-if="!computedHideCloseButton" class="b-sidepanel__close-button"> <bento-button variant="tertiary" @click="startCloseSidepanelAnimation"> <cross-icon :svg-title="t('close')" /> </bento-button> </div> <slot /> </bento-focus-trap> </dialog> </template> <script setup lang="ts"> import { computed, nextTick, provide, ref, toRefs, watch } from 'vue'; import { BentoButton } from '@/components/button'; import { BentoFocusTrap } from '@/components/focus-trap'; import CrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; import { BentoSidepanelEvent } from './sidepanel.types'; import { BentoDialogPageAnimation, type BentoDialogPageConfigData, type BentoDialogPageRegisterPage, DIALOG_PAGE_CONFIG_INJECTION_KEY, DIALOG_PAGE_REGISTER_PAGE_INJECTION_KEY, } from '@/internal'; import { SIDEPANEL_DIALOG_ID_INJECTION_KEY } from './sidepanel.keys'; import { BentoClickOutsideDirective as vBentoClickOutsideDirective } from '@/directives/click-outside'; import { useI18n } from '@/utils/ts/i18n'; import messages from './messages.json'; import { generateUid } from '@/core/utils/ts'; import { deprecate } from '@/utils/ts/deprecate'; type MessageSchema = (typeof messages)['en-US']; const props = defineProps({ /** * The id of the current active page * Should be the same id as the BentoSidepanelPage to be displayed * If the component only has one page, it can be left null/undefined. */ activePage: { type: String, default: null, }, /** * If set to true, it allows the sidepanel to be closed. * Shows close button when `true` and hides close button when `false`. * * _Note: This property will not set the visibility of the close button in v2.0.0_ */ isDismissible: { type: Boolean, default: false, }, /** * Controls the sidepanel's state from the outside. Set it to true to open the sidepanel. */ isOpen: { type: Boolean, default: null, }, /** * Controls the visibility of close button on the sidepanel. * Defaults to `true` when `isDismissible=false`, and `true` when `isDimissible=true`. * * _Note: This property will be set to `false` by default, independent of `isDismissible` in v2.0.0_ */ hideCloseButton: { type: Boolean, default: null, }, }); const emit = defineEmits<{ /** * Emits update event when page is changed internally */ (e: BentoSidepanelEvent.UPDATE_ACTIVE_PAGE, page: string): void; /** * Emits close-sidepanel event when the sidepanel is closed */ (e: BentoSidepanelEvent.UPDATE_IS_OPEN, isOpen: boolean): void; }>(); // Refs const { activePage, isOpen } = toRefs(props); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const sidepanel = ref(null); const pages = ref<Array<string>>([]); const previousPages = ref([]); const animation = ref(BentoDialogPageAnimation.TRANSITION_LEVEL_DEEPER); const isClosing = ref(false); const sidepanelId = generateUid('sidepanel'); const internalIsOpen = ref(false); const isAnimationActive = ref(false); // Animation styling const conditionalClasses = computed(() => ({ 'b-sidepanel--hide': isClosing.value, 'b-sidepanel--animation': isAnimationActive.value, })); // Handle closing and closing animation for dialog element const closeSidepanelAfterAnimation = () => { if (isClosing.value) { sidepanel.value.close(); if (previousPages.value[0]) { emit(BentoSidepanelEvent.UPDATE_ACTIVE_PAGE, previousPages.value[0]); } previousPages.value = []; isClosing.value = false; internalIsOpen.value = false; emit(BentoSidepanelEvent.UPDATE_IS_OPEN, false); } }; const startCloseSidepanelAnimation = () => { if (!sidepanel.value.open) { return; } isClosing.value = true; }; const closeSidepanelOnClickOutside = () => { if (props.isDismissible) { startCloseSidepanelAnimation(); } }; // TODO: Remove in upcoming major. See properties `hideCloseButton` and `isDismissible`. const computedHideCloseButton = computed(() => { if (props.hideCloseButton !== null) { return props.hideCloseButton; } return !props.isDismissible; }); // Watching isOpen prop to toggle open/close the dialog element watch( () => isOpen.value, async () => { // Wait for sidepanel ref to be created await nextTick(); if (!sidepanel.value) { return; } if (isOpen.value) { internalIsOpen.value = true; sidepanel.value.showModal(); if (!previousPages.value.length) { previousPages.value.push(activePage.value); } } else if (sidepanel.value.open) { startCloseSidepanelAnimation(); } }, { immediate: true } ); // Pages const updatePage = page => emit(BentoSidepanelEvent.UPDATE_ACTIVE_PAGE, page); // 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 => { pages.value.push(pageId); return { unregisterPage: unregisterPage(pageId) }; }; provide<BentoDialogPageConfigData>(DIALOG_PAGE_CONFIG_INJECTION_KEY, { activePage, animation, isAnimationActive, isOpen: internalIsOpen, pages, previousPages, updatePage, }); provide(DIALOG_PAGE_REGISTER_PAGE_INJECTION_KEY, registerPage); // Let's Children components know they are inside a SidePanel (dialog element) provide(SIDEPANEL_DIALOG_ID_INJECTION_KEY, sidepanelId); if (!props.isDismissible && props.hideCloseButton === null) { deprecate( 'BentoSidepanel "isDismissible" property’s behavior', 'BentoSidepanel "isDismissible" will no longer hide the close button. Use "hideCloseButton=true" instead.', '2.0.0' ); } </script> <script lang="ts"> /** * Sidepanel component is a slide-in panel that should be used for presenting actions, settings or features that are directly associated with a specific task or interaction on the main screen. * * @example * import { BentoSidepanel, BentoSidepanelPage } from '@adyen/bento-vue2'; * * export default { * components: { BentoSidepanel, BentoSidepanelPage }, * template: ` * <bento-sidepanel active-page='step1' is-open='true'> * <bento-sidepanel-page * title='Step 1' * pageId='step1' * :actions="[ * { * title: 'Next step', * event: () => { * activePage.value = 'step2'; * }, * }]"> * Step 1 info * </bento-sidepanel-page> * <bento-sidepanel-page * title='Step 2' * pageId='step2' * :actions="[ * { * title: 'Save', * event: () => {}, * }]"> * Step 2 info * </bento-sidepanel-page> * </bento-sidepanel> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./sidepanel.scss" />
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Canvas, Story, Meta } from '@storybook/blocks';
|
|
2
|
+
import * as ComponentStories from './status.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={ComponentStories} />
|
|
5
|
+
|
|
6
|
+
# Status
|
|
7
|
+
|
|
8
|
+
<Canvas of={ComponentStories.Default} />
|
|
9
|
+
|
|
10
|
+
A status component is used to indicate a passage of time or a particular step of a process.
|
|
11
|
+
|
|
12
|
+
## Use Cases
|
|
13
|
+
|
|
14
|
+
Use the status component to indicate progression and when you need to label things such as:
|
|
15
|
+
|
|
16
|
+
- Payment processing steps (successful, refused, in progress...).
|
|
17
|
+
- How long ago a terminal has been used (1 day ago, 1 week ago, 1 month ago...).
|
|
18
|
+
|
|
19
|
+
### Do not use
|
|
20
|
+
|
|
21
|
+
It is strongly advised to not rely on the icon color only to mark progress, as users with color blindness may struggle
|
|
22
|
+
with discerning them. Instead, opt fot text conveying such progress so everyone can understand its meaning (e.g.
|
|
23
|
+
`Active today` instead of `Activity` paired with a green icon).
|
|
24
|
+
|
|
25
|
+
## Variations
|
|
26
|
+
|
|
27
|
+
### Variant
|
|
28
|
+
|
|
29
|
+
Defines the color of the component's icon and it can take up six different values:
|
|
30
|
+
|
|
31
|
+
- `'grey'`: renders the icon in the color grey.
|
|
32
|
+
- `'blue'`: renders the icon in the color blue.
|
|
33
|
+
- `'green'`: renders the icon in the color green.
|
|
34
|
+
- `'yellow'`: renders the icon in the color yellow.
|
|
35
|
+
- `'orange'`: renders the icon in the color orange.
|
|
36
|
+
- `'red'`: renders the icon in the color red.
|
|
37
|
+
|
|
38
|
+
## Modifiers
|
|
39
|
+
|
|
40
|
+
There are no available modifiers for this component.
|
|
41
|
+
|
|
42
|
+
## Accessibility
|
|
43
|
+
|
|
44
|
+
This component is marked with a
|
|
45
|
+
[`status` ARIA role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/status_role) so as to make
|
|
46
|
+
its purpose clear to assistive technology.
|
|
47
|
+
|
|
48
|
+
### Keyboard interaction
|
|
49
|
+
|
|
50
|
+
The status component is not reachable through keyboard navigation as it's a non-interactive element. A detailed list of
|
|
51
|
+
keyboard interactions is provided in the table below.
|
|
52
|
+
|
|
53
|
+
### Roles, states and properties
|
|
54
|
+
|
|
55
|
+
| ARIA attribute | is automatic? | fallback used | manual override |
|
|
56
|
+
| -------------- | ----------------------------------------- | ------------- | --------------- |
|
|
57
|
+
| `role` | Yes, `role="status"` set on the root div. | - | - |
|
|
58
|
+
|
|
59
|
+
## Resources
|
|
60
|
+
|
|
61
|
+
- [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=791-9659&t=668CkphganKdxlyp-0)
|
|
62
|
+
- [WAI-ARIA guidelines](https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA22)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { BentoStatusVariant } from './status.types'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoStatus from './status.vue'; const meta: Meta = { title: 'Status', component: BentoStatus, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'Default slot content', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, // Enum props variant: { options: Object.values(BentoStatusVariant), mapping: BentoStatusVariant, control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoStatus>; const DEFAULT_TEXT = 'Status'; const defaultCode = ` <bento-status variant="grey"> ${DEFAULT_TEXT} </bento-status> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoStatus }, props: Object.keys(argTypes), template: ` <bento-status v-bind="args"> {{args.default}} </bento-status> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { default: DEFAULT_TEXT, variant: 'grey', }, parameters: storybookDocsParameter(defaultCode), };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div class="b-status" role="status"> <div class="b-status__indicator-wrapper"> <span class="b-status__indicator" :class="conditionalClass" role="presentation"></span> </div> <bento-typography class="b-status__label" el="span" variant="body"> <slot></slot> </bento-typography> </div> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoTypography } from '@/components/typography'; import { type BentoStatusProps } from './status.types'; const props = defineProps<BentoStatusProps>(); const conditionalClass = computed(() => ({ [`b-status__indicator--${props.variant}`]: true, })); </script> <script lang="ts"> /** * A Status component is used to indicate a * passage of time or a particular step of a process. * * @example * import { BentoStatus } from '@adyen/bento-vue2'; * * export default { * components: { BentoStatus }, * template: ` * <bento-status variant="red"> * 1 week ago * </bento-status> * ` * } */ export default { name: 'bento-status', }; </script> <style lang="scss" scoped src="./status.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import BentoStep from './step.vue'; import BentoStepper from '../../stepper.vue'; import { isVue2 } from 'vue-demi'; import type { Meta, StoryObj } from '@storybook/vue'; const meta: Meta = { title: 'Stepper/Step', component: BentoStep, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'Default slot content', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, substeps: { name: 'substeps', type: { name: 'string', required: false }, description: 'Substeps slot', control: 'text', table: { type: { summary: 'BentoStep[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoStepper>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoStep, BentoStepper }, props: Object.keys(argTypes), template: ` <bento-stepper type="non-linear"> <bento-step v-bind="args">Step 1</bento-step> </bento-stepper> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: {}, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <li :class="conditionalClasses" class="b-step"> <base-button ref="buttonComponent" :aria-current="ariaCurrentValue" :aria-disabled="ariaDisabledValue" class="b-step__button" type="button" :tabindex="tabIndexValue" :disabled="isDisabled" @click="navigateToStep" @focus="isFocused = true" @blur="isFocused = false" > <template #iconLeft> <bento-typography el="span" variant="body" stronger class="b-step__index" aria-hidden="true"> <checkmark-small-icon v-if="isCompleted" /> <template v-else-if="!isSubstep">{{ index + 1 }}</template> <dot-icon v-else /> </bento-typography> </template> <bento-typography ref="stepContent" el="span" variant="body" :stronger="isActive" class="b-step__content"> <slot></slot> </bento-typography> </base-button> <ul v-show="isSubstepsVisible" ref="substepList" class="b-step__substep-list"> <slot name="substeps"></slot> </ul> </li> </template> <script setup lang="ts"> import { computed, inject, onBeforeUnmount, onMounted, provide, ref, useSlots, watch } from 'vue'; import { BaseButton } from '@/components/internal/base-button'; import { BentoTypography } from '@/components/typography'; import CheckmarkSmallIcon from '@adyen/ui-assets-icons-16/vue/checkmark-small'; import DotIcon from '@adyen/ui-assets-icons-16/vue/dot'; import { useStepRegister } from '../../composables'; import { STEPPER_STATE } from '../../stepper.keys'; import { type BentoStepperState, type BentoStepperStepExposeState, BentoStepperTypeOption, BentoStepperVariantOption, } from '../../stepper.types'; import { type Booleanish } from '@/types/prop-types'; const slots = useSlots(); const props = defineProps({ /** * Completed status. Used on non-linear steppers to indicate if the step is completed. */ completed: { type: Boolean, default: false, }, /** * Active substep index */ substepIndex: { type: Number, default: 0, }, }); const emit = defineEmits<{ /** * Event triggered when substep is updated by the parent step */ (e: 'update:substep-index', index: number): void; }>(); // Element refs const buttonComponent = ref<InstanceType<typeof BaseButton> | null>(null); const stepContent = ref<InstanceType<typeof BentoTypography> | null>(null); const substeps = ref<Array<BentoStepperStepExposeState>>([]); // Injects parent BentoStepper state data const stepper = inject<BentoStepperState>(STEPPER_STATE); const buttonElement = computed(() => buttonComponent.value?.$el as HTMLButtonElement); const index = computed(() => stepper?.value.steps.indexOf(buttonElement.value)); const stepContentValue = computed(() => stepContent.value?.$el.textContent); // Substep logic const substepsButtonElements = computed(() => substeps.value.map(({ buttonElement }) => buttonElement)); const activeSubStepIndex = ref(props.substepIndex); const latestActiveSubStepIndex = ref(activeSubStepIndex.value); const navigateToSubstep = (value: number) => { if (value > latestActiveSubStepIndex.value) { latestActiveSubStepIndex.value = value; } activeSubStepIndex.value = value; }; const { registerStep, removeStep } = useStepRegister(substeps); const stepState: BentoStepperState = ref({ activeStepIndex: activeSubStepIndex, latestActiveStep: latestActiveSubStepIndex, type: stepper.value.type, variant: stepper.value.variant, steps: substepsButtonElements, isParentStep: true, navigateToStep: navigateToSubstep, registerStep, removeStep, }); provide(STEPPER_STATE, stepState); watch(() => props.substepIndex, navigateToSubstep); watch(activeSubStepIndex, value => { emit('update:substep-index', value); }); onMounted(() => { if (!stepper) { throw new Error( 'BentoStep components should only be used inside BentoStepper or other BentoStep components.' ); } stepper.value.registerStep({ buttonElement, stepContent: stepContentValue, substeps, }); }); onBeforeUnmount(() => { stepper.value.removeStep({ buttonElement, stepContent: stepContentValue, substeps, }); }); // Step states const isSubstep = computed(() => stepper.value.isParentStep); const isFocused = ref(false); const isActive = computed(() => stepper?.value.activeStepIndex === index.value); const isSubstepsVisible = computed( () => isActive.value && stepper.value.variant !== BentoStepperVariantOption.HORIZONTAL && !isSubstep.value && slots.substeps ); const isCompleted = computed(() => { if (stepper?.value.type === BentoStepperTypeOption.NON_LINEAR) { return props.completed; } return stepper?.value.latestActiveStep > index.value; }); const isDisabled = computed( () => stepper?.value.type === BentoStepperTypeOption.LINEAR && index.value > stepper?.value.latestActiveStep ); const navigateToStep = () => { if (isDisabled.value) { return; } stepper.value.navigateToStep(index.value); }; // Styling and a11y const ariaCurrentValue = computed(() => (isActive.value ? 'step' : ('false' as Booleanish))); const ariaDisabledValue = computed(() => isDisabled.value.toString() as Booleanish); const tabIndexValue = computed(() => (isFocused.value || (index.value === 0 && !isSubstep.value) ? '0' : '-1')); const conditionalClasses = computed(() => ({ 'b-step--active': isActive.value, 'b-step--completed': isCompleted.value, 'b-step--substep': isSubstep.value, })); </script> <script lang="ts"> /** * Steps are components placed inside BentoSteppers to break down complex processes * * @example * import { BentoStep } from '@adyen/bento-vue2'; * * export default { * components: { BentoStep }, * template: ` * <bento-stepper> * <bento-step>Step 1</bento-step> * <bento-step> * Step 2 * <template #substeps> * <bento-step>Substep 1</bento-step> * <bento-step>Substep 2</bento-step> * </template> * </bento-step> * </bento-stepper> * ` * } */ export default { name: 'step', }; </script> <style lang="scss" scoped src="./step.scss" />
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Canvas, Meta, Story, Source } from '@storybook/blocks';
|
|
2
|
+
import * as StepperStories from './stepper.stories';
|
|
3
|
+
import ResetStepperExample from './__tests__/reset-stepper-example.vue?raw';
|
|
4
|
+
|
|
5
|
+
<Meta of={StepperStories} />
|
|
6
|
+
|
|
7
|
+
# Stepper
|
|
8
|
+
|
|
9
|
+
Steppers break down complex processes into manageable steps to guide users through a sequential or multi-step process.
|
|
10
|
+
|
|
11
|
+
<Canvas withSource="closed">
|
|
12
|
+
<Story of={StepperStories.Default} />
|
|
13
|
+
</Canvas>
|
|
14
|
+
|
|
15
|
+
## Use Cases
|
|
16
|
+
|
|
17
|
+
Use steppers when:
|
|
18
|
+
|
|
19
|
+
- You need to simplify the experience in lengthy forms into manageable steps.
|
|
20
|
+
- Users need to track progress through a process so that they understand where they are and what steps remain.
|
|
21
|
+
|
|
22
|
+
### Do not use
|
|
23
|
+
|
|
24
|
+
- Don’t use steppers to manage page navigation. Consider using `BentoTabs`.
|
|
25
|
+
|
|
26
|
+
- Don’t use steppers if users needs to read through all the content to fully understand a process or flow, or if they
|
|
27
|
+
need to compare information between them.
|
|
28
|
+
|
|
29
|
+
## Modifiers
|
|
30
|
+
|
|
31
|
+
### Type
|
|
32
|
+
|
|
33
|
+
There are two ways you can use your stepper:
|
|
34
|
+
|
|
35
|
+
1. Linear steppers
|
|
36
|
+
|
|
37
|
+
This stepper requires users to complete one step in order to move on to the next. Steps always follow a specific order
|
|
38
|
+
and users cannot skip a step.
|
|
39
|
+
|
|
40
|
+
2. Non-linear steppers
|
|
41
|
+
|
|
42
|
+
This stepper allow users to complete steps in random order. Users can navigate to any step they wish.
|
|
43
|
+
|
|
44
|
+
#### Using stepper with a loop
|
|
45
|
+
|
|
46
|
+
If you want to build your stepper from an array of navigation options, you can do so:
|
|
47
|
+
|
|
48
|
+
<Canvas withSource="open">
|
|
49
|
+
<Story of={StepperStories.ConditionalSteps} />
|
|
50
|
+
</Canvas>
|
|
51
|
+
|
|
52
|
+
This also also allows for conditional steps - simply update your array of steps, and the stepper component will update
|
|
53
|
+
accordingly.
|
|
54
|
+
|
|
55
|
+
#### Set the stepper programatically
|
|
56
|
+
|
|
57
|
+
If you need to set your stepper, you just need to add a `key` attribute to it and increase it.
|
|
58
|
+
|
|
59
|
+
<Source
|
|
60
|
+
dark
|
|
61
|
+
language="html"
|
|
62
|
+
code={`
|
|
63
|
+
<bento-stepper :key="stepKey">
|
|
64
|
+
...
|
|
65
|
+
</bento-stepper>
|
|
66
|
+
<div role="status">
|
|
67
|
+
A dynamic content such as computed(() => steps[activeIndex]) is needed here in order for the section to be announced by the screen reader on step change
|
|
68
|
+
</div>
|
|
69
|
+
`}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
#### Resetting stepper
|
|
73
|
+
|
|
74
|
+
In some cases, you may need to reset the stepper to its initial state (e.g., when a user wants to start a process over).
|
|
75
|
+
The `BentoStepper` component exposes a `reset()` method that sets both the active step and the maximum progress reached
|
|
76
|
+
back to zero.
|
|
77
|
+
|
|
78
|
+
<Source dark language="html" code={ResetStepperExample} />
|
|
79
|
+
|
|
80
|
+
## Accessibility
|
|
81
|
+
|
|
82
|
+
### Keyboard interaction
|
|
83
|
+
|
|
84
|
+
| Keyboard interaction | Action |
|
|
85
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `Tab` | Sets focus on the first step |
|
|
87
|
+
| `ArrowLeft` | Moves the focus to the previous step (to the left on horizontal stepper and upwards on vertical stepper) |
|
|
88
|
+
| `ArrowRight` | Moves the focus to the next step (to the right on horizontal stepper and down on vertical stepper) |
|
|
89
|
+
| `ArrowUp` | Moves the focus to the previous step (to the left on horizontal stepper and upwards on vertical stepper) |
|
|
90
|
+
| `ArrowDown` | Moves the focus to the next step (to the right on horizontal stepper and down on vertical stepper) |
|
|
91
|
+
| `Enter` | Activates the step. |
|
|
92
|
+
|
|
93
|
+
### Roles, states and properties
|
|
94
|
+
|
|
95
|
+
| Role | Attribute | Element | Usage |
|
|
96
|
+
| ------------ | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
97
|
+
| `navigation` | | `nav` | Indicates that the element contains a collection of navigational elements for navigating the document. |
|
|
98
|
+
| `button` | | `button` | Indicates that the element serves as a stepper control. |
|
|
99
|
+
| | `aria-disabled="true"` | `button` | <ul> <li> Indicates that the step control can't be selected.</li> <li>On a linear stepper, is set when the previous steps are not completed. </li></ul> |
|
|
100
|
+
| | `aria-disabled="false"` | `button` | Indicates that the step control can be selected. |
|
|
101
|
+
| | `tabindex="-1"` | `button` | <ul> <li> Removes the element from the page <kbd>Tab</kbd> sequence. </li> <li> Set on every step control, except the first one. </li> </ul> |
|
|
102
|
+
| | `aria-current="step"` | `button` | Indicates that the current step is active. |
|
|
103
|
+
| | `aria-current="false"` | `button` | Indicates that the current step is not active. |
|
|
104
|
+
|
|
105
|
+
### Screen reader
|
|
106
|
+
|
|
107
|
+
To ensure that the content of each step is announced by screen readers when the user navigates between steps, the
|
|
108
|
+
content should be rendered within an element with the `role` attribute set to `status`.
|
|
109
|
+
|
|
110
|
+
<Source
|
|
111
|
+
dark
|
|
112
|
+
language="html"
|
|
113
|
+
code={`
|
|
114
|
+
<template>
|
|
115
|
+
<div>
|
|
116
|
+
<bento-stepper :index="index" @update:index="index = $event">
|
|
117
|
+
<bento-step v-for="step in steps" :key="step">{{ step }}</bento-step>
|
|
118
|
+
</bento-stepper>
|
|
119
|
+
<div role="status">
|
|
120
|
+
A dynamic content such as computed(() => steps[activeIndex]) is needed here in order for the section to be announced by the screen reader on step change
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
`}
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
- [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=17186-91237&mode=design&t=d0AYyM2iVqBMrOI4-0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import BentoStepper from './stepper.vue'; import BentoStep from './components/step/step.vue'; import { BentoButton } from '@/components/button'; import { action } from '@storybook/addon-actions'; import { computed, isVue2, ref } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { Meta, StoryObj } from '@storybook/vue'; import { BentoStepperTypeOption, BentoStepperVariantOption } from './stepper.types'; import './stepper.stories.scss?module'; const meta: Meta = { title: 'Stepper', component: BentoStepper, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'BentoStep components', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, // Enum props type: { options: Object.values(BentoStepperTypeOption), mapping: BentoStepperTypeOption, control: { type: 'select', }, }, variant: { options: Object.values(BentoStepperVariantOption), mapping: BentoStepperVariantOption, control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoStepper>; export const Default: Story = { render(_args, { argTypes }) { return { components: { BentoStepper, BentoStep }, props: Object.keys(argTypes), template: ` <bento-stepper v-bind="args" :key="JSON.stringify(args)" @update:index="updateIndex"> <bento-step>Step 1</bento-step> <bento-step>Step 2</bento-step> <bento-step>Step 3</bento-step> <bento-step>Step 4</bento-step> </bento-stepper> `, setup(props) { const updateIndex = (index: number) => action('update:index')(index); return { updateIndex, args: isVue2 ? props : _args, }; }, }; }, args: { index: 2, }, }; Default.parameters = storybookDocsParameter(` <bento-stepper :index="index" @update:index="index = $event"> <bento-step>Step 1</bento-step> <bento-step>Step 2</bento-step> <bento-step>Step 3</bento-step> <bento-step>Step 4</bento-step> </bento-stepper> <div role="status"> A dynamic content such as computed(() => steps[activeIndex]) is needed here in order for the section to be announced by the screen reader on step change </div> `); export const Substeps: Story = { render(_args, { argTypes }) { return { components: { BentoStepper, BentoStep }, props: Object.keys(argTypes), template: ` <bento-stepper :index="index" @update:index="index = $event"> <bento-step>Step 1</bento-step> <bento-step :substep-index="args.substepIndex" @update:substep-index="substepAction"> Step 2 <template #substeps> <bento-step>Substep 1</bento-step> <bento-step>Substep 2</bento-step> <bento-step>Substep 3</bento-step> </template> </bento-step> <bento-step>Step 3</bento-step> </bento-stepper> `, setup(props) { const updateIndex = (index: number) => action('update:index')(index); const substepAction = (index: number) => action('update:substep-index')(index); return { updateIndex, substepAction, args: isVue2 ? props : _args, }; }, }; }, args: { index: 1, substepIndex: 1, }, }; Substeps.parameters = storybookDocsParameter(` <bento-stepper :index="index" @update:index="index = $event"> <bento-step>Step 1</bento-step> <bento-step :substep-index="args.substepIndex" @update:substep-index="substepAction"> Step 2 <template #substeps> <bento-step>Substep 1</bento-step> <bento-step>Substep 2</bento-step> <bento-step>Substep 3</bento-step> </template> </bento-step> <bento-step>Step 3</bento-step> </bento-stepper> <div role="status"> A dynamic content such as computed(() => steps[activeIndex]) is needed here in order for the section to be announced by the screen reader on step change </div> `); export const ConditionalSteps: Story = { render(_args, { argTypes }) { return { components: { BentoStepper, BentoStep, BentoButton }, props: Object.keys(argTypes), template: ` <div class="b-stepper-story" :class="args.variant === 'horizontal' && 'b-stepper-story--horizontal'"> <bento-stepper v-bind="args" :key="JSON.stringify(args)" :style="stepperStyle" @update:index="updateIndex"> <bento-step v-for="(step, index) in steps" :key="step">{{ step }}</bento-step> </bento-stepper> <bento-button @click="toggleSteps">Toggle extra steps</bento-button> </div> `, setup(props) { const updateIndex = (index: number) => action('update:index')(index); const withMiddleSteps = ref(true); const steps = computed(() => [ 'General config', withMiddleSteps.value && 'Online settings', withMiddleSteps.value && 'Offline settings', 'Summary', withMiddleSteps.value && 'Extra: post config', ].filter(Boolean) ); const toggleSteps = () => { withMiddleSteps.value = !withMiddleSteps.value; }; const stepperStyle = computed(() => ({ width: props.variant === 'horizontal' ? 'unset' : '200px' })); return { updateIndex, args: isVue2 ? props : _args, withMiddleSteps, steps, toggleSteps, stepperStyle, }; }, }; }, args: { index: 1, }, }; ConditionalSteps.parameters = storybookDocsParameter(` <bento-stepper :index="index" @update:index="index = $event"> <bento-step v-for="step in steps" :key="step">{{ step }}</bento-step> </bento-stepper> <div role="status"> A dynamic content such as computed(() => steps[activeIndex]) is needed here in order for the section to be announced by the screen reader on step change </div> `);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <nav class="b-stepper"> <ol v-bento-keyboard-navigation-directive class="b-stepper__list" :class="conditionalClasses" v-on="keyboardNavigationListeners" > <slot></slot> </ol> <bento-typography el="span" variant="body" stronger class="b-stepper__text-stepper" >{{ t('step', { currentIndex: n(activeStepIndex + 1), totalSteps: n(stepsAmount), content: currentStepContent, }) }} </bento-typography> </nav> </template> <script setup lang="ts"> import { BentoTypography } from '@/components/typography'; import { computed, type DeepReadonly, type PropType, provide, readonly, ref, watch } from 'vue'; import { BentoKeyboardNavigationDirective as vBentoKeyboardNavigationDirective } from '@/directives'; import { useKeyboardNavigation, useStepRegister } from './composables'; import { useI18n } from '@/utils/ts/i18n'; import { STEPPER_STATE } from './stepper.keys'; import { type BentoStepperState, type BentoStepperStepExposeState, type BentoStepperTypeOption, BentoStepperVariantOption, } from './stepper.types'; import messages from './messages.json'; const props = defineProps({ /** * Type of stepper; defines how the stepper should behave. Default: linear * @values linear, non-linear */ type: { type: String as PropType<`${BentoStepperTypeOption}`>, default: 'linear', }, /** * Stepper variant; defines how the stepper should be displayed. Default: vertical * @values vertical, horizontal */ variant: { type: String as PropType<`${BentoStepperVariantOption}`>, default: 'vertical', }, /** * Active step index */ index: { type: Number, default: 0, }, }); const emit = defineEmits<{ /** * Event triggered when step is updated by the stepper */ (e: 'update:index', index: number): void; }>(); type MessageSchema = (typeof messages)['en-US']; const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const childrenSteps = ref<Array<BentoStepperStepExposeState>>([]); const activeStepIndex = ref(props.index); const latestActiveStep = ref(activeStepIndex.value); const navigateToStep = (index: number) => { // Stores latest active step index so users can navigate backwards and forwards on linear stepper if (index > latestActiveStep.value) { latestActiveStep.value = index; } activeStepIndex.value = index; }; const currentStepContent = computed(() => childrenSteps.value[activeStepIndex.value]?.stepContent); const stepsAmount = computed(() => childrenSteps.value.length); const childrenStepsElements = computed(() => childrenSteps.value.map(({ buttonElement }) => buttonElement)); const isHorizontalVariant = computed(() => props.variant === BentoStepperVariantOption.HORIZONTAL); // List of all visible steps nested on the stepper, including the substeps of the children steps // Flattening the steps on a single list makes the keyboard navigation easier to be handled const allVisibleStepsElements = computed(() => childrenSteps.value .map(({ buttonElement, substeps }, index) => { const isStepActive = activeStepIndex.value === index; const isSubstepsVisible = !isHorizontalVariant.value && isStepActive; if (!isSubstepsVisible) { return [buttonElement]; } return [buttonElement].concat(substeps.map(step => step.buttonElement)); }) .flat() ); const { registerStep, removeStep } = useStepRegister(childrenSteps); const stepperState: BentoStepperState = ref({ activeStepIndex, latestActiveStep, steps: childrenStepsElements, type: props.type, variant: props.variant, navigateToStep, registerStep, removeStep, }); const conditionalClasses = computed(() => ({ 'b-stepper__list--horizontal': isHorizontalVariant.value, })); watch(() => props.index, navigateToStep); watch(activeStepIndex, index => { emit('update:index', index); }); const keyboardNavigationListeners = useKeyboardNavigation(allVisibleStepsElements); // Provides stepper state to BentoStep children components provide<DeepReadonly<BentoStepperState>>(STEPPER_STATE, readonly(stepperState)); const reset = () => { activeStepIndex.value = 0; latestActiveStep.value = 0; }; defineExpose({ reset, }); </script> <script lang="ts"> /** * Steppers break down complex processes into manageable steps to guide users through a sequential or multi-step process. * * @example * import { BentoStep, BentoStepper } from '@adyen/bento-vue2'; * * export default { * components: { BentoStep, BentoStepper }, * template: ` * <bento-stepper :index="1"> * <bento-step>Step 1</bento-step> * <bento-step>Step 2</bento-step> * <bento-step>Step 3</bento-step> * </bento-stepper> * ` * } */ export default { i18n: { messages }, name: 'stepper', }; </script> <style scoped src="./stepper.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import BentoStructuredList from '../../structured-list.vue'; import BentoStructuredListItem from './structured-list-item.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { BentoTypography } from '@/components/typography'; import { isVue2 } from 'vue-demi'; const meta: Meta = { title: 'Structured list/Structured list item', component: BentoStructuredListItem, argTypes: { search: { table: { type: { summary: '($event) => void' }, }, }, default: { description: 'Value of the item', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, } as Meta; export default meta; type Story = StoryObj<typeof BentoStructuredList>; const DEFAULT_PROPS = { default: 'Value', label: 'Label', copy: 'Text to copy', labelInfo: 'Extra info about the item', searchTooltip: 'Perform this search', truncate: false, }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoStructuredList, BentoStructuredListItem, BentoTypography, }, props: Object.keys(argTypes), template: ` <bento-structured-list> <bento-structured-list-item v-bind="args"> <bento-typography>{{ args.default }} </bento-typography> </bento-structured-list-item> </bento-structured-list> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: DEFAULT_PROPS, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div class="b-structured-list-item" :class="conditionalClasses"> <dt class="b-structured-list-item__label" :style="computedLabelStyle"> <bento-typography :variant="labelTypographyVariant">{{ label }}</bento-typography> <bento-info-icon v-if="labelInfo" class="b-structured-list-item__icon" :tooltip-text="labelInfo" /> </dt> <dd class="b-structured-list-item__value" :class="valueConditionalClasses"> <slot /> <copy v-if="copy" class="b-structured-list-item__icon" :text="copy" @click="emitCopy" /> <bento-button v-if="search" v-bento-tooltip-directive:top="searchTooltip ? searchTooltip : t('search')" class="b-structured-list-item__icon" variant="tertiary" @click="search" > <template #iconLeft> <search-icon :svg-title="t('search')" /> </template> </bento-button> </dd> </div> </template> <script setup lang="ts"> import { computed, inject, ref, type Ref } from 'vue'; // Components import { BentoButton } from '@/components/button'; import { BentoInfoIcon } from '@/components/info-icon'; import { BentoTypography } from '@/components/typography'; import Copy from '@/components/internal/copy/copy.vue'; import SearchIcon from '@adyen/ui-assets-icons-16/vue/search'; // Utils import { useI18n } from '@/utils/ts/i18n'; // Directives import { BentoTooltipDirective as vBentoTooltipDirective } from '@/directives'; // Keys import { STRUCTURED_LIST_CONDENSED_INJECTION_KEY, STRUCTURED_LIST_LABELS_WIDTH_INJECTION_KEY, } from '../../structured-list.keys'; import messages from '../../messages.json'; type MessageSchema = (typeof messages)['en-US']; const DEFAULT_LABELS_WIDTH = '33%'; const props = defineProps<{ /** * Label to display for the value */ label: string; /** * Text to display in info icon tooltip in the label to display for the value */ labelInfo?: string; /** * Text to copy when the user clicks `Copy` icon */ copy?: string; /** * Function to trigger when user clicks `Search` icon */ search?: (event: Event) => void; /** * Text to display when the user hovers over `Search` icon */ searchTooltip?: string; /** * Truncates the value of the item */ truncate?: boolean; }>(); const emit = defineEmits<{ /** * Emits copy event and value when copy button is clicked */ (e: 'copy', value: string): void; }>(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const isCondensed = inject(STRUCTURED_LIST_CONDENSED_INJECTION_KEY, false); const labelsWidth = inject<Ref<string>>(STRUCTURED_LIST_LABELS_WIDTH_INJECTION_KEY, ref(DEFAULT_LABELS_WIDTH)); const computedLabelStyle = computed(() => `flex-basis:${labelsWidth.value}`); const conditionalClasses = computed(() => ({ 'b-structured-list-item--condensed': isCondensed, })); const valueConditionalClasses = computed(() => ({ 'b-structured-list-item__value--truncate': props.truncate, })); const emitCopy = () => { emit('copy', props.copy); }; const labelTypographyVariant = computed(() => (isCondensed ? 'caption' : 'body')); </script> <script lang="ts"> /** * This is an item of the `BentoStructuredList` meant to be used only with that component. * It consists of a label/value pair, and also the optional copy and search modifiers. * * @example * import { BentoCurrencyISOCode, BentoStructuredListItem, BentoStructuredListItem } from '@adyen/bento-vue2'; * * export default { * components: { BentoStructuredListItem, BentoStructuredListItem }, * template: ` * <bento-structured-list> * <bento-structured-list-item label="Shopper" search='searchShopper' search-tooltip='Find shopper'> * <bento-typography> * 143442642 * </bento-status> * </bento-structured-list-item> * <bento-structured-list-item label="Amount" copy='123456'> * <bento-currency :currency="BentoCurrencyISOCode.USD" :value="123456" /> * </bento-structured-list-item> * </bento-structured-list> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./structured-list-item.scss" />
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Canvas, Meta, Story } from '@storybook/blocks';
|
|
2
|
+
import * as StructuredListStories from './structured-list.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={StructuredListStories} />
|
|
5
|
+
|
|
6
|
+
# Structured List
|
|
7
|
+
|
|
8
|
+
This is a list used to display pairs of information in a clear and organized manner. It consists of labels and their
|
|
9
|
+
corresponding values.
|
|
10
|
+
|
|
11
|
+
`StructuredList` should always be used in combination with `StructuredListItem` to correctly render the label/value
|
|
12
|
+
pairs.
|
|
13
|
+
|
|
14
|
+
<Canvas withSource="closed">
|
|
15
|
+
<Story of={StructuredListStories.EverythingBagel} />
|
|
16
|
+
</Canvas>
|
|
17
|
+
|
|
18
|
+
## Use Cases
|
|
19
|
+
|
|
20
|
+
Use a structured list when there is crucial details to display. They are often used to display details of an account,
|
|
21
|
+
payments, profiles or even summaries of information. They usually sit in cards, modals and side panels.
|
|
22
|
+
|
|
23
|
+
## Flexible width
|
|
24
|
+
|
|
25
|
+
To help with visual consistency, we provide four presets for the structured list layout, which can be applied by passing
|
|
26
|
+
the following values to the `layout` prop:
|
|
27
|
+
|
|
28
|
+
- `25-75`
|
|
29
|
+
- `33-66` (default)
|
|
30
|
+
- `42-58`
|
|
31
|
+
- `50-50`
|
|
32
|
+
|
|
33
|
+
These presets can also be overridden with any static pixel value to match your content needs, by passing a number to the
|
|
34
|
+
`labelsWidth` prop.
|
|
35
|
+
|
|
36
|
+
## Structured List Item
|
|
37
|
+
|
|
38
|
+
Structure List Item is the subcomponent used to display each line of the Structured List.
|
|
39
|
+
|
|
40
|
+
### Variants
|
|
41
|
+
|
|
42
|
+
There are 7 recommended variants for structured lists. Each variant is accompanied by a label.
|
|
43
|
+
|
|
44
|
+
- `Text`: Used to display text only
|
|
45
|
+
- `Payment method`: Used to display payment methods accompanied by their logos.
|
|
46
|
+
- `Links`: Used to display links internally and externally.
|
|
47
|
+
- `Countries`: Used to display countries.
|
|
48
|
+
- `Status`: Used to display different statuses accompanied by an indicator.
|
|
49
|
+
- `Amount`: Used to display financial data, such as currency values and monetary amounts.
|
|
50
|
+
- `Date`: Used to display date and time information.
|
|
51
|
+
|
|
52
|
+
It is also possible to pass other values into the slot if necessary.
|
|
53
|
+
|
|
54
|
+
### Modifiers
|
|
55
|
+
|
|
56
|
+
- `Copy`: This allows users to copy a the value to clipboard
|
|
57
|
+
- `Search`: This automatically searches the value within the payment list
|
|
58
|
+
|
|
59
|
+
## Accessibility
|
|
60
|
+
|
|
61
|
+
### Keyboard interaction
|
|
62
|
+
|
|
63
|
+
User can use `Tab` key to move focus within the component and use Spacebar/Enter to activate the any interactive
|
|
64
|
+
elements such as links, copy or search elements. Using Spacebar/Enter triggers the interactive element that is in focus.
|
|
65
|
+
|
|
66
|
+
## Resources
|
|
67
|
+
|
|
68
|
+
- [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=9935-30026&mode=design&t=QDQzlQDdZV9Gi1Vu-0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { BentoPaymentMethod } from '@/components/payment-method'; import { BentoStatus } from '@/components/status'; import { BentoCurrency, BentoCurrencyISOCode } from '@/components/currency'; import { BentoCountry, BentoCountryCode } from '@/components/country'; import { BentoTypography } from '@/components/typography'; import { BentoDate, BentoDateFormat } from '@/components/date'; import { BentoLink } from '@/components/link'; import BentoStructuredList from './structured-list.vue'; import BentoStructuredListItem from './components/structered-list-item/structured-list-item.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { BentoStructuredListLayoutOptions } from './structured-list.types'; import BentoStructuredListDefault from './__tests__/structured-list-default.vue?raw'; const meta: Meta = { title: 'Structured list', component: BentoStructuredList, argTypes: { layout: { options: Object.values(BentoStructuredListLayoutOptions), mapping: BentoStructuredListLayoutOptions, control: { type: 'select', }, }, default: { control: false, table: { type: { summary: 'BentoStructuredListItem[]' }, }, }, }, } as Meta; export default meta; type Story = StoryObj<typeof BentoStructuredList>; export const EverythingBagel: Story = { render: (_args, { argTypes }) => ({ components: { BentoStructuredList, BentoStructuredListItem, BentoPaymentMethod, BentoCurrency, BentoLink, BentoTypography, BentoDate, BentoStatus, BentoCountry, }, props: Object.keys(argTypes), template: ` <bento-structured-list v-bind="args"> <bento-structured-list-item label="Status"> <bento-status variant="green">Approved</bento-status> </bento-structured-list-item> <bento-structured-list-item label="Linked"> <bento-link to="router/path">This is a link</bento-link> </bento-structured-list-item> <bento-structured-list-item label="Amount"> <bento-currency currency="USD" :value="123456" /> </bento-structured-list-item> <bento-structured-list-item label="Funding source"> <bento-typography>Credit</bento-typography> </bento-structured-list-item> <bento-structured-list-item label="Date" label-info="Payment date"> <bento-date :date="new Date('2017-09-25')" :format="BentoDateFormat.SHORT_DATE" /> </bento-structured-list-item> <bento-structured-list-item label="Country/Region"> <bento-country :code="BentoCountryCode.NL" /> </bento-structured-list-item> <bento-structured-list-item label="PSP reference" copy="P1234DBE2435" @copy="copyAction"> <bento-typography>P1234DBE2435</bento-typography> </bento-structured-list-item> <bento-structured-list-item label="Payment method"> <bento-payment-method type="Klarna" label="Klarna" /> </bento-structured-list-item> <bento-structured-list-item label="Shopper" :search="search" search-tooltip="Search in payments list" > <bento-typography>143442642</bento-typography> </bento-structured-list-item> </bento-structured-list> `, setup(props) { const copyAction = value => action('copy')(value); return { BentoCurrencyISOCode, BentoDateFormat, BentoCountryCode, // eslint-disable-next-line @typescript-eslint/no-empty-function search: () => {}, copyAction, dateObject: new Date('2017-09-25'), args: isVue2 ? props : _args, }; }, }), args: { layout: '33-66' }, parameters: storybookDocsParameter(BentoStructuredListDefault), };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <dl class="b-structured-list"> <slot /> </dl> </template> <script setup lang="ts"> import { computed, inject, provide } from 'vue'; import { STRUCTURED_LIST_CONDENSED_INJECTION_KEY, STRUCTURED_LIST_LABELS_WIDTH_INJECTION_KEY, } from '@/components/structured-list/structured-list.keys'; import { BentoStructuredListLayoutOptions } from './structured-list.types'; const props = withDefaults( defineProps<{ /** * Width of the labels container, in pixels. Overrides layout prop. */ labelsWidth?: number; /** * The layout of the label value pair in percentage. * If labelsWidth is set, layout will be overriden by the static pixel value. * * If the value entered is not listed in the example * list below '33-66' will be applied * @example * '25-75' * '33-66' * '42-58' * '50-50' * '75-25' * '66-33' * '58-42' */ layout?: BentoStructuredListLayoutOptions; }>(), { labelsWidth: null, layout: '33-66', } ); const isCondensed = inject(STRUCTURED_LIST_CONDENSED_INJECTION_KEY, false); const computedLayout = computed(() => BentoStructuredListLayoutOptions.includes(props.layout) ? props.layout : '33-66' ); const computedLabelsWidth = computed(() => { if (isCondensed) { return '130px'; } if (props.labelsWidth) { return `${props.labelsWidth}px`; } return `${computedLayout.value.split('-')[0]}%`; }); provide(STRUCTURED_LIST_LABELS_WIDTH_INJECTION_KEY, computedLabelsWidth); </script> <script lang="ts"> /** * This is a list used to display pairs of information in a clear and organized manner. * `BentoStructureListItem` should be used to pass the label/value pairs to the list. * * @example * import { BentoStructuredListItem, BentoStructuredListItem } from '@adyen/bento-vue2'; * * export default { * components: { BentoStructuredList, BentoStructuredListItem }, * template: ` * <bento-structured-list> * <bento-structured-list-item label="Payment"> * <bento-payment-method label="Klarna" type="Klarna" /> * </bento-structured-list-item> * <bento-structured-list-item label="Amount"> * <bento-currency :currency="BentoCurrencyISOCode.USD" :value="123456" /> * </bento-structured-list-item> * </bento-structured-list> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./structured-list.scss" />
|
package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.vue
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div class="b-summary-grid-item" :class="conditionalClasses" :style="computedStyles"> <div class="b-summary-grid-item__header"> <bento-typography class="b-summary-grid-item__title" el="span" variant="body"> <bento-dashed-underline v-if="withUnderline" :tooltip-text="underlineTooltipText" :popover-size="underlinePopoverSize" > {{ title }} <template v-if="hasSlot('dashed-underline')" #popover-content> <slot name="dashed-underline"></slot> </template> </bento-dashed-underline> <template v-else> {{ title }} </template> </bento-typography> <div class="b-summary-grid-item__title-icons"> <copy v-if="copy" class="b-structured-list__icon" :text="copy" @click="$emit('copy-click', $event)" /> <bento-info-icon v-if="infoIconTooltipText" :tooltip-text="infoIconTooltipText" tooltip-position="top" class="b-summary-grid-item__title-tooltip" @mouseenter="$emit('info-icon-mouseenter', $event)" /> </div> </div> <bento-typography data-testid="summary-grid-item-content-typography" class="b-summary-grid-item__title" v-bind="typographyProps" el="span" > <slot /> <bento-tag v-if="trend && itemType !== 'text'" :variant="trendVariant" :label="trend" class="b-summary-grid-item__content-trend" /> </bento-typography> <bento-typography v-if="hasSlot('secondary')" class="b-summary-grid-item__title" data-testid="summary-grid-item-secondary-metric-typography" el="span" variant="caption" stronger > <slot name="secondary" /> <bento-tag v-if="secondaryTrend" :variant="secondaryTrendVariant" :label="secondaryTrend" class="b-summary-grid-item__content-trend" /> </bento-typography> </div> </template> <script setup lang="ts"> import { computed, inject, ref, toRefs, useSlots } from 'vue'; // Components import { BentoDashedUnderline } from '@/components/dashed-underline'; import { BentoInfoIcon } from '@/components/info-icon'; import { BentoTypography, type BentoTypographyVariant } from '@/components/typography'; import { BentoTag, type BentoTagVariant } from '@/components/tag'; import { Copy } from '@/internal'; // Utils import { useHasSlot } from '@/composables'; // Types import { SUMMARY_GRID_SLASHED_ZERO } from '@/components/summary-grid/summary-grid.keys'; import type { BentoSummaryGridItemTrendDirection } from '@/components/summary-grid/summary-grid.types'; import type { BentoSummaryGridItemProps } from './summary-grid-item.types'; defineEmits<{ /** * Emitted when the copy button is clicked. */ (e: 'copy-click', event: MouseEvent): void; /** * Emitted when the mouse pointer enters the info icon component. */ (e: 'info-icon-mouseenter', event: MouseEvent): void; }>(); const props = withDefaults(defineProps<BentoSummaryGridItemProps>(), { copy: undefined, fitContent: false, infoIconTooltipText: null, itemType: 'custom', secondaryTrend: null, secondaryTrendDirection: 'positive', size: 'small', trend: null, trendDirection: 'positive', underlinePopoverSize: 'fit-content', underlineTooltipText: null, }); const { trendDirection, secondaryTrendDirection, itemType, size } = toRefs(props); const hasSlashedZero = inject(SUMMARY_GRID_SLASHED_ZERO, ref(false)); const tagVariantColor = (direction: `${BentoSummaryGridItemTrendDirection}`): `${BentoTagVariant}` => { switch (direction) { case 'positive': return 'green'; case 'negative': return 'red'; default: return 'blue'; } }; // TODO implement plus or minus symbol logic const trendVariant = computed(() => tagVariantColor(trendDirection.value)); const secondaryTrendVariant = computed(() => tagVariantColor(secondaryTrendDirection.value)); const typographyContentElement = computed<`${BentoTypographyVariant}`>(() => { if (size.value === 'small') { return 'body'; } if (itemType.value !== 'text') { return 'title'; } return null; }); const typographyProps = computed(() => ({ medium: size.value === 'large', strongest: size.value === 'small', wide: size.value === 'small', variant: typographyContentElement.value, })); const computedStyles = computed(() => (props.fitContent ? { width: 'fit-content' } : {})); const conditionalClasses = computed(() => ({ 'b-summary-grid-item--slashed-zero': hasSlashedZero.value })); const slots = useSlots(); const hasSlot = useHasSlot(slots); const withUnderline = computed(() => props.underlineTooltipText || hasSlot('dashed-underline')); </script> <script lang="ts"> /** * A summary bar item is a piece of information to be displayed in `bento-summary-bar` component * * @example * import { SummaryBarItem } from '@adyen/bento-vue2'; * * export default { * components: { SummaryBarItem }, * template: ` * <summary-grid-item :title="textVariable"> * Content of the item * </summary-grid-item> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./summary-grid-item.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { type Meta, type StoryObj } from '@storybook/vue'; import BentoSummaryGridItemAmount from './summary-grid-item-amount.vue'; import { BentoSummaryGridItemSize, BentoSummaryGridItemTrendDirection } from '@/components/summary-grid'; import { isVue2 } from 'vue-demi'; import { action } from '@storybook/addon-actions'; const meta: Meta = { title: 'Summary Grid/Summary Grid Item/Summary Grid Item Amount', component: BentoSummaryGridItemAmount, argTypes: { size: { options: Object.values(BentoSummaryGridItemSize), mapping: BentoSummaryGridItemSize, control: { type: 'select', }, }, trendDirection: { options: Object.values(BentoSummaryGridItemTrendDirection), mapping: BentoSummaryGridItemTrendDirection, control: { type: 'select', }, }, secondaryTrendDirection: { options: Object.values(BentoSummaryGridItemTrendDirection), mapping: BentoSummaryGridItemTrendDirection, control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoSummaryGridItemAmount>; const SUMMARY_BAR_ITEM_TITLE = 'Summary grid item amount'; const SUMMARY_BAR_ITEM_LABEL = '200.00 EUR'; const SUMMARY_BAR_ITEM_SECONDARY_LABEL = '/ day'; const SUMMARY_BAR_INFO_ICON_TOOLTIP = 'Text for icon tooltip'; const SUMMARY_BAR_ITEM_TREND = '+12%'; const SUMMARY_BAR_ITEM_SECONDARY_METRIC = '220.00 USD / day'; const SUMMARY_BAR_ITEM_SECONDARY_TREND = '+12%'; const SUMMARY_BAR_ITEM_DEFAULT_PROPS = { title: SUMMARY_BAR_ITEM_TITLE, label: SUMMARY_BAR_ITEM_LABEL, secondaryLabel: SUMMARY_BAR_ITEM_SECONDARY_LABEL, size: 'small', infoIconTooltipText: SUMMARY_BAR_INFO_ICON_TOOLTIP, copy: 'copy this', trend: SUMMARY_BAR_ITEM_TREND, }; const SUMMARY_BAR_ITEM_WITH_SECONDARY_METRIC_PROPS = { ...SUMMARY_BAR_ITEM_DEFAULT_PROPS, secondaryMetric: SUMMARY_BAR_ITEM_SECONDARY_METRIC, secondaryTrend: SUMMARY_BAR_ITEM_SECONDARY_TREND, secondaryTrendDirection: 'positive', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoSummaryGridItemAmount }, props: Object.keys(argTypes), template: ` <bento-summary-grid-item-amount v-bind="args" @copy-click="copyClicked" @info-icon-mouseenter="infoIconMouseEnter" /> `, setup(props) { const copyClicked = (event: PointerEvent) => action('copy-click')(event); const infoIconMouseEnter = (event: PointerEvent) => action('info-icon-mouseenter')(event); return { args: isVue2 ? props : _args, copyClicked, infoIconMouseEnter, }; }, }), args: { ...SUMMARY_BAR_ITEM_DEFAULT_PROPS, }, }; export const WithSecondaryMetric: Story = { render: (_args, { argTypes }) => ({ components: { BentoSummaryGridItemAmount }, props: Object.keys(argTypes), template: `<bento-summary-grid-item-amount v-bind="args" />`, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...SUMMARY_BAR_ITEM_WITH_SECONDARY_METRIC_PROPS, }, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <summary-grid-item item-type="amount" :info-icon-tooltip-text="infoIconTooltipText" :title="title" :copy="copy" :fit-content="fitContent" :size="size" :trend="trend" :trend-direction="trendDirection" :secondary-trend="secondaryTrend" :secondary-trend-direction="secondaryTrendDirection" :underline-popover-size="underlinePopoverSize" :underline-tooltip-text="underlineTooltipText" class="b-summary-grid-item-amount" @copy-click="$emit('copy-click', $event)" @info-icon-mouseenter="$emit('info-icon-mouseenter', $event)" > <template v-if="hasSlot('dashed-underline')" #dashed-underline> <slot name="dashed-underline" /> </template> <div class="b-summary-grid-item-amount__content"> <div>{{ label }}</div> <bento-typography v-if="secondaryLabel" variant="caption" class="b-summary-grid-item-amount__secondary-label" >{{ secondaryLabel }}</bento-typography > </div> <template v-if="secondaryMetric" #secondary> {{ secondaryMetric }} </template> </summary-grid-item> </template> <script setup lang="ts"> import { useSlots } from 'vue'; import { BentoTypography } from '@/components/typography'; import { SummaryGridItem } from '../summary-grid-item'; import { useHasSlot } from '@/composables'; import type { BentoSummaryGridItemAmountProps } from './summary-grid-item-amount.types'; defineEmits<{ /** * Emitted when the copy button is clicked. */ (e: 'copy-click', event: MouseEvent): void; /** * Emitted when the mouse pointer enters the info icon component. */ (e: 'info-icon-mouseenter', event: MouseEvent): void; }>(); withDefaults(defineProps<BentoSummaryGridItemAmountProps>(), { copy: undefined, fitContent: false, infoIconTooltipText: null, secondaryLabel: null, secondaryMetric: null, secondaryTrend: null, secondaryTrendDirection: 'positive', size: 'small', trend: null, trendDirection: 'positive', underlinePopoverSize: 'fit-content', underlineTooltipText: null, }); const slots = useSlots(); const hasSlot = useHasSlot(slots); </script> <script lang="ts"> /** * Summary grid item component used to display financial data, such as currency values and monetary amounts * * @example * import { BentoSummaryGridItemAmount } from '@adyen/bento-vue2'; * * export default { * components: { BentoSummaryGridItemAmount }, * template: ` * <bento-summary-grid-item-amount * title="Your title" * label="200.00 EUR" * secondary-label="/ day" * info-icon-tooltip-text="Your tooltip text here will be toggled by the info icon" * trend="+12%" * trend-direction="neutral" * size="medium" * /> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./summary-grid-item-amount.scss" />
|