@adyen/bento-mcp 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +113 -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/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-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 +951 -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/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 +30031 -0
- package/package.json +45 -3
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Canvas, Story, Meta } from '@storybook/blocks';
|
|
2
|
+
import * as InputFieldStories from './input-field.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={InputFieldStories} />
|
|
5
|
+
|
|
6
|
+
# Input field
|
|
7
|
+
|
|
8
|
+
An input field component allows users enter and edit text. It typically appears in forms and dialogs.
|
|
9
|
+
|
|
10
|
+
It's a complete form control including a label, input, icons and description text.
|
|
11
|
+
|
|
12
|
+
<Canvas withSource="closed">
|
|
13
|
+
<Story of={InputFieldStories.Default} />
|
|
14
|
+
</Canvas>
|
|
15
|
+
|
|
16
|
+
- [Input field](#input-field)
|
|
17
|
+
- [Variants](#variants)
|
|
18
|
+
- [Icons](#icons)
|
|
19
|
+
- [Modifiers](#modifiers)
|
|
20
|
+
- [Accessibility](#accessibility)
|
|
21
|
+
- [Focus](#focus)
|
|
22
|
+
|
|
23
|
+
## Variants
|
|
24
|
+
|
|
25
|
+
There are available 3 variants, which can be set by passing one of the listed string value to the `variant` prop:
|
|
26
|
+
|
|
27
|
+
- 'default': sets the default variant
|
|
28
|
+
- 'payment-method': sets the payment method variant
|
|
29
|
+
- 'static-value': sets the static value variant
|
|
30
|
+
- 'dropdown': sets the dropdown variant
|
|
31
|
+
|
|
32
|
+
### Payment method
|
|
33
|
+
|
|
34
|
+
<Canvas withSource="closed">
|
|
35
|
+
<Story of={InputFieldStories.PaymentsMethod} />
|
|
36
|
+
</Canvas>
|
|
37
|
+
|
|
38
|
+
Use this variant for creating an input field for payments with a payment method pre-fixed or suffixed inside of it.
|
|
39
|
+
|
|
40
|
+
### Static value
|
|
41
|
+
|
|
42
|
+
<Canvas withSource="closed">
|
|
43
|
+
<Story of={InputFieldStories.StaticValue} />
|
|
44
|
+
</Canvas>
|
|
45
|
+
|
|
46
|
+
Use this variant for creating an input field that has a static value pre-fixed or suffixed inside of it.
|
|
47
|
+
|
|
48
|
+
#### Static value positions
|
|
49
|
+
|
|
50
|
+
Static value elements can be positioned before or after text value
|
|
51
|
+
|
|
52
|
+
This is controlled with property `staticValuePosition`, which accepts these 2 values:
|
|
53
|
+
|
|
54
|
+
- `'start'`
|
|
55
|
+
- `'end'`
|
|
56
|
+
|
|
57
|
+
### Dropdown
|
|
58
|
+
|
|
59
|
+
<Canvas withSource="closed">
|
|
60
|
+
<Story of={InputFieldStories.Dropdown} />
|
|
61
|
+
</Canvas>
|
|
62
|
+
|
|
63
|
+
A single select dropdown can be added to the `BentoInputField`, it properties are set with the `dropdown` property
|
|
64
|
+
defined by the `BentoInputDropdownProps` interface, which is only a small subset of the `bento-dropdown` component.
|
|
65
|
+
Every other property will be ignored.
|
|
66
|
+
|
|
67
|
+
The available properties are:
|
|
68
|
+
|
|
69
|
+
- `value`: Selected value of the dropdown
|
|
70
|
+
- `items`: List of available items
|
|
71
|
+
- `dynamic-filtering`: Allows user to filter the dropdown items
|
|
72
|
+
|
|
73
|
+
It's position can be controlled via the `dropdownPosition` property, which accepts these 2 values:
|
|
74
|
+
|
|
75
|
+
- `'start'`
|
|
76
|
+
- `'end'`
|
|
77
|
+
|
|
78
|
+
You can hook into either the `dropdown-input` emit even for the value or use the `update:dropdown` and the `.sync`
|
|
79
|
+
modifier with the `dropdown` property.
|
|
80
|
+
|
|
81
|
+
## Icons
|
|
82
|
+
|
|
83
|
+
<Canvas withSource="closed">
|
|
84
|
+
<Story of={InputFieldStories.DefaultWithIcons} />
|
|
85
|
+
</Canvas>
|
|
86
|
+
|
|
87
|
+
Icon for Default input icon may be displayed in 3 ways:
|
|
88
|
+
|
|
89
|
+
- before text field value
|
|
90
|
+
- after text field value
|
|
91
|
+
- before and after text field value
|
|
92
|
+
|
|
93
|
+
Icon for other variants may be displayed only after text field value
|
|
94
|
+
|
|
95
|
+
## Modifiers
|
|
96
|
+
|
|
97
|
+
### Disabled
|
|
98
|
+
|
|
99
|
+
The disabled property determines whether the input component is enabled or disabled.
|
|
100
|
+
|
|
101
|
+
When an input component is disabled, it will not allow user interaction and the value of the input cannot be changed.
|
|
102
|
+
|
|
103
|
+
The visual representation of a disabled input-field component is as a grayed-out color or and lack of hover effect.
|
|
104
|
+
|
|
105
|
+
### Readonly
|
|
106
|
+
|
|
107
|
+
Renders the `input-field` component in a readonly state. In terms of functionality, read only states are focusable and
|
|
108
|
+
submittable in a form. They're considered to be 'active' from an application's point of view. User can highlight and
|
|
109
|
+
copy the data in them.
|
|
110
|
+
|
|
111
|
+
### Error
|
|
112
|
+
|
|
113
|
+
There are two properties which control the error state of `input-field`:
|
|
114
|
+
|
|
115
|
+
- `errorMessage`: renders the component in an error state with an error message below the input field
|
|
116
|
+
- `error`: toggles the error state
|
|
117
|
+
|
|
118
|
+
`error` is deprecated and will be removed in the near future, so please use `errorMessage`.
|
|
119
|
+
|
|
120
|
+
### Slashed zero
|
|
121
|
+
|
|
122
|
+
If this prop is set to `true`, the input box will render zeroes in a slashed font numeric style.
|
|
123
|
+
|
|
124
|
+
### Tooltip
|
|
125
|
+
|
|
126
|
+
A tooltip with extra information for the field can be rendered by passing the desired information to the `tooltipText`
|
|
127
|
+
prop.
|
|
128
|
+
|
|
129
|
+
## Accessibility
|
|
130
|
+
|
|
131
|
+
### Focus
|
|
132
|
+
|
|
133
|
+
When an input-field or label is clicked or selected, the focus ring should be visible. The focus ring is an affordance
|
|
134
|
+
that allows users to easily identify which element is currently focused.
|
|
135
|
+
|
|
136
|
+
Identifying the current focused element is key to keep tab order obvious and explicit. Not providing a focus state on
|
|
137
|
+
click events could result in making focus management unintuitive and more complex, and fail to comply with basic
|
|
138
|
+
accessibility guidelines such as:
|
|
139
|
+
|
|
140
|
+
2.4.3 Focus order: If a Web page can be navigated sequentially and the navigation sequences affect meaning or operation,
|
|
141
|
+
focusable components receive focus in an order that preserves meaning and operability.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type BentoInputDropdownProps, BentoInputFieldElementPosition, BentoInputFieldType, BentoInputFieldVariant, } from './input-field.types'; import { type BentoListboxSelectedValue } from '@/types/listbox'; import { ref, watch } from 'vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoInputField from './input-field.vue'; import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import PaymentMethodTypes from '@adyen/ui-assets-icons-16/vue/payment-method-types'; import './input-field.stories.scss?module'; const meta: Meta = { title: 'Input field', component: BentoInputField, argTypes: { variant: { options: Object.values(BentoInputFieldVariant), mapping: BentoInputFieldVariant, defaultValue: BentoInputFieldVariant.DEFAULT, control: { type: 'select', }, }, type: { options: Object.values(BentoInputFieldType), mapping: BentoInputFieldType, defaultValue: BentoInputFieldType.TEXT, control: { type: 'select', }, }, default: { name: 'default', defaultValue: '', description: 'Label for the input field', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, description: { name: 'description', defaultValue: '', description: 'Description below input', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, dropdownPosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, staticValue: { name: 'staticValue', defaultValue: '', description: 'Static text that displayed before or after input value', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, staticValuePosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, defaultIconBefore: { name: 'defaultIconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, category: 'Deprecated', }, control: { disable: true, }, }, iconBefore: { name: 'iconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, iconAfter: { name: 'iconAfter', defaultValue: '', description: 'Optional `icon` at the right side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, error: { control: { type: 'boolean' }, table: { category: 'Deprecated' }, }, value: { control: { type: 'text' }, table: { category: 'Deprecated' }, description: 'Deprecated for usage with properties since version 2.0. Use `v-model` or `model-value` property instead', }, // Events input: { table: { type: { summary: '(value: BentoInputFieldValue) => void' }, category: 'Deprecated', }, control: { disable: true }, description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value` instead', }, 'update:model-value': { table: { type: { summary: '(value: BentoInputFieldValue) => void' }, }, control: { disable: true }, }, }, }; export default meta; type Story = StoryObj<typeof BentoInputField>; const INPUT_FIELD_DEFAULT_LABEL = 'Label above'; const INPUT_FIELD_DEFAULT_VALUE = 'Entered Text'; const INPUT_FIELD_DEFAULT_DESCRIPTION = 'Description below'; const DEFAULT_INPUT_FIELD_PROPS = { disabled: false, variant: 'default', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, staticValue: 'EUR', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #description>{{ args.description }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const inputValue = ref(args.modelValue); const inputAction = (value: string) => { inputValue.value = value; action('input')(value); }; const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); watch( () => args.modelValue, () => { inputValue.value = args.modelValue; } ); return { args, inputValue, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: INPUT_FIELD_DEFAULT_LABEL, }, parameters: storybookDocsParameter(` <bento-input-field variant="default" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> </bento-input-field> `), }; export const DefaultWithIcons = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #iconBefore> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description>{{ args.description }}</template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'With Icons', }, parameters: storybookDocsParameter(` <bento-input-field variant="default" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #iconBefore> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; export const PaymentsMethod = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description>{{ args.description }}</template> <template #staticValue> EUR </template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Payment Method Variant', variant: 'payment-method', }, parameters: storybookDocsParameter(` <bento-input-field variant="payment-method" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #paymentMethod> <bento-payment-method svg-title="payment-method" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; export const StaticValue = { ...PaymentsMethod, args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Static Value Variant', variant: 'static-value', }, parameters: storybookDocsParameter(` <bento-input-field variant="static-value" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #staticValue> EUR </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; const DROPDOWN_DEFAULT_OPTIONS = [ { label: 'EUR', value: 'EUR' }, { label: 'USD', value: 'USD' }, ]; export const Dropdown: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :dropdown.sync="dropdownProps" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description>{{ args.description }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const dropdownProps = ref({ ...args?.dropdown }); const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); const dropdownInputAction = (value: BentoListboxSelectedValue) => { action('dropdown-input')(value); }; watch( () => args.dropdown, () => { Object.assign(dropdownProps.value, args.dropdown); } ); return { args, inputAction, changeAction, focusAction, blurAction, dropdownInputAction, dropdownProps, }; }, }), args: { default: 'Dropdown Variant', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, variant: 'dropdown', dropdown: { items: DROPDOWN_DEFAULT_OPTIONS, modelValue: DROPDOWN_DEFAULT_OPTIONS[0].value, readonly: false, 'aria-label': 'Dropdown aria label', dynamicFiltering: false, } satisfies BentoInputDropdownProps, }, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <bento-typography class="b-input-field" data-testid="input-text-container" :class="inputFieldConditionalClasses" el="div" variant="body" > <field-label v-if="hasSlot('default') || label" :for="inputFieldId" data-testid="input-field-label" :tooltip-text="tooltipText" :optional="optional" :required="required" :label="label" @click="focusInput" > <slot></slot> </field-label> <div class="b-input-field__input-box" data-testid="input-box" :aria-disabled="disabled" @click="focusInput"> <!-- Optional Icon for Default text variant --> <span v-if="isDefaultVariant && (hasSlot('defaultIconBefore') || hasSlot('iconBefore'))" :aria-hidden="ariaHidden ? 'true' : undefined" class="b-input-field__icon-before" > <slot v-if="hasSlot('iconBefore')" name="iconBefore" /> <slot v-if="hasSlot('defaultIconBefore') && !hasSlot('iconBefore')" name="defaultIconBefore" /> </span> <!-- Mandatory Icon for Payment Method variant --> <span v-if="isPaymentMethodVariant && hasSlot('paymentMethod')" :aria-hidden="ariaHidden ? 'true' : undefined" class="b-input-field__payment-method" > <slot name="paymentMethod" /> </span> <!-- Dropdown at start --> <div v-if="shouldDisplayDropdownAtStart" :id="dropdownId" class="b-input-field__dropdown b-input-field__dropdown--start" @click.stop > <bento-dropdown v-if="dropdown" ref="inputDropdownElement" v-bind="inputFieldDropdownProps" :disabled="disabled" :readonly="isDropdownReadOnly" @update:model-value="onDropdownInput" /> </div> <!-- Static Value at start --> <bento-typography v-if="shouldDisplayStaticValueAtStart" :id="staticValueId" class="b-input-field__static-value b-input-field__static-value--start" el="span" variant="body" > <slot name="staticValue" /> </bento-typography> <div class="b-input-field__input-container"> <input :id="inputFieldId" v-bind="attrs" ref="inputFieldElement" class="b-input-field__input" :aria-label="computedAriaLabel" :aria-describedby="computedAriaDescribedBy" :aria-owns="computedAriaOwns" :aria-invalid="shouldShowError" :placeholder="placeholder" :required="required" :type="type" :value="modelValue ?? value" :disabled="disabled" :readonly="isReadOnly" @input="onInput" @change="emit('change')" @focus="onFocus" @blur="onBlur" @keydown="emit('keydown', $event)" @keyup="emit('keyup', $event)" @keydown.esc="emit('escape-pressed')" @click="emit('click', $event)" /> <!-- Hint at the end --> <bento-typography v-if="hint && isFocused && (modelValue || value)" el="span" class="b-input-field__hint" > {{ hint }} </bento-typography> </div> <span v-if="hasSlot('iconAfter')" :aria-hidden="ariaHidden ? 'true' : undefined" class="b-input-field__icon-after" data-testid="input-text__icon-after" > <slot name="iconAfter" /> </span> <!-- Static Value at the end --> <bento-typography v-if="shouldDisplayStaticValueAtEnd" :id="staticValueId" class="b-input-field__static-value b-input-field__static-value--end" el="span" variant="body" > <slot name="staticValue" /> </bento-typography> <!-- Dropdown at end --> <div v-if="shouldDisplayDropdownAtEnd" :id="dropdownId" data-testid="input-field-dropdown-container-end" class="b-input-field__dropdown b-input-field__dropdown--end" @click.stop > <bento-dropdown v-if="dropdown" :id="dropdownId" ref="inputDropdownElement" v-bind="inputFieldDropdownProps" :disabled="disabled" :readonly="isDropdownReadOnly" @update:model-value="onDropdownInput" /> </div> </div> <error-message v-if="shouldShowError && !!errorMessage" :id="errorId" :error-message="errorMessage" class="b-input-field__error-message" /> <span v-if="hasSlot('description') || description" :id="descriptionId" class="b-input-field__description" @click="focusInput" > <bento-typography el="span" variant="body"> <slot id="description" name="description"> {{ description }} </slot> </bento-typography> </span> </bento-typography> </template> <script setup lang="ts"> import { computed, inject, provide, type Ref, ref, toRef, toRefs, useAttrs, useSlots } from 'vue'; // Components import { BentoDropdown } from '@/components/dropdown'; import { BentoTypography } from '@/components/typography'; import { ErrorMessage, FieldLabel } from '@/internal'; // Utils import { deprecate } from '@/utils/ts/deprecate'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; import { generateUid } from '@/core/utils/ts'; import { getSlotText } from '@/utils/ts/get-slot-text'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { useI18n } from '@/utils/ts/i18n'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { INPUT_FIELD_COMPONENT_INJECTION_KEY, INPUT_FIELD_HINT_INJECTION_KEY, INPUT_FIELD_PARENT_COMPONENT_INJECTION_KEY, } from '@/components/input-field/input-field.keys'; import { type BentoListboxSelectedValue } from '@/types/listbox'; // Composables import { useFormLayoutFieldLoading, useHasSlot } from '@/composables'; // Types import { type BentoInputDropdownProps, BentoInputFieldElementPosition, type BentoInputFieldProps, BentoInputFieldStateClass, BentoInputFieldType, type BentoInputFieldValue, BentoInputFieldVariant, } from './input-field.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const attrs = useAttrs(); const slots = useSlots(); const props = withDefaults(defineProps<BentoInputFieldProps>(), { ariaHidden: true, ariaLabel: undefined, condensed: false, description: '', disabled: false, dropdown: undefined, dropdownPosition: BentoInputFieldElementPosition.START, error: false, errorMessage: null, label: '', modelValue: undefined, optional: false, placeholder: '', required: false, readonly: false, slashedZero: false, staticValue: '', staticValuePosition: BentoInputFieldElementPosition.START, tooltipText: null, type: BentoInputFieldType.TEXT, value: undefined, variant: BentoInputFieldVariant.DEFAULT, }); const emit = defineEmits<{ /** * Emitted when the element has lost focus */ (e: 'blur'): void; /** * Emitted when the input is clicked */ (e: 'click', event: MouseEvent); /** * Emitted when the user modifies the element's value. Unlike the 'input' event, the change event is not necessarily fired for each alteration to an element's value */ (e: 'change'): void; /** * Emitted when the Input Field's internal dropdown changes it's value */ (e: 'dropdown-input', selectedValue: BentoListboxSelectedValue): void; /** * Emitted when the "ESC" key is pressed */ (e: 'escape-pressed'): void; /** * Emitted when the element has received focus */ (e: 'focus'): void; /** * Emitted when a key is pressed down */ (e: 'keydown', event: KeyboardEvent): void; /** * Emitted when a key is pressed and lifted up */ (e: 'keyup', event: KeyboardEvent): void; /** * Emitted when the component's model changes * @deprecated since version 2.0. Use `v-model` or `update:model-value` instead. */ (e: 'input', inputValue: string): void; /** * Emitted when the Input Field's internal dropdown changes it's value. Contains every dropdown prop passed with the updated selected value as 'value' */ (e: 'update:dropdown', updatedDropdownProps: BentoInputDropdownProps): void; /** * Emitted when the component's model changes */ (e: 'update:model-value', inputValue: BentoInputFieldValue): void; }>(); const { emitValue } = useFormFieldEmits<BentoInputFieldValue>(emit); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const hasSlot = useHasSlot(slots); const inputFieldElement = ref(null); const inputDropdownElement = ref(null); const inputFieldId = generateUid('input'); const dropdownId = generateUid('input-dropdown'); const staticValueId = generateUid('input-static-value'); const descriptionId = generateUid('input-description'); const errorId = generateUid('input-error'); const isFocused = ref(false); // Provide INPUT_FIELD_COMPONENT_INJECTION_KEY as true, to set the input only dropdown size variant in the BentoDropdown comp provide(INPUT_FIELD_COMPONENT_INJECTION_KEY, true); // Renders the input with a hint if a string is provided const hint = inject<Ref<string | undefined>>(INPUT_FIELD_HINT_INJECTION_KEY, ref(undefined)); const onFocus = () => { isFocused.value = true; emit('focus'); }; const onBlur = () => { isFocused.value = false; emit('blur'); }; const { isReadOnly } = useFormLayoutFieldLoading(toRef(props, 'readonly')); const isDropdownReadOnly = computed<BentoInputDropdownProps['readonly']>( () => isReadOnly.value || props.dropdown?.readonly ); const shouldShowStaticValue = computed( () => props.variant === BentoInputFieldVariant.STATIC_VALUE && !!slots.staticValue ); const isDefaultVariant = computed(() => props.variant === BentoInputFieldVariant.DEFAULT); const isPaymentMethodVariant = computed(() => props.variant === BentoInputFieldVariant.PAYMENT_METHOD); const shouldDisplayStaticValueAtStart = computed( () => shouldShowStaticValue.value && props.staticValuePosition === BentoInputFieldElementPosition.START ); const shouldDisplayStaticValueAtEnd = computed( () => shouldShowStaticValue.value && props.staticValuePosition === BentoInputFieldElementPosition.END ); const shouldShowDropdown = computed(() => props.variant === BentoInputFieldVariant.DROPDOWN && !!props.dropdown); const shouldDisplayDropdownAtStart = computed( () => shouldShowDropdown.value && props.dropdownPosition === BentoInputFieldElementPosition.START ); const shouldDisplayDropdownAtEnd = computed( () => shouldShowDropdown.value && props.dropdownPosition === BentoInputFieldElementPosition.END ); const shouldShowError = computed( () => !props.disabled && !isReadOnly.value && (!!props.errorMessage || props.error) ); const computedLabel = computed(() => (getSlotText(slots)('default') as string) || props.label); const computedDescription = computed(() => (getSlotText(slots)('description') as string) || props.description); const computedAriaOwns = computed(() => { if (shouldShowDropdown.value === true && shouldShowStaticValue.value === true) { return `${staticValueId} ${dropdownId}`; } if (shouldShowDropdown.value === true) { return `${dropdownId}`; } if (shouldShowStaticValue.value === true) { return `${staticValueId}`; } return null; }); // vue-i18n issue with t() used in script - direct usage in template works // requires either createI18n({ legacy: false }) or to be wrapped in computed() const computedAriaLabelFallbackMessage = computed(() => t('ariaLabelFallback')); const computedAriaLabelDropdowFallback = computed(() => t('ariaLabelDropdownFallback', { inputLabel: computedAriaLabel.value }) ); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel } = toRefs(props); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, defaultFallback: computedLabel.value || computedAriaLabelFallbackMessage, }); const computedAriaDescribedBy = computed( () => `${computedDescription.value ? descriptionId : ''} ${ shouldShowError.value && !!props.errorMessage ? errorId : '' }` ); const inputFieldConditionalClasses = computed(() => ({ [`b-input-field--${BentoInputFieldStateClass.CONDENSED}`]: props.condensed, [`b-input-field--${BentoInputFieldStateClass.DISABLED}`]: props.disabled, [`b-input-field--${BentoInputFieldStateClass.READONLY}`]: isReadOnly.value, [`b-input-field--${BentoInputFieldStateClass.ERROR}`]: shouldShowError.value, [`b-input-field--slashed-zero`]: props.slashedZero, })); const inputFieldDropdownProps = computed(() => ({ 'aria-label': computedAriaLabelDropdowFallback.value, ...props.dropdown, })); const onInput = (event: Event) => { const inputValue = (event.target as HTMLInputElement).value; if (!props.disabled && !isReadOnly.value) { emitValue(inputValue); } }; const onDropdownInput = (selectedValue: BentoListboxSelectedValue) => { emit('dropdown-input', selectedValue); emit('update:dropdown', { ...props?.dropdown, value: selectedValue }); }; const focusInput = () => { inputFieldElement.value.focus(); }; const focus = () => { if (props.variant === 'dropdown') { inputDropdownElement.value.focus(); } else { focusInput(); } }; // Expose method so that other components can focus on it in Vue3 defineExpose({ focusInput, focus, }); const inputFieldParentComponent = inject(INPUT_FIELD_PARENT_COMPONENT_INJECTION_KEY, ''); if (!Object.values(BentoInputFieldType).includes(props.type as BentoInputFieldType) && !inputFieldParentComponent) { if (props.type === 'tel') { printDevelopmentWarning( `"bento-input-field" dropdown does not support the type '${props.type}', use "bento-input-field-phone-number" instead.` ); } else if (props.type === 'password') { printDevelopmentWarning( `"bento-input-field" dropdown does not support the type '${props.type}', use "bento-input-field-password" instead.` ); } else { printDevelopmentWarning(`"bento-input-field" dropdown does not support the type '${props.type}'`); } } /** * Deprecations */ if (props.error) { deprecate( 'BentoInputField "error" property', `Use the BentoInputField "errorMessage" property instead to indicate that there is a modifier error. Having the field as "undefined" will hide/remove the error. <bento-dropdown errorMessage="Error message text" />`, '2.0.0' ); } if (hasSlot('defaultIconBefore')) { deprecate( 'BentoInputField "defaultIconBefore" slot', `Use the BentoInputField "iconBefore" slot instead to add an icon at the left side of the input field <bento-input-field> \t<template #iconBefore> \t\t<my-icon-goes-here /> \t</template> </bento-input-field>`, '2.0.0' ); } if (props.value) { deprecate( 'BentoInputField "value" property', `The use of "value" prop in "BentoInputField" is no longer supported. Use the "v-model" or "model-value" property instead.`, '2.0.0' ); } </script> <script lang="ts"> /** * Input field component is used to create interactive controls for web-based * forms in order to accept data from the user. * * @example * import { BentoInputField } from '@adyen/bento-vue2' * * const inputValue = ref(''); * * export default { * components: { BentoInputField }, * tempate: ` * <bento-input-field * :model-value="inputValue" * :@update:model-value="newValue => inputValue.value = newValue" * > * } */ export default { name: 'bento-input-field', model: { prop: 'modelValue' }, i18n: { messages }, }; </script> <style lang="scss" scoped src="./input-field.scss" />
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { Canvas, Meta, Source } from '@storybook/blocks';
|
|
2
|
+
import * as InputFieldPasswordStories from './input-field-password.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={InputFieldPasswordStories} />
|
|
5
|
+
|
|
6
|
+
# Input field password
|
|
7
|
+
|
|
8
|
+
The input field password is a component designed to support users' preference for entering values securely. This is an
|
|
9
|
+
extension of the `bento-input-field` component.
|
|
10
|
+
|
|
11
|
+
<Canvas of={InputFieldPasswordStories.Default} />
|
|
12
|
+
|
|
13
|
+
## Use Cases
|
|
14
|
+
|
|
15
|
+
Use this component if:
|
|
16
|
+
|
|
17
|
+
- You need to collect sensitive information in forms, such as when users are creating or updating their accounts.
|
|
18
|
+
|
|
19
|
+
### Do not use
|
|
20
|
+
|
|
21
|
+
Avoid using this component for:
|
|
22
|
+
|
|
23
|
+
- Regular inputs, such as name, phone number, or food preferences.
|
|
24
|
+
|
|
25
|
+
## Modifiers
|
|
26
|
+
|
|
27
|
+
### Validations
|
|
28
|
+
|
|
29
|
+
The input performs live validations (in real-time), to disable this feature set `disable-validation` (see
|
|
30
|
+
[Disable validation](#disable-validation)).
|
|
31
|
+
|
|
32
|
+
The input validation consists of the following object properties:
|
|
33
|
+
|
|
34
|
+
- title
|
|
35
|
+
- list
|
|
36
|
+
- label
|
|
37
|
+
- callback
|
|
38
|
+
- additional arguments for the callback
|
|
39
|
+
- suggestion
|
|
40
|
+
|
|
41
|
+
These properties are displayed on the screen when the input field is focused or activated.
|
|
42
|
+
|
|
43
|
+
The validation callbacks are executed with each user input, applying a default debounce of 0.5 seconds and a maximum
|
|
44
|
+
wait time of 5 seconds. By default, the input value is validated against the following criteria:
|
|
45
|
+
|
|
46
|
+
- Minimum length of 12 characters (`minLength`)
|
|
47
|
+
- Contains at least one lowercase letter (`requireLowercase`)
|
|
48
|
+
- Contains at least one uppercase letter (`requireUppercase`)
|
|
49
|
+
- Contains at least one numerical character (`requireNumeric`)
|
|
50
|
+
- Contains at least one special character (`requireSpecialCharacter`)
|
|
51
|
+
|
|
52
|
+
The default functions can be imported and reused as follows:
|
|
53
|
+
|
|
54
|
+
<Source
|
|
55
|
+
dark
|
|
56
|
+
language="typescript"
|
|
57
|
+
code={`import {
|
|
58
|
+
minLength,
|
|
59
|
+
requireLowercase,
|
|
60
|
+
requireNumeric,
|
|
61
|
+
requireSpecialCharacter,
|
|
62
|
+
requireUppercase,
|
|
63
|
+
} from '@adyen/bento-vue2';`}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
It is important to note that the `title` and `suggestion` properties are not validated against the input value.
|
|
67
|
+
|
|
68
|
+
### Custom validation
|
|
69
|
+
|
|
70
|
+
Not all passwords have the same requirements. `bento-input-field-password` also supports custom rules, by allowing the
|
|
71
|
+
user to pass a custom `validation` object.
|
|
72
|
+
|
|
73
|
+
<Source
|
|
74
|
+
dark
|
|
75
|
+
language="javascript"
|
|
76
|
+
code={`
|
|
77
|
+
validation: {
|
|
78
|
+
title: 'Password must contain at least:',
|
|
79
|
+
suggestion: 'Avoid using currency symbols or accented letters.',
|
|
80
|
+
list: [
|
|
81
|
+
{
|
|
82
|
+
label: 'Always true',
|
|
83
|
+
key: 'alwaysTrue',
|
|
84
|
+
validate: () => true
|
|
85
|
+
},
|
|
86
|
+
}`}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
#### Extend default validation
|
|
90
|
+
|
|
91
|
+
By default, the validation object will override the default rules. To merge your custom rules with the default rules,
|
|
92
|
+
set `extendDefaultValidation` to `true`.
|
|
93
|
+
|
|
94
|
+
<Source
|
|
95
|
+
dark
|
|
96
|
+
language="javascript"
|
|
97
|
+
code={`
|
|
98
|
+
validation: {
|
|
99
|
+
extendDefaultValidation: true,
|
|
100
|
+
list: [
|
|
101
|
+
{
|
|
102
|
+
label: 'Always true',
|
|
103
|
+
key: 'alwaysTrue',
|
|
104
|
+
validate: () => true
|
|
105
|
+
},
|
|
106
|
+
}`}
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
#### Modify default rules
|
|
110
|
+
|
|
111
|
+
You can also modify the default rules. For example, if you want to change the character limit, you must pass the same
|
|
112
|
+
key as the default rule, `minLength`, in this case, and then customize the rule to your use case.
|
|
113
|
+
|
|
114
|
+
Similarly, if you want to disable one of the rules, you pass the same key as the default rule, and then set it to
|
|
115
|
+
`disabled: true`.
|
|
116
|
+
|
|
117
|
+
<Source
|
|
118
|
+
dark
|
|
119
|
+
language="javascript"
|
|
120
|
+
code={`
|
|
121
|
+
validation: {
|
|
122
|
+
extendDefaultValidation: true,
|
|
123
|
+
list: [
|
|
124
|
+
{
|
|
125
|
+
label: '18 characters',
|
|
126
|
+
key: 'minLength',
|
|
127
|
+
validate: minLength,
|
|
128
|
+
additionalArgs: [18],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
key: 'requireSpecialCharacter',
|
|
132
|
+
disabled: true,
|
|
133
|
+
},
|
|
134
|
+
}`}
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
<Canvas of={InputFieldPasswordStories.CustomValidation} />
|
|
138
|
+
|
|
139
|
+
### Hint
|
|
140
|
+
|
|
141
|
+
Hides the password validation hint when `with-hint="false"` is set. However, the validations will continue to run even
|
|
142
|
+
if the hint is hidden. Therefore, it is recommended to either provide `:validation="{}"` or implement a custom interface
|
|
143
|
+
to ensure an enhanced user experience.
|
|
144
|
+
|
|
145
|
+
<Source
|
|
146
|
+
dark
|
|
147
|
+
language="html"
|
|
148
|
+
code={`<!-- Disabled hint with custom validations -->
|
|
149
|
+
<template>
|
|
150
|
+
<bento-input-field-password
|
|
151
|
+
v-model="password"
|
|
152
|
+
name="password"
|
|
153
|
+
autocomplete="current-password"
|
|
154
|
+
label="Password input"
|
|
155
|
+
:validation="validation"
|
|
156
|
+
:with-hint="false"
|
|
157
|
+
:error-message="errors.password"
|
|
158
|
+
@input:error="inputErrorAction"
|
|
159
|
+
/>
|
|
160
|
+
</template>
|
|
161
|
+
<script setup lang="ts">
|
|
162
|
+
import { BentoButton, BentoInputFieldPassword, BentoInputFieldPasswordValidation } from '@adyen/bento-vue2';
|
|
163
|
+
import { ref, reactive } from 'vue';
|
|
164
|
+
|
|
165
|
+
const password = ref<string>();
|
|
166
|
+
const errors = reactive({
|
|
167
|
+
password: '',
|
|
168
|
+
});
|
|
169
|
+
const validation: BentoInputFieldPasswordValidation = {
|
|
170
|
+
list: [
|
|
171
|
+
{
|
|
172
|
+
label: 'isValid',
|
|
173
|
+
validate: (value: string) => value === 'pwd',
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
const inputErrorAction = () => {
|
|
178
|
+
errors.password = 'Password field has an error';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
</script>`}
|
|
182
|
+
/>
|
|
183
|
+
|
|
184
|
+
### Disabled
|
|
185
|
+
|
|
186
|
+
The disabled property determines whether the input component is enabled or disabled.
|
|
187
|
+
|
|
188
|
+
When an input component is disabled, it will not allow user interaction and the value of the input cannot be changed.
|
|
189
|
+
|
|
190
|
+
The visual representation of a disabled `input-field-password` component is as a grayed-out color or and lack of hover
|
|
191
|
+
effect.
|
|
192
|
+
|
|
193
|
+
### Readonly
|
|
194
|
+
|
|
195
|
+
Renders the `input-field-password` component in a readonly state.
|
|
196
|
+
|
|
197
|
+
In terms of functionality, read only states are focusable and submittable in a form. They're considered to be 'active'
|
|
198
|
+
from an application's point of view. User can highlight and copy the data in them.
|
|
199
|
+
|
|
200
|
+
### Error Message
|
|
201
|
+
|
|
202
|
+
When provided, it displays the error message below the input after the field has been 'touched,' meaning the user has
|
|
203
|
+
interacted with the input by focusing on it
|
|
204
|
+
|
|
205
|
+
### Error emit
|
|
206
|
+
|
|
207
|
+
When validation fails, the component will emit an event `input:error` with an object containing the error's key and
|
|
208
|
+
label. If you do not set a key for your error, it'll show up as `custom-rule-{index}`.
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
{
|
|
212
|
+
minLength: '12 characters',
|
|
213
|
+
requireLowercase: '1 lowercase (a-z)',
|
|
214
|
+
'custom-rule-1': 'Custom Rule'
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Disable validation
|
|
219
|
+
|
|
220
|
+
When creating a new password the input password field performs live validations to the input value as the user types,
|
|
221
|
+
providing real-time feedback about the input password.
|
|
222
|
+
|
|
223
|
+
In cases where the password is already created (such as login forms) and validations are performed outside the input
|
|
224
|
+
password field this functionality is no longer required and can be disabled through the `disableValidation` (default:
|
|
225
|
+
`false`) prop.
|
|
226
|
+
|
|
227
|
+
> **Important** Enabling `disableValidation` will disable the hint functionality too.
|
|
228
|
+
|
|
229
|
+
<Source
|
|
230
|
+
dark
|
|
231
|
+
language="html"
|
|
232
|
+
code={`<!-- No real-time validations & no hint -->
|
|
233
|
+
<template>
|
|
234
|
+
<bento-input-field-password
|
|
235
|
+
v-model="password"
|
|
236
|
+
name="password"
|
|
237
|
+
autocomplete="current-password"
|
|
238
|
+
disable-validation
|
|
239
|
+
label="Password input"
|
|
240
|
+
:error-message="errors.password"
|
|
241
|
+
@input:error="inputErrorAction"
|
|
242
|
+
/>
|
|
243
|
+
</template>
|
|
244
|
+
<script setup lang="ts">
|
|
245
|
+
import { BentoButton, BentoInputFieldPassword, BentoInputFieldPasswordValidation } from '@adyen/bento-vue2';
|
|
246
|
+
import { ref, reactive } from 'vue';
|
|
247
|
+
|
|
248
|
+
const password = ref<string>();
|
|
249
|
+
const errors = reactive({
|
|
250
|
+
password: '',
|
|
251
|
+
});
|
|
252
|
+
const inputErrorAction = () => {
|
|
253
|
+
errors.password = 'Password field has an error';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
</script>`}
|
|
257
|
+
/>
|
|
258
|
+
|
|
259
|
+
## Accessibility
|
|
260
|
+
|
|
261
|
+
### Keyboard interaction
|
|
262
|
+
|
|
263
|
+
| Keyboard interaction | Output |
|
|
264
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
265
|
+
| `Tab` | Focus visibly moves to input. Subsequent tab will navigate the focus to the password visibility toggle. |
|
|
266
|
+
|
|
267
|
+
### Roles, states and properties
|
|
268
|
+
|
|
269
|
+
| ARIA attribute | is automatic? | fallback used | manual override |
|
|
270
|
+
| -------------- | ------------------- | --------------------------------------------------- | --------------- |
|
|
271
|
+
| label | Yes, label property | If no label then must provide `aria-label` property | - |
|
|
272
|
+
| role | Yes, `textbox` used | - | - |
|
|
273
|
+
|
|
274
|
+
### Screenreader
|
|
275
|
+
|
|
276
|
+
By default, when the input field is focused, the validation title, suggestions, and associated list are announced via
|
|
277
|
+
`aria-describedby`. If the input contains an error, the corresponding error message is also read aloud.
|
|
278
|
+
|
|
279
|
+
As each item in the validation list is completed, the status of that item will be announced as "checked" using
|
|
280
|
+
`aria-live="polite"` and `aria-atomic="true"`. If the input changes and the item becomes unfulfilled again, it will be
|
|
281
|
+
announced as "unchecked" with the same `aria-live` and `aria-atomic` attributes.
|
|
282
|
+
|
|
283
|
+
Once all required validation items are completed, a screen reader-only announcement will indicate that the input is
|
|
284
|
+
valid using `role="alert"`.
|
|
285
|
+
|
|
286
|
+
## Resources
|
|
287
|
+
|
|
288
|
+
- [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=32638-114172)
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { action } from '@storybook/addon-actions'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoInputFieldPassword from './input-field-password.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { minLength } from '@/components/input-field-password/utilities/input-field-password.validation'; import { type BentoInputFieldPasswordValidation } from './input-field-password.types'; const meta: Meta = { title: 'Input Field Password', component: BentoInputFieldPassword, argTypes: { condensed: { table: { type: { summary: 'boolean' }, }, }, disabled: { table: { type: { summary: 'boolean' }, }, }, errorMessage: { table: { type: { summary: 'string' }, }, }, placeholder: { table: { type: { summary: 'string' }, }, }, readonly: { table: { type: { summary: 'boolean' }, }, }, modelValue: { control: { type: 'text' }, table: { type: { summary: 'string | number' }, }, }, label: { table: { type: { summary: 'string' }, }, }, // Events 'input:valid': { table: { type: { summary: '() => void' }, }, control: { disable: true }, }, // Deprecated input: { description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value instead', table: { category: 'Deprecated' }, control: { disable: true }, }, value: { control: { type: 'text' }, table: { category: 'Deprecated' }, description: 'Deprecated for usage with properties since version 2.0. Use `v-model` or `model-value` property instead', }, }, }; export default meta; type Story = StoryObj<typeof BentoInputFieldPassword>; const DEFAULT_PROPS = { label: 'Password', withHint: true, condensed: false, disabled: false, readonly: false, description: 'Enter your password', }; const CUSTOM_VALIDATION: BentoInputFieldPasswordValidation = { extendDefaultValidation: true, list: [ { label: 'Always true', key: 'alwaysTrue', validate: () => true, }, { label: '18 characters', key: 'minLength', validate: minLength, additionalArgs: [18], }, { key: 'requireSpecialCharacter', disabled: true, }, ], }; const defaultCode = ` <template> <bento-input-field-password label="Label" :error-message="errors.password" @update:model-value="updateModelValue" @input:valid="inputValidAction" @input:error="inputErrorAction" /> </template> <script setup lang="ts"> import { BentoInputFieldPassword } from '@adyen/bento-vue2' import { reactive } from 'vue'; const errors = reactive({ password: '', }); const updateModelValue = () => { // do something when the input value changes } const inputValidAction = () => { // do something when password is valid } const inputErrorAction = () => { errors.password = 'Password field has an error'; } </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputFieldPassword }, props: Object.keys(argTypes), template: ` <bento-input-field-password v-bind="args" @update:model-value="updateModelValue" @input:valid="inputValidAction" @input:error="inputErrorAction" /> `, setup(props) { return { // Values args: isVue2 ? props : _args, // Events updateModelValue: (value: string) => action('update:model-value')(value), inputErrorAction: (value: string) => action('input:error')(value), inputValidAction: action('input:valid'), }; }, }), args: DEFAULT_PROPS, parameters: storybookDocsParameter(defaultCode), }; const customValidationCode = ` <template> <bento-input-field-password label="Label" :error-message="errors.password" :validation="{ extendDefaultValidation: true, list: [ { // custom rule label: 'Always true', key: 'alwaysTrue', validate: () => true, }, { // override existing rule label: '18 characters', key: 'minLength', validate: minLength, additionalArgs: [18], }, { // disable existing rule key: 'requireSpecialCharacter', disabled: true, }, ], }" @update:model-value="updateModelValue" @input:valid="inputValidAction" @input:error="inputErrorAction" /> </template> <script setup lang="ts"> import { BentoInputFieldPassword, minLength } from '@adyen/bento-vue2' import { reactive } from 'vue'; const errors = reactive({ password: '', }); const updateModelValue = () => { // do something when the input value changes } const inputValidAction = () => { // do something when password is valid } const inputErrorAction = () => { errors.password = 'Password field has an error'; } </script> `; export const CustomValidation: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputFieldPassword }, props: Object.keys(argTypes), template: ` <bento-input-field-password v-bind="args" @update:model-value="updateModelValue" @input:valid="inputValidAction" @input:error="inputErrorAction" /> `, setup(props) { return { // Values args: isVue2 ? props : _args, // Events updateModelValue: (value: string) => action('update:model-value')(value), inputErrorAction: (value: string) => action('input:error')(value), inputValidAction: action('input:valid'), }; }, }), args: { ...DEFAULT_PROPS, validation: CUSTOM_VALIDATION, }, parameters: storybookDocsParameter(customValidationCode), };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div ref="inputFieldPasswordRef"> <bento-input-field ref="inputFieldPasswordInputRef" v-bind="inputFieldArgs" class="b-input-field-password__input" spellcheck="false" @blur="onBlur" @focus="onFocus" @update:model-value="onUpdateModelValue" @keydown="checkCapsLock" @keyup="checkCapsLock" > <template v-if="label">{{ label }}</template> <template v-else><slot /></template> <template v-if="description" #description> <slot name="description">{{ description }}</slot> </template> <template v-if="isCapsLockOn" #iconBefore> <div class="b-input-field-password__caps-lock-indicator" @mouseenter="handleCapsLockMouseEnter" @mouseleave="handleCapsLockMouseLeave" > <bento-image ref="inputFieldPasswordCapsLockIconRef" :src="CapsLockIcon" :alt="t('capsLockEnabled')" /> </div> <tooltip v-if="inputFieldPasswordCapsLockIconRef" :class="tooltipConditionalClass" :content="t('capsLockEnabled')" :disabled-focus-trap="true" :fallback-position="['top', 'bottom']" :is-shown="isTooltipDisplayed" :target-element="inputFieldPasswordCapsLockIconRef" /> </template> <template v-if="!disabled" #iconAfter> <bento-button variant="tertiary" @click="toggleInputType"> <template #iconLeft> <show-icon v-if="!isPasswordReadable" :svg-title="t('showPassword')" /> <hide-icon v-if="isPasswordReadable" :svg-title="t('hidePassword')" /> </template> </bento-button> </template> </bento-input-field> <bento-popover v-if="hasValidations" :open="isHintDisplayed" :target-element="inputFieldPasswordInputRef" :disable-focus-trap="true" :aria-label="computedValidations.title" :aria-describedby="validationListId" position="right-start" > <div :id="validationPopoverId"> <bento-typography v-if="computedValidations.title" class="b-input-field-password__validation-title"> {{ computedValidations.title }} </bento-typography> <p v-if="!hasErrors" class="b-input-field-password__validation-status--valid" role="alert"> {{ t('validationValid') }} </p> <ul :id="validationListId" class="b-input-field-password__validation-list"> <li v-for="(rule, index) in computedValidationList" :key="index" :class="passwordValidityConditionalClass[index]" aria-live="polite" aria-atomic="true" > <span class="b-input-field-password__validation-list-icon"> <checkmark-circle-fill-icon v-if="passwordValidity[index]" :svg-title="t('validationChecked')" /> <dot-icon v-else :svg-title="t('validationUnchecked')" /> </span> <bento-typography class="b-input-field-password__validation-list-text" el="span"> {{ rule.label }} </bento-typography> </li> </ul> <bento-typography v-if="computedValidations.suggestion" class="b-input-field-password__validation-suggestion" > {{ computedValidations.suggestion }} </bento-typography> </div> </bento-popover> </div> </template> <script setup lang="ts"> import { BentoButton } from '@/components/button'; import { BentoImage } from '@/components/image'; import { BentoInputField } from '@/components/input-field'; import { BentoPopover } from '@/components/popover'; import { BentoTypography } from '@/components/typography'; import { Tooltip } from '@/internal'; import type { BentoInputFieldProps } from '@/components/input-field/input-field.types'; import { BentoInputFieldPasswordDefaultRule, type BentoInputFieldPasswordErrorsList, type BentoInputFieldPasswordProps, BentoInputFieldPasswordType, type BentoInputFieldPasswordValidation, type BentoInputFieldPasswordValidationListItem, } from './input-field-password.types'; import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import DotIcon from '@adyen/ui-assets-icons-16/vue/dot'; import HideIcon from '@adyen/ui-assets-icons-16/vue/hide'; import ShowIcon from '@adyen/ui-assets-icons-16/vue/show'; import CapsLockIcon from './assets/capslock.svg'; import { useFocusWithin, watchDebounced } from '@vueuse/core'; import { computed, provide, ref, type SetupContext, useAttrs, watch } from 'vue'; import { useI18n } from '@/utils/ts/i18n'; import { minLength, requireLowercase, requireNumeric, requireSpecialCharacter, requireUppercase, } from '@/components/input-field-password/utilities/input-field-password.validation'; import { generateUid } from '@/core/utils/ts'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; import { deprecate } from '@/utils/ts/deprecate'; import messages from './messages.json'; import { INPUT_FIELD_PARENT_COMPONENT_INJECTION_KEY } from '@/components/input-field/input-field.keys'; type MessageSchema = (typeof messages)['en-US']; const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const props = withDefaults(defineProps<BentoInputFieldPasswordProps>(), { condensed: false, debounceTime: DEBOUNCE_DURATION, description: null, disabled: false, disableValidation: false, errorMessage: null, label: null, optional: false, placeholder: '', readonly: false, required: false, tooltipText: null, validation: undefined, value: undefined, modelValue: undefined, withHint: true, }); const emit = defineEmits<{ /** * Emitted when the element has lost focus */ (e: 'blur'): void; /** * Emitted when the element has received focus */ (e: 'focus'): void; /** * Emits an `input:valid` event when the value is validated against the provided validations. * This emit has a debounce and is emitted after an `input` event. */ (e: 'input:valid'): void; /** * Emits an `input:error` event when the value fails the validation. * This emit has a debounce and is emitted after an `input` event. */ (e: 'input:error', errors: BentoInputFieldPasswordErrorsList): void; /** * Emits an `input` event whenever the value in the input is changed. * @deprecated from version 2.0.0. Use `v-model` or `update:model-value` instead. */ (e: 'input', value: BentoInputFieldPasswordProps['value']): void; /** * Emits an `update:model-value` event whenever the value in the input is changed. */ (e: 'update:model-value', value: BentoInputFieldPasswordProps['modelValue']): void; }>(); const attrs = useAttrs(); const inputType = ref(BentoInputFieldPasswordType.PASSWORD); const inputValue = ref(props.modelValue ?? props.value); const isHintDisplayed = ref(false); const isTouched = ref(false); const passwordValidity = ref<Array<boolean>>([]); const errorsList = ref<BentoInputFieldPasswordErrorsList>({}); const capsLockTooltipTimeoutID = ref<ReturnType<typeof setTimeout>>(null); const isTooltipDisplayed = ref(false); const inputFieldPasswordRef = ref(null); const inputFieldPasswordInputRef = ref(null); const inputFieldPasswordCapsLockIconRef = ref(null); const isCapsLockOn = ref(false); provide(INPUT_FIELD_PARENT_COMPONENT_INJECTION_KEY, 'bento-input-field-password'); const { focused } = useFocusWithin(inputFieldPasswordRef); const { emitValue } = useFormFieldEmits<BentoInputFieldPasswordProps['modelValue']>(emit); const validationListId = generateUid('validationListId'); const validationPopoverId = generateUid('validationPopoverId'); const isPasswordReadable = computed(() => inputType.value === BentoInputFieldPasswordType.TEXT); const passwordValidityConditionalClass = computed(() => passwordValidity.value.map(validity => validity ? 'b-input-field-password__validation-list--valid' : 'b-input-field-password__validation-list--invalid' ) ); const tooltipConditionalClass = computed(() => capsLockTooltipTimeoutID.value ? 'b-input-field-password__input-tooltip--timed' : '' ); const inputFieldArgs = computed<BentoInputFieldProps & SetupContext['attrs']>(() => ({ ariaHidden: false, condensed: props.condensed, disabled: props.disabled, errorMessage: props.errorMessage, optional: props.optional, placeholder: props.placeholder, readonly: props.readonly, required: props.required, tooltipText: props.tooltipText, type: inputType.value, modelValue: inputValue.value, 'aria-haspopup': hasValidations.value ? 'dialog' : false, 'aria-describedby': hasValidations.value ? validationPopoverId : '', ...attrs, })); const computedValidations = computed<BentoInputFieldPasswordValidation>(() => { if (props.validation && !props.validation.extendDefaultValidation) { return props.validation; } const defaultValidationList = [ { label: t('validationMinLength', { numberOfCharacters: n(MIN_PASSWORD_LENGTH) }), key: BentoInputFieldPasswordDefaultRule.MIN_LENGTH, validate: minLength, }, { label: t('validationRequireLowercase', { numberOne: n(1) }), key: BentoInputFieldPasswordDefaultRule.REQUIRE_LOWERCASE, validate: requireLowercase, }, { label: t('validationRequireUppercase', { numberOne: n(1) }), key: BentoInputFieldPasswordDefaultRule.REQUIRE_UPPERCASE, validate: requireUppercase, }, { label: t('validationRequireNumeric', { numberOne: n(1) }), key: BentoInputFieldPasswordDefaultRule.REQUIRE_NUMERIC, validate: requireNumeric, }, { label: t('validationRequireSpecialCharacter', { numberOne: n(1) }), key: BentoInputFieldPasswordDefaultRule.REQUIRE_SPECIAL_CHARACTER, validate: requireSpecialCharacter, }, ]; let validationList: Array<BentoInputFieldPasswordValidationListItem> = defaultValidationList; if (props.validation) { const defaultValidationKeys: Array<string> = defaultValidationList.map(rule => rule.key); const customRuleKeys = props.validation?.list?.map(rule => rule.key); // remove from the custom list any rules with default keys (rules that are overriding existent rules) const customValidationList = props.validation?.list?.filter(rule => !defaultValidationKeys.includes(rule.key)) ?? []; // loop through the default list and replace default rule with the custom rule if it exists const validationListWithOverrides = defaultValidationList.map(rule => { if (customRuleKeys?.includes(rule.key)) { const customRule = props.validation?.list?.find(customRule => customRule.key === rule.key); return customRule; } else { return rule; } }); validationList = [...validationListWithOverrides, ...customValidationList]; } return { title: props.validation?.title ?? t('validationTitle'), suggestion: props.validation?.suggestion ?? t('validationSuggestion'), list: validationList, }; }); const computedValidationList = computed(() => computedValidations.value?.list?.filter(rule => !rule.disabled)); const hasErrors = computed(() => passwordValidity.value?.some(validation => !validation)); const hasValidations = computed( () => inputFieldPasswordInputRef.value && computedValidations.value?.list?.length > 0 ); const checkCapsLock = (e: KeyboardEvent) => { // Check if getModifierState exists and is a function in case the user's browser is autofilling if (typeof e.getModifierState === 'function') { isCapsLockOn.value = e.getModifierState('CapsLock'); } }; const toggleInputType = () => { if (isPasswordReadable.value) { inputType.value = BentoInputFieldPasswordType.PASSWORD; } else { inputType.value = BentoInputFieldPasswordType.TEXT; } }; const onUpdateModelValue = (value: string) => { if (props.disabled) { return; } inputValue.value = value; emitValue(inputValue.value); }; const onFocus = () => { emit('focus'); }; const onBlur = () => { emit('blur'); }; const checkValidity = (value: BentoInputFieldPasswordProps['value']) => { const rulesValidityObj = computedValidationList.value?.map(validationItem => ({ key: validationItem.key, label: validationItem.label, isValid: validationItem.validate(value, ...(validationItem.additionalArgs ?? [])), })); let customRuleIndex = 1; passwordValidity.value = rulesValidityObj?.map(error => error.isValid); errorsList.value = rulesValidityObj ?.filter(rule => !rule.isValid) .reduce((errorsObject, error) => { const key = error.key ?? `custom-rule-${customRuleIndex++}`; return { ...errorsObject, [key]: error.label, }; }, {}); }; const handleCapsLockMouseLeave = () => { isTooltipDisplayed.value = false; capsLockTooltipTimeoutID.value = null; }; const handleCapsLockMouseEnter = () => { isTooltipDisplayed.value = true; }; watch( () => [props.value, props.modelValue], () => { inputValue.value = props.modelValue ?? props.value; } ); watch(focused, focused => { // Always set dirty to true when blur occurs if (!focused) { isTouched.value = true; } // Disable validations & hint if "disableValidation" is "true" if (!props.disableValidation) { if (props.withHint) { isHintDisplayed.value = focused; } checkValidity(inputValue.value); } }); watch(isCapsLockOn, isCapsLockOn => { if (!isCapsLockOn) { clearTimeout(capsLockTooltipTimeoutID.value); capsLockTooltipTimeoutID.value = null; isTooltipDisplayed.value = false; } else { isTooltipDisplayed.value = true; capsLockTooltipTimeoutID.value = setTimeout(() => { capsLockTooltipTimeoutID.value = null; isTooltipDisplayed.value = false; }, TOOLTIP_TIMEOUT); } }); watchDebounced( inputValue, inputValue => { checkValidity(inputValue); if (!hasErrors.value) { emit('input:valid'); } else { emit('input:error', errorsList.value); } }, // wait for 0.5s before checking validity OR 2s of continuous typing { debounce: props.debounceTime, maxWait: DEBOUNCE_MAX_WAIT } ); if (props.value) { deprecate( 'BentoInputFieldPassword "value" property', `The use of "value" prop in "BentoInputFieldPassword" is no longer supported. Use the "v-model" or "model-value" property instead.`, '2.0.0' ); } </script> <script lang="ts"> const MIN_PASSWORD_LENGTH = 12; const DEBOUNCE_DURATION = 500; const DEBOUNCE_MAX_WAIT = 2000; const TOOLTIP_TIMEOUT = 2000; /** * The input field password is a component designed to support users' preference for entering values securely. * This is an extension of the `bento-input-field` component. * * @example * import { BentoInputFieldPassword } from '@adyen/bento-vue2'; * * export default { * components: { BentoInputFieldPassword }, * template: ` * <bento-input-field-password * label="Label" * error-message="Error" * :validation="{ * extendDefaultValidation: true, * list: [ * { label: '18 characters', key: 'minLength', validate: minLength, additionalArgs: [18] }, * { label: 'Always true', key: 'alwaysTrue', validate: () => true }, * { key: 'requireSpecialCharacter', disabled: true } * ], * }" * /> * ` * } */ export default { i18n: { messages }, name: 'bento-input-field-password', model: { prop: 'modelValue' }, }; </script> <style lang="scss" scoped src="./input-field-password.scss" />
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Canvas, Story, Meta, Source } from '@storybook/blocks';
|
|
2
|
+
import * as InputFieldPhoneNumberStories from './input-field-phone-number.stories';
|
|
3
|
+
import InputFieldPhoneNumberSupportedCountriesFilteredExample from './__tests__/input-field-phone-number-supported-countries-filtered-example.vue?raw';
|
|
4
|
+
|
|
5
|
+
<Meta of={InputFieldPhoneNumberStories} />
|
|
6
|
+
|
|
7
|
+
# Input Field Phone Number
|
|
8
|
+
|
|
9
|
+
The input field phone number can be used to display phone numbers that come with a country code.
|
|
10
|
+
|
|
11
|
+
This component uses InputField component as a base, so please refer to
|
|
12
|
+
[InputField documentation](https://adyen-bento.netlify.app/?path=/docs/components-input-field--docs#modifiers) for more
|
|
13
|
+
information.
|
|
14
|
+
|
|
15
|
+
<Canvas of={InputFieldPhoneNumberStories.Default} />
|
|
16
|
+
|
|
17
|
+
## Countries dropdown
|
|
18
|
+
|
|
19
|
+
The searchable dropdown shows country codes and phone codes. Users can search by country name or phone code to find
|
|
20
|
+
their desired result.
|
|
21
|
+
|
|
22
|
+
### Countries
|
|
23
|
+
|
|
24
|
+
Array of countries that should be displayed in the dropdown. If no `countries` array is passed, the dropdown will be
|
|
25
|
+
populated with every country.
|
|
26
|
+
|
|
27
|
+
### Supported countries
|
|
28
|
+
|
|
29
|
+
By default, the component includes all the supported countries in the dropdown list. To display a smaller subset, you
|
|
30
|
+
can import the `BentoInputFieldPhoneNumberCountries` constant and filter it as needed.
|
|
31
|
+
|
|
32
|
+
<Source dark language="html" code={InputFieldPhoneNumberSupportedCountriesFilteredExample} />
|
|
33
|
+
|
|
34
|
+
### Selected country
|
|
35
|
+
|
|
36
|
+
Pass `selectedCountry` prop to assign a selected coutry to the dropdown.
|
|
37
|
+
|
|
38
|
+
### Dynamic filtering
|
|
39
|
+
|
|
40
|
+
Dynamic filtering is enabled by default and should be used for long lists. For short lists, you can choose to disable it
|
|
41
|
+
with the `dynamicFiltering` prop.
|
|
42
|
+
|
|
43
|
+
## Modifiers
|
|
44
|
+
|
|
45
|
+
Check
|
|
46
|
+
[Input field section on modifiers](https://adyen-bento.netlify.app/?path=/docs/components-input-field--docs#modifiers).
|
|
47
|
+
|
|
48
|
+
## Number formatting
|
|
49
|
+
|
|
50
|
+
To enhance user experience, we use a localized phone number format that automatically adjusts based on the selected
|
|
51
|
+
country code. If there's no input, the placeholder text provides a formatting example. If there is input, the number is
|
|
52
|
+
formatted correctly.
|
|
53
|
+
|
|
54
|
+
If there is no selected country, we will try to parse the country from the number, if this is not possible the input
|
|
55
|
+
will not be formatted. Placeholder will not be generated if no country is selected.
|
|
56
|
+
|
|
57
|
+
**This formatting is only visual and is not passed as the actual input value.**
|
|
58
|
+
|
|
59
|
+
We are using libphonenumber-js for the number formatting.
|
|
60
|
+
|
|
61
|
+
Number formatting should always follow the E.164 standard (+[countryCode][phoneNumber]) e.g. +12025550123.
|
|
62
|
+
|
|
63
|
+
We always emit the value in the E.164 standard.
|
|
64
|
+
|
|
65
|
+
## Accessibility
|
|
66
|
+
|
|
67
|
+
Check
|
|
68
|
+
[Input field section on accessibility](https://adyen-bento.netlify.app/?path=/docs/components-input-field--docs#accessibility).
|
|
69
|
+
|
|
70
|
+
## Resources
|
|
71
|
+
|
|
72
|
+
- [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=30251-91718&m=dev)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { isVue2 } from 'vue-demi'; import { action } from '@storybook/addon-actions'; import { nextTick, onMounted, ref } from 'vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoInputFieldPhoneNumber from './input-field-phone-number.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import type { BentoInputFieldPhoneNumberDropdownProps } from './input-field-phone-number.types'; import InputFieldPhoneNumberDefaultExample from './__tests__/input-field-phone-number-default-example.vue?raw'; const meta: Meta = { title: 'Input field phone number', component: BentoInputFieldPhoneNumber, argTypes: { input: { description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value instead', table: { category: 'Deprecated' }, control: { disable: true }, }, value: { control: { type: 'text' }, table: { category: 'Deprecated' }, description: 'Deprecated for usage with properties since version 2.0. Use `v-model` or `model-value` property instead', }, }, }; export default meta; type Story = StoryObj<typeof BentoInputFieldPhoneNumber>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputFieldPhoneNumber }, props: Object.keys(argTypes), template: ` <bento-input-field-phone-number v-bind="args" :model-value="value" :selected-country="country" :error-message="errorMessage" @update:model-value="updateModelValue" @update:selected-country="onSelectedCountry" @blur="onBlur" @input:valid="validate" > <template #default v-if='args.default'> {{ args.default }} </template> <template #description v-if='args.description'> {{ args.description }} </template> </bento-input-field-phone-number> `, setup(props) { const value = ref('+12025550123'); const country = ref('US'); const errorMessage = ref(''); const isValid = ref(false); const updateErrorMessage = () => { if (!isValid.value && value.value) { errorMessage.value = 'This number is not valid.'; } else { errorMessage.value = ''; } }; const onSelectedCountry = async updatedCountry => { country.value = updatedCountry; action('update:selected-country')(updatedCountry); await nextTick(); updateErrorMessage(); }; const updateModelValue = input => { value.value = input; action('update:model-value')(input); }; const validate = (validity: boolean) => { isValid.value = validity; if (validity) { updateErrorMessage(); } action('input:valid')(validity); }; const onBlur = () => { updateErrorMessage(); action('blur')(); }; onMounted(() => { updateErrorMessage(); }); return { // Values args: isVue2 ? props : _args, value, country, errorMessage, // Methods onSelectedCountry, updateModelValue, onBlur, validate, }; }, }), args: { label: 'Phone number', description: 'Select your country and input the phone number', dropdown: { readonly: false, } satisfies BentoInputFieldPhoneNumberDropdownProps, }, parameters: storybookDocsParameter(InputFieldPhoneNumberDefaultExample), };
|