@salesforce/retail-react-app 1.0.0-preview.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/.eslintignore +7 -0
- package/.eslintrc.js +25 -0
- package/.prettierignore +4 -0
- package/.prettierrc.yaml +7 -0
- package/CHANGELOG.md +173 -0
- package/LICENSE +14 -0
- package/README.md +48 -0
- package/app/assets/svg/account.svg +3 -0
- package/app/assets/svg/alert.svg +3 -0
- package/app/assets/svg/basket.svg +3 -0
- package/app/assets/svg/brand-logo.svg +10 -0
- package/app/assets/svg/cc-amex.svg +7 -0
- package/app/assets/svg/cc-cvv.svg +8 -0
- package/app/assets/svg/cc-discover.svg +14 -0
- package/app/assets/svg/cc-mastercard.svg +8 -0
- package/app/assets/svg/cc-visa.svg +11 -0
- package/app/assets/svg/check-circle.svg +3 -0
- package/app/assets/svg/check.svg +3 -0
- package/app/assets/svg/chevron-down.svg +3 -0
- package/app/assets/svg/chevron-left.svg +3 -0
- package/app/assets/svg/chevron-right.svg +3 -0
- package/app/assets/svg/chevron-up.svg +3 -0
- package/app/assets/svg/close.svg +3 -0
- package/app/assets/svg/dashboard.svg +4 -0
- package/app/assets/svg/figma-logo.svg +14 -0
- package/app/assets/svg/file.svg +3 -0
- package/app/assets/svg/filter.svg +3 -0
- package/app/assets/svg/flag-ca.svg +5 -0
- package/app/assets/svg/flag-cn.svg +19 -0
- package/app/assets/svg/flag-fr.svg +19 -0
- package/app/assets/svg/flag-gb.svg +16 -0
- package/app/assets/svg/flag-it.svg +29 -0
- package/app/assets/svg/flag-jp.svg +10 -0
- package/app/assets/svg/flag-us.svg +7 -0
- package/app/assets/svg/github-logo.svg +40 -0
- package/app/assets/svg/hamburger.svg +8 -0
- package/app/assets/svg/heart-solid.svg +7 -0
- package/app/assets/svg/heart.svg +3 -0
- package/app/assets/svg/info.svg +3 -0
- package/app/assets/svg/like.svg +4 -0
- package/app/assets/svg/location.svg +3 -0
- package/app/assets/svg/lock.svg +3 -0
- package/app/assets/svg/paypal.svg +19 -0
- package/app/assets/svg/plug.svg +3 -0
- package/app/assets/svg/plus.svg +3 -0
- package/app/assets/svg/receipt.svg +3 -0
- package/app/assets/svg/search.svg +8 -0
- package/app/assets/svg/signout.svg +3 -0
- package/app/assets/svg/social-facebook.svg +3 -0
- package/app/assets/svg/social-instagram.svg +3 -0
- package/app/assets/svg/social-pinterest.svg +4 -0
- package/app/assets/svg/social-twitter.svg +3 -0
- package/app/assets/svg/social-youtube.svg +3 -0
- package/app/assets/svg/user.svg +3 -0
- package/app/assets/svg/visibility-off.svg +5 -0
- package/app/assets/svg/visibility.svg +3 -0
- package/app/components/_app/index.jsx +401 -0
- package/app/components/_app/index.test.js +85 -0
- package/app/components/_app/partials/above-header.jsx +10 -0
- package/app/components/_app-config/index.jsx +125 -0
- package/app/components/_app-config/index.test.js +77 -0
- package/app/components/_error/index.jsx +142 -0
- package/app/components/_error/index.test.js +25 -0
- package/app/components/action-card/index.jsx +75 -0
- package/app/components/address-display/index.jsx +30 -0
- package/app/components/basic-tile/index.jsx +65 -0
- package/app/components/basic-tile/index.test.js +23 -0
- package/app/components/breadcrumb/index.jsx +67 -0
- package/app/components/breadcrumb/index.test.js +30 -0
- package/app/components/confirmation-modal/index.jsx +111 -0
- package/app/components/confirmation-modal/index.test.js +98 -0
- package/app/components/drawer-menu/index.jsx +405 -0
- package/app/components/drawer-menu/index.test.js +33 -0
- package/app/components/dynamic-image/index.jsx +56 -0
- package/app/components/field/index.jsx +161 -0
- package/app/components/footer/index.jsx +269 -0
- package/app/components/footer/index.test.js +22 -0
- package/app/components/forms/address-fields.jsx +49 -0
- package/app/components/forms/credit-card-fields.jsx +149 -0
- package/app/components/forms/form-action-buttons.jsx +55 -0
- package/app/components/forms/login-fields.jsx +31 -0
- package/app/components/forms/password-requirements.jsx +99 -0
- package/app/components/forms/post-checkout-registration-fields.jsx +43 -0
- package/app/components/forms/profile-fields.jsx +36 -0
- package/app/components/forms/promo-code-fields.jsx +43 -0
- package/app/components/forms/registration-fields.jsx +42 -0
- package/app/components/forms/reset-password-fields.jsx +31 -0
- package/app/components/forms/state-province-options.jsx +75 -0
- package/app/components/forms/update-password-fields.jsx +49 -0
- package/app/components/forms/useAddressFields.jsx +196 -0
- package/app/components/forms/useCreditCardFields.jsx +146 -0
- package/app/components/forms/useLoginFields.jsx +52 -0
- package/app/components/forms/useProfileFields.jsx +95 -0
- package/app/components/forms/usePromoCodeFields.jsx +39 -0
- package/app/components/forms/useRegistrationFields.jsx +136 -0
- package/app/components/forms/useResetPasswordFields.jsx +40 -0
- package/app/components/forms/useUpdatePasswordFields.jsx +89 -0
- package/app/components/header/index.jsx +290 -0
- package/app/components/header/index.test.js +217 -0
- package/app/components/hero/index.jsx +84 -0
- package/app/components/hero/index.test.js +40 -0
- package/app/components/icons/index.jsx +158 -0
- package/app/components/icons/index.test.js +20 -0
- package/app/components/image-gallery/index.jsx +176 -0
- package/app/components/image-gallery/index.test.js +485 -0
- package/app/components/item-variant/index.jsx +33 -0
- package/app/components/item-variant/item-attributes.jsx +107 -0
- package/app/components/item-variant/item-image.jsx +73 -0
- package/app/components/item-variant/item-name.jsx +28 -0
- package/app/components/item-variant/item-price.jsx +117 -0
- package/app/components/link/index.jsx +32 -0
- package/app/components/link/index.test.js +72 -0
- package/app/components/links-list/index.jsx +89 -0
- package/app/components/links-list/index.test.js +62 -0
- package/app/components/list-menu/index.jsx +280 -0
- package/app/components/list-menu/index.test.js +44 -0
- package/app/components/loading-spinner/index.jsx +46 -0
- package/app/components/locale-selector/index.jsx +124 -0
- package/app/components/locale-selector/index.test.js +37 -0
- package/app/components/locale-text/index.jsx +97 -0
- package/app/components/locale-text/index.test.js +36 -0
- package/app/components/login/index.jsx +96 -0
- package/app/components/nested-accordion/index.jsx +185 -0
- package/app/components/nested-accordion/index.test.js +98 -0
- package/app/components/offline-banner/index.jsx +40 -0
- package/app/components/offline-banner/index.test.js +15 -0
- package/app/components/offline-boundary/index.jsx +104 -0
- package/app/components/offline-boundary/index.test.js +123 -0
- package/app/components/order-summary/index.jsx +331 -0
- package/app/components/page-action-placeholder/index.jsx +50 -0
- package/app/components/pagination/index.jsx +134 -0
- package/app/components/pagination/index.test.js +25 -0
- package/app/components/product-item/index.jsx +146 -0
- package/app/components/product-item/index.test.js +38 -0
- package/app/components/product-scroller/index.jsx +172 -0
- package/app/components/product-scroller/index.test.js +98 -0
- package/app/components/product-tile/index.jsx +195 -0
- package/app/components/product-tile/index.test.js +96 -0
- package/app/components/product-view/index.jsx +538 -0
- package/app/components/product-view/index.test.js +224 -0
- package/app/components/product-view-modal/index.jsx +48 -0
- package/app/components/product-view-modal/index.test.js +72 -0
- package/app/components/promo-code/index.jsx +162 -0
- package/app/components/promo-popover/index.jsx +83 -0
- package/app/components/quantity-picker/index.jsx +58 -0
- package/app/components/radio-card/index.jsx +75 -0
- package/app/components/recommended-products/index.jsx +227 -0
- package/app/components/register/index.jsx +114 -0
- package/app/components/reset-password/index.jsx +87 -0
- package/app/components/responsive/index.jsx +29 -0
- package/app/components/scroll-to-top/index.jsx +24 -0
- package/app/components/scroll-to-top/index.test.js +46 -0
- package/app/components/search/index.jsx +279 -0
- package/app/components/search/index.test.js +127 -0
- package/app/components/search/partials/recent-searches.jsx +76 -0
- package/app/components/search/partials/search-suggestions.jsx +45 -0
- package/app/components/search/partials/suggestions.jsx +43 -0
- package/app/components/section/index.jsx +68 -0
- package/app/components/seo/index.jsx +33 -0
- package/app/components/social-icons/index.jsx +101 -0
- package/app/components/social-icons/index.test.js +30 -0
- package/app/components/swatch-group/index.jsx +77 -0
- package/app/components/swatch-group/index.test.js +136 -0
- package/app/components/swatch-group/swatch.jsx +94 -0
- package/app/components/toggle-card/index.jsx +97 -0
- package/app/components/with-registration/index.jsx +58 -0
- package/app/components/with-registration/index.test.js +85 -0
- package/app/constants.js +109 -0
- package/app/contexts/index.js +92 -0
- package/app/hooks/einstein-mock-data.js +916 -0
- package/app/hooks/index.js +17 -0
- package/app/hooks/use-add-to-cart-modal.js +344 -0
- package/app/hooks/use-add-to-cart-modal.test.js +625 -0
- package/app/hooks/use-auth-modal.js +337 -0
- package/app/hooks/use-auth-modal.test.js +365 -0
- package/app/hooks/use-currency.js +20 -0
- package/app/hooks/use-currency.test.js +41 -0
- package/app/hooks/use-current-basket.js +39 -0
- package/app/hooks/use-current-customer.js +29 -0
- package/app/hooks/use-derived-product.js +77 -0
- package/app/hooks/use-derived-product.test.js +69 -0
- package/app/hooks/use-einstein.js +512 -0
- package/app/hooks/use-einstein.test.js +224 -0
- package/app/hooks/use-intersection-observer.js +64 -0
- package/app/hooks/use-limit-urls.js +31 -0
- package/app/hooks/use-limit-urls.test.js +40 -0
- package/app/hooks/use-multi-site.js +36 -0
- package/app/hooks/use-multi-site.test.js +53 -0
- package/app/hooks/use-navigation.js +37 -0
- package/app/hooks/use-navigation.test.js +109 -0
- package/app/hooks/use-page-urls.js +35 -0
- package/app/hooks/use-page-urls.test.js +39 -0
- package/app/hooks/use-pdp-search-params.js +16 -0
- package/app/hooks/use-pdp-search-params.test.js +52 -0
- package/app/hooks/use-previous.js +17 -0
- package/app/hooks/use-product-view-modal.js +93 -0
- package/app/hooks/use-product-view-modal.test.js +172 -0
- package/app/hooks/use-search-params.js +96 -0
- package/app/hooks/use-search-params.test.js +91 -0
- package/app/hooks/use-sort-urls.js +33 -0
- package/app/hooks/use-sort-urls.test.js +42 -0
- package/app/hooks/use-toast.js +68 -0
- package/app/hooks/use-toast.test.js +58 -0
- package/app/hooks/use-variant.js +32 -0
- package/app/hooks/use-variant.test.js +81 -0
- package/app/hooks/use-variation-attributes.js +138 -0
- package/app/hooks/use-variation-attributes.test.js +119 -0
- package/app/hooks/use-variation-params.js +31 -0
- package/app/hooks/use-variation-params.test.js +73 -0
- package/app/hooks/use-wish-list.js +42 -0
- package/app/main.jsx +14 -0
- package/app/mocks/basket-with-suit.js +146 -0
- package/app/mocks/empty-basket.js +39 -0
- package/app/mocks/mock-data.js +5632 -0
- package/app/mocks/product-set-winter-lookM.js +1224 -0
- package/app/mocks/searchResults.js +144 -0
- package/app/mocks/variant-750518699578M.js +434 -0
- package/app/page-designer/README.md +102 -0
- package/app/page-designer/assets/image-tile/index.jsx +51 -0
- package/app/page-designer/assets/image-tile/index.test.js +30 -0
- package/app/page-designer/assets/image-with-text/index.jsx +140 -0
- package/app/page-designer/assets/image-with-text/index.test.js +38 -0
- package/app/page-designer/assets/index.js +9 -0
- package/app/page-designer/index.js +10 -0
- package/app/page-designer/layouts/carousel/index.jsx +222 -0
- package/app/page-designer/layouts/carousel/index.test.js +43 -0
- package/app/page-designer/layouts/index.js +14 -0
- package/app/page-designer/layouts/mobileGrid1r1c/index.jsx +36 -0
- package/app/page-designer/layouts/mobileGrid1r1c/index.test.js +35 -0
- package/app/page-designer/layouts/mobileGrid2r1c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r1c/index.test.js +47 -0
- package/app/page-designer/layouts/mobileGrid2r2c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r2c/index.test.js +71 -0
- package/app/page-designer/layouts/mobileGrid2r3c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid2r3c/index.test.js +95 -0
- package/app/page-designer/layouts/mobileGrid3r1c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid3r1c/index.test.js +59 -0
- package/app/page-designer/layouts/mobileGrid3r2c/index.jsx +37 -0
- package/app/page-designer/layouts/mobileGrid3r2c/index.test.js +95 -0
- package/app/page-designer/utils.js +14 -0
- package/app/pages/account/addresses.jsx +382 -0
- package/app/pages/account/addresses.test.js +120 -0
- package/app/pages/account/constant.js +57 -0
- package/app/pages/account/index.jsx +237 -0
- package/app/pages/account/index.test.js +188 -0
- package/app/pages/account/order-detail.jsx +397 -0
- package/app/pages/account/order-history.jsx +264 -0
- package/app/pages/account/orders.jsx +30 -0
- package/app/pages/account/orders.test.js +95 -0
- package/app/pages/account/profile.jsx +357 -0
- package/app/pages/account/wishlist/index.jsx +195 -0
- package/app/pages/account/wishlist/index.mock.js +1481 -0
- package/app/pages/account/wishlist/index.test.js +56 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +170 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.mock.js +1623 -0
- package/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +99 -0
- package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +120 -0
- package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +391 -0
- package/app/pages/cart/index.jsx +476 -0
- package/app/pages/cart/index.test.js +481 -0
- package/app/pages/cart/partials/cart-cta.jsx +46 -0
- package/app/pages/cart/partials/cart-secondary-button-group.jsx +135 -0
- package/app/pages/cart/partials/cart-secondary-button-group.test.js +103 -0
- package/app/pages/cart/partials/cart-skeleton.jsx +93 -0
- package/app/pages/cart/partials/cart-title.jsx +27 -0
- package/app/pages/cart/partials/empty-cart.jsx +86 -0
- package/app/pages/checkout/confirmation.jsx +541 -0
- package/app/pages/checkout/confirmation.mock.js +450 -0
- package/app/pages/checkout/confirmation.test.js +114 -0
- package/app/pages/checkout/index.jsx +169 -0
- package/app/pages/checkout/index.test.js +582 -0
- package/app/pages/checkout/partials/cc-radio-group.jsx +122 -0
- package/app/pages/checkout/partials/checkout-footer.jsx +140 -0
- package/app/pages/checkout/partials/checkout-footer.test.js +16 -0
- package/app/pages/checkout/partials/checkout-header.jsx +54 -0
- package/app/pages/checkout/partials/checkout-header.test.js +16 -0
- package/app/pages/checkout/partials/checkout-skeleton.jsx +52 -0
- package/app/pages/checkout/partials/contact-info.jsx +251 -0
- package/app/pages/checkout/partials/contact-info.test.js +43 -0
- package/app/pages/checkout/partials/payment-form.jsx +97 -0
- package/app/pages/checkout/partials/payment.jsx +276 -0
- package/app/pages/checkout/partials/shipping-address-selection.jsx +377 -0
- package/app/pages/checkout/partials/shipping-address.jsx +132 -0
- package/app/pages/checkout/partials/shipping-options.jsx +232 -0
- package/app/pages/checkout/util/checkout-context.js +94 -0
- package/app/pages/home/data.js +134 -0
- package/app/pages/home/index.jsx +301 -0
- package/app/pages/home/index.test.js +23 -0
- package/app/pages/login/index.jsx +123 -0
- package/app/pages/login/index.test.js +229 -0
- package/app/pages/login-redirect/index.jsx +23 -0
- package/app/pages/login-redirect/index.test.js +16 -0
- package/app/pages/page-not-found/index.jsx +90 -0
- package/app/pages/page-not-found/index.test.js +31 -0
- package/app/pages/product-detail/index.jsx +394 -0
- package/app/pages/product-detail/index.mock.js +197 -0
- package/app/pages/product-detail/index.test.js +162 -0
- package/app/pages/product-detail/partials/information-accordion.jsx +121 -0
- package/app/pages/product-list/index.jsx +735 -0
- package/app/pages/product-list/index.test.js +180 -0
- package/app/pages/product-list/partials/above-page-header.jsx +10 -0
- package/app/pages/product-list/partials/checkbox-refinements.jsx +41 -0
- package/app/pages/product-list/partials/checkbox-refinements.test.js +53 -0
- package/app/pages/product-list/partials/color-refinements.jsx +88 -0
- package/app/pages/product-list/partials/empty-results.jsx +118 -0
- package/app/pages/product-list/partials/link-refinements.jsx +38 -0
- package/app/pages/product-list/partials/page-header.jsx +42 -0
- package/app/pages/product-list/partials/radio-refinements.jsx +60 -0
- package/app/pages/product-list/partials/refinements.jsx +144 -0
- package/app/pages/product-list/partials/selected-refinements.jsx +100 -0
- package/app/pages/product-list/partials/size-refinements.jsx +55 -0
- package/app/pages/registration/index.jsx +87 -0
- package/app/pages/registration/index.test.jsx +132 -0
- package/app/pages/reset-password/index.jsx +112 -0
- package/app/pages/reset-password/index.test.jsx +141 -0
- package/app/request-processor.js +118 -0
- package/app/request-processor.test.js +23 -0
- package/app/routes.jsx +111 -0
- package/app/routes.test.js +13 -0
- package/app/ssr.js +70 -0
- package/app/static/ico/favicon.ico +0 -0
- package/app/static/img/global/app-icon-192.png +0 -0
- package/app/static/img/global/app-icon-512.png +0 -0
- package/app/static/img/global/apple-touch-icon.png +0 -0
- package/app/static/img/hero.png +0 -0
- package/app/static/manifest.json +19 -0
- package/app/static/robots.txt +2 -0
- package/app/theme/components/base/accordion.js +21 -0
- package/app/theme/components/base/alert.js +17 -0
- package/app/theme/components/base/badge.js +25 -0
- package/app/theme/components/base/button.js +77 -0
- package/app/theme/components/base/checkbox.js +30 -0
- package/app/theme/components/base/container.js +17 -0
- package/app/theme/components/base/drawer.js +26 -0
- package/app/theme/components/base/formLabel.js +13 -0
- package/app/theme/components/base/icon.js +13 -0
- package/app/theme/components/base/input.js +44 -0
- package/app/theme/components/base/modal.js +11 -0
- package/app/theme/components/base/popover.js +61 -0
- package/app/theme/components/base/radio.js +33 -0
- package/app/theme/components/base/select.js +15 -0
- package/app/theme/components/base/skeleton.js +12 -0
- package/app/theme/components/base/tooltip.js +19 -0
- package/app/theme/components/project/_app.js +25 -0
- package/app/theme/components/project/breadcrumb.js +25 -0
- package/app/theme/components/project/checkout-footer.js +35 -0
- package/app/theme/components/project/drawer-menu.js +66 -0
- package/app/theme/components/project/footer.js +84 -0
- package/app/theme/components/project/header.js +84 -0
- package/app/theme/components/project/image-gallery.js +59 -0
- package/app/theme/components/project/links-list.js +43 -0
- package/app/theme/components/project/list-menu.js +91 -0
- package/app/theme/components/project/locale-selector.js +42 -0
- package/app/theme/components/project/nested-accordion.js +26 -0
- package/app/theme/components/project/offline-banner.js +25 -0
- package/app/theme/components/project/pagination.js +22 -0
- package/app/theme/components/project/product-tile.js +32 -0
- package/app/theme/components/project/social-icons.js +52 -0
- package/app/theme/components/project/swatch-group.js +115 -0
- package/app/theme/foundations/colors.js +170 -0
- package/app/theme/foundations/gradients.js +9 -0
- package/app/theme/foundations/layerStyles.js +41 -0
- package/app/theme/foundations/shadows.js +9 -0
- package/app/theme/foundations/sizes.js +18 -0
- package/app/theme/foundations/space.js +9 -0
- package/app/theme/foundations/styles.js +21 -0
- package/app/theme/index.js +104 -0
- package/app/utils/cc-utils.js +112 -0
- package/app/utils/cc-utils.test.js +41 -0
- package/app/utils/image-groups-utils.js +62 -0
- package/app/utils/image-groups-utils.test.js +65 -0
- package/app/utils/locale.js +78 -0
- package/app/utils/locale.test.js +112 -0
- package/app/utils/password-utils.js +21 -0
- package/app/utils/phone-utils.js +22 -0
- package/app/utils/phone-utils.test.js +15 -0
- package/app/utils/product-utils.js +35 -0
- package/app/utils/product-utils.test.js +51 -0
- package/app/utils/responsive-image.js +198 -0
- package/app/utils/responsive-image.test.js +170 -0
- package/app/utils/routes-utils.js +111 -0
- package/app/utils/routes-utils.test.js +291 -0
- package/app/utils/site-utils.js +222 -0
- package/app/utils/site-utils.test.js +376 -0
- package/app/utils/test-utils.js +257 -0
- package/app/utils/url.js +291 -0
- package/app/utils/url.test.js +421 -0
- package/app/utils/utils.js +201 -0
- package/app/utils/utils.test.js +182 -0
- package/babel.config.js +7 -0
- package/cache-hash-config.json +8 -0
- package/config/default.js +64 -0
- package/config/mocks/default.js +131 -0
- package/config/sites.js +78 -0
- package/jest-setup.js +191 -0
- package/jest.config.js +50 -0
- package/jsconfig.json +13 -0
- package/package.json +105 -0
- package/scripts/extract-default-messages.js +92 -0
- package/tests/lighthouserc.js +37 -0
- package/translations/README.md +127 -0
- package/translations/compiled/de-DE.json +3212 -0
- package/translations/compiled/en-GB.json +3212 -0
- package/translations/compiled/en-US.json +3212 -0
- package/translations/compiled/en-XA.json +6948 -0
- package/translations/compiled/es-MX.json +3216 -0
- package/translations/compiled/fr-FR.json +3216 -0
- package/translations/compiled/it-IT.json +3188 -0
- package/translations/compiled/ja-JP.json +3200 -0
- package/translations/compiled/ko-KR.json +3180 -0
- package/translations/compiled/pt-BR.json +3220 -0
- package/translations/compiled/zh-CN.json +3212 -0
- package/translations/compiled/zh-TW.json +3208 -0
- package/translations/de-DE.json +1417 -0
- package/translations/en-GB.json +1417 -0
- package/translations/en-US.json +1417 -0
- package/translations/es-MX.json +1417 -0
- package/translations/fr-FR.json +1417 -0
- package/translations/it-IT.json +1417 -0
- package/translations/ja-JP.json +1417 -0
- package/translations/ko-KR.json +1417 -0
- package/translations/pt-BR.json +1417 -0
- package/translations/zh-CN.json +1417 -0
- package/translations/zh-TW.json +1417 -0
- package/worker/main.js +36 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react'
|
|
8
|
+
import PropTypes from 'prop-types'
|
|
9
|
+
import {Box, useRadio, useRadioGroup} from '@chakra-ui/react'
|
|
10
|
+
import {CheckIcon} from '@salesforce/retail-react-app/app/components/icons'
|
|
11
|
+
|
|
12
|
+
const RadioCardGroupContext = React.createContext()
|
|
13
|
+
|
|
14
|
+
export const RadioCard = (props) => {
|
|
15
|
+
const getRadioProps = React.useContext(RadioCardGroupContext)
|
|
16
|
+
const {getInputProps, getCheckboxProps} = useRadio(getRadioProps(props))
|
|
17
|
+
|
|
18
|
+
const input = getInputProps()
|
|
19
|
+
const checkbox = getCheckboxProps()
|
|
20
|
+
return (
|
|
21
|
+
<Box as="label">
|
|
22
|
+
<input {...input} />
|
|
23
|
+
<Box
|
|
24
|
+
{...checkbox}
|
|
25
|
+
aria-hidden={false}
|
|
26
|
+
position="relative"
|
|
27
|
+
cursor="pointer"
|
|
28
|
+
border="1px solid"
|
|
29
|
+
borderColor="gray.200"
|
|
30
|
+
borderRadius="base"
|
|
31
|
+
height="full"
|
|
32
|
+
_checked={{
|
|
33
|
+
borderColor: 'blue.600'
|
|
34
|
+
}}
|
|
35
|
+
_focus={{
|
|
36
|
+
boxShadow: 'outline'
|
|
37
|
+
}}
|
|
38
|
+
px={4}
|
|
39
|
+
py={4}
|
|
40
|
+
>
|
|
41
|
+
{input.checked && (
|
|
42
|
+
<Box
|
|
43
|
+
position="absolute"
|
|
44
|
+
top={0}
|
|
45
|
+
right={0}
|
|
46
|
+
w={0}
|
|
47
|
+
h={0}
|
|
48
|
+
borderStyle="solid"
|
|
49
|
+
borderWidth="0 38px 38px 0"
|
|
50
|
+
borderColor="transparent"
|
|
51
|
+
borderRightColor="blue.600"
|
|
52
|
+
>
|
|
53
|
+
<CheckIcon color="white" position="absolute" right="-40px" top="1px" />
|
|
54
|
+
</Box>
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
{props.children}
|
|
58
|
+
</Box>
|
|
59
|
+
</Box>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const RadioCardGroup = (props) => {
|
|
64
|
+
const {getRootProps, getRadioProps} = useRadioGroup(props)
|
|
65
|
+
const group = getRootProps()
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<RadioCardGroupContext.Provider value={getRadioProps}>
|
|
69
|
+
<Box {...group}>{props.children}</Box>
|
|
70
|
+
</RadioCardGroupContext.Provider>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
RadioCard.propTypes = {children: PropTypes.any}
|
|
75
|
+
RadioCardGroup.propTypes = {children: PropTypes.any}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2022, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {useEffect, useRef, useState} from 'react'
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
import {useIntl} from 'react-intl'
|
|
11
|
+
import {Button} from '@chakra-ui/react'
|
|
12
|
+
import ProductScroller from '@salesforce/retail-react-app/app/components/product-scroller'
|
|
13
|
+
import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
|
|
14
|
+
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
|
|
15
|
+
import useIntersectionObserver from '@salesforce/retail-react-app/app/hooks/use-intersection-observer'
|
|
16
|
+
import {useWishList} from '@salesforce/retail-react-app/app/hooks/use-wish-list'
|
|
17
|
+
|
|
18
|
+
import {useToast} from '@salesforce/retail-react-app/app/hooks/use-toast'
|
|
19
|
+
import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation'
|
|
20
|
+
import {
|
|
21
|
+
API_ERROR_MESSAGE,
|
|
22
|
+
TOAST_ACTION_VIEW_WISHLIST,
|
|
23
|
+
TOAST_MESSAGE_ADDED_TO_WISHLIST,
|
|
24
|
+
TOAST_MESSAGE_REMOVED_FROM_WISHLIST
|
|
25
|
+
} from '@salesforce/retail-react-app/app/constants'
|
|
26
|
+
import {useShopperCustomersMutation} from '@salesforce/commerce-sdk-react'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A component for fetching and rendering product recommendations from the Einstein API
|
|
30
|
+
* by a zone or a recommender name.
|
|
31
|
+
*/
|
|
32
|
+
const RecommendedProducts = ({zone, recommender, products, title, shouldFetch, ...props}) => {
|
|
33
|
+
const {
|
|
34
|
+
isLoading,
|
|
35
|
+
recommendations,
|
|
36
|
+
getZoneRecommendations,
|
|
37
|
+
getRecommendations,
|
|
38
|
+
sendClickReco,
|
|
39
|
+
sendViewReco
|
|
40
|
+
} = useEinstein()
|
|
41
|
+
const {data: customer} = useCurrentCustomer()
|
|
42
|
+
const {customerId} = customer
|
|
43
|
+
const {data: wishlist} = useWishList()
|
|
44
|
+
|
|
45
|
+
const createCustomerProductListItem = useShopperCustomersMutation(
|
|
46
|
+
'createCustomerProductListItem'
|
|
47
|
+
)
|
|
48
|
+
const deleteCustomerProductListItem = useShopperCustomersMutation(
|
|
49
|
+
'deleteCustomerProductListItem'
|
|
50
|
+
)
|
|
51
|
+
const toast = useToast()
|
|
52
|
+
const navigate = useNavigation()
|
|
53
|
+
const {formatMessage} = useIntl()
|
|
54
|
+
|
|
55
|
+
const ref = useRef()
|
|
56
|
+
const isOnScreen = useIntersectionObserver(ref, {useOnce: true})
|
|
57
|
+
const [_products, setProducts] = useState(products)
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
// Check if the component should fetch results or not. This is useful
|
|
61
|
+
// when you are still waiting on additional data, like `products`.
|
|
62
|
+
if (typeof shouldFetch === 'function' && !shouldFetch()) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Fetch either zone or recommender, but not both. If a zone and recommender
|
|
67
|
+
// name are both provided, `zone` takes precendence.
|
|
68
|
+
if (zone) {
|
|
69
|
+
getZoneRecommendations(zone, _products)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
if (recommender) {
|
|
73
|
+
getRecommendations(recommender, _products)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
}, [zone, recommender, _products])
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
// This is an optimization that eliminates superfluous rerenders/fetching by
|
|
80
|
+
// keeping a copy of the `products` array prop in state for shallow comparison.
|
|
81
|
+
if (!Array.isArray(products)) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
products.length !== _products?.length ||
|
|
86
|
+
!products.every((val, index) => val === _products?.[index])
|
|
87
|
+
) {
|
|
88
|
+
setProducts(products)
|
|
89
|
+
}
|
|
90
|
+
}, [products])
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (isOnScreen && recommendations?.recs) {
|
|
94
|
+
sendViewReco(
|
|
95
|
+
{
|
|
96
|
+
recommenderName: recommendations.recommenderName,
|
|
97
|
+
__recoUUID: recommendations.recoUUID
|
|
98
|
+
},
|
|
99
|
+
recommendations.recs.map((rec) => ({id: rec.id}))
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}, [isOnScreen, recommendations])
|
|
103
|
+
|
|
104
|
+
// The component should remove itself altogether if it has no recommendations
|
|
105
|
+
// and we aren't loading any.
|
|
106
|
+
if (!isLoading && (!recommendations || recommendations.length < 1)) {
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// TODO: DRY this handler when intl provider is available globally
|
|
111
|
+
const addItemToWishlist = async (product) => {
|
|
112
|
+
try {
|
|
113
|
+
if (!wishlist || !customerId) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
await createCustomerProductListItem.mutateAsync({
|
|
117
|
+
parameters: {
|
|
118
|
+
listId: wishlist.id,
|
|
119
|
+
customerId
|
|
120
|
+
},
|
|
121
|
+
body: {
|
|
122
|
+
quantity: 1,
|
|
123
|
+
productId: product.productId,
|
|
124
|
+
public: false,
|
|
125
|
+
priority: 1,
|
|
126
|
+
type: 'product'
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
toast({
|
|
131
|
+
title: formatMessage(TOAST_MESSAGE_ADDED_TO_WISHLIST, {quantity: 1}),
|
|
132
|
+
status: 'success',
|
|
133
|
+
action: (
|
|
134
|
+
// it would be better if we could use <Button as={Link}>
|
|
135
|
+
// but unfortunately the Link component is not compatible
|
|
136
|
+
// with Chakra Toast, since the ToastManager is rendered via portal
|
|
137
|
+
// and the toast doesn't have access to intl provider, which is a
|
|
138
|
+
// requirement of the Link component.
|
|
139
|
+
<Button variant="link" onClick={() => navigate('/account/wishlist')}>
|
|
140
|
+
{formatMessage(TOAST_ACTION_VIEW_WISHLIST)}
|
|
141
|
+
</Button>
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
} catch {
|
|
145
|
+
toast({
|
|
146
|
+
title: formatMessage(API_ERROR_MESSAGE),
|
|
147
|
+
status: 'error'
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const removeItemFromWishlist = async (product) => {
|
|
153
|
+
try {
|
|
154
|
+
const wishlistItem = wishlist?.customerProductListItems?.find(
|
|
155
|
+
(item) => item.productId === product.productId
|
|
156
|
+
)
|
|
157
|
+
if (!wishlistItem || !wishlist || !customerId) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
await deleteCustomerProductListItem.mutateAsync({
|
|
161
|
+
parameters: {
|
|
162
|
+
customerId,
|
|
163
|
+
itemId: wishlistItem.id,
|
|
164
|
+
listId: wishlist.id
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
toast({
|
|
168
|
+
title: formatMessage(TOAST_MESSAGE_REMOVED_FROM_WISHLIST),
|
|
169
|
+
status: 'success',
|
|
170
|
+
id: product.productId
|
|
171
|
+
})
|
|
172
|
+
} catch {
|
|
173
|
+
toast({
|
|
174
|
+
title: formatMessage(API_ERROR_MESSAGE),
|
|
175
|
+
status: 'error'
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<ProductScroller
|
|
182
|
+
ref={ref}
|
|
183
|
+
title={title || recommendations?.displayMessage}
|
|
184
|
+
products={recommendations.recs}
|
|
185
|
+
isLoading={isLoading}
|
|
186
|
+
productTileProps={(product) => ({
|
|
187
|
+
onClick: () => {
|
|
188
|
+
sendClickReco(
|
|
189
|
+
{
|
|
190
|
+
recommenderName: recommendations.recommenderName,
|
|
191
|
+
__recoUUID: recommendations.recoUUID
|
|
192
|
+
},
|
|
193
|
+
product
|
|
194
|
+
)
|
|
195
|
+
},
|
|
196
|
+
enableFavourite: true,
|
|
197
|
+
isFavourite: wishlist?.customerProductListItems?.some(
|
|
198
|
+
(item) => item.productId === product?.productId
|
|
199
|
+
),
|
|
200
|
+
onFavouriteToggle: (isFavourite) => {
|
|
201
|
+
const action = isFavourite ? removeItemFromWishlist : addItemToWishlist
|
|
202
|
+
return action(product)
|
|
203
|
+
}
|
|
204
|
+
})}
|
|
205
|
+
{...props}
|
|
206
|
+
/>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
RecommendedProducts.propTypes = {
|
|
211
|
+
/* The title to appear above the product scroller */
|
|
212
|
+
title: PropTypes.any,
|
|
213
|
+
|
|
214
|
+
/* The zone to request */
|
|
215
|
+
zone: PropTypes.string,
|
|
216
|
+
|
|
217
|
+
/* The recommender to request */
|
|
218
|
+
recommender: PropTypes.string,
|
|
219
|
+
|
|
220
|
+
/* The products to use for recommendation context */
|
|
221
|
+
products: PropTypes.arrayOf(PropTypes.object),
|
|
222
|
+
|
|
223
|
+
/* Callback to determine if the component should fetch results */
|
|
224
|
+
shouldFetch: PropTypes.func
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export default RecommendedProducts
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {Fragment} from 'react'
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
import {FormattedMessage} from 'react-intl'
|
|
11
|
+
import {Alert, Button, Stack, Text, Link as ChakraLink} from '@chakra-ui/react'
|
|
12
|
+
import {AlertIcon, BrandLogo} from '@salesforce/retail-react-app/app/components/icons'
|
|
13
|
+
import {noop} from '@salesforce/retail-react-app/app/utils/utils'
|
|
14
|
+
import RegistrationFields from '@salesforce/retail-react-app/app/components/forms/registration-fields'
|
|
15
|
+
import Link from '@salesforce/retail-react-app/app/components/link'
|
|
16
|
+
|
|
17
|
+
const RegisterForm = ({submitForm, clickSignIn = noop, form}) => {
|
|
18
|
+
return (
|
|
19
|
+
<Fragment>
|
|
20
|
+
<Stack justify="center" align="center" spacing={8}>
|
|
21
|
+
<BrandLogo width="60px" height="auto" />
|
|
22
|
+
<Stack spacing={2}>
|
|
23
|
+
<Text align="center" fontSize="xl" fontWeight="semibold">
|
|
24
|
+
<FormattedMessage
|
|
25
|
+
defaultMessage="Let's get started!"
|
|
26
|
+
id="register_form.heading.lets_get_started"
|
|
27
|
+
/>
|
|
28
|
+
</Text>
|
|
29
|
+
<Text fontSize="sm" align="center" color="gray.700">
|
|
30
|
+
<FormattedMessage
|
|
31
|
+
defaultMessage="Create an account and get first access to the very best products, inspiration and community."
|
|
32
|
+
id="register_form.message.create_an_account"
|
|
33
|
+
/>
|
|
34
|
+
</Text>
|
|
35
|
+
</Stack>
|
|
36
|
+
</Stack>
|
|
37
|
+
<form
|
|
38
|
+
onSubmit={form.handleSubmit(submitForm)}
|
|
39
|
+
data-testid="sf-auth-modal-form-register"
|
|
40
|
+
>
|
|
41
|
+
<Stack paddingTop={8} spacing={8} paddingLeft={4} paddingRight={4}>
|
|
42
|
+
{form.formState.errors?.global && (
|
|
43
|
+
<Alert status="error">
|
|
44
|
+
<AlertIcon color="red.500" boxSize={4} />
|
|
45
|
+
<Text fontSize="sm" ml={3}>
|
|
46
|
+
{form.formState.errors.global.message}
|
|
47
|
+
</Text>
|
|
48
|
+
</Alert>
|
|
49
|
+
)}
|
|
50
|
+
<RegistrationFields form={form} />
|
|
51
|
+
<Stack spacing={6}>
|
|
52
|
+
<Button
|
|
53
|
+
type="submit"
|
|
54
|
+
onClick={() => form.clearErrors('global')}
|
|
55
|
+
isLoading={form.formState.isSubmitting}
|
|
56
|
+
>
|
|
57
|
+
<FormattedMessage
|
|
58
|
+
defaultMessage="Create Account"
|
|
59
|
+
id="register_form.button.create_account"
|
|
60
|
+
/>
|
|
61
|
+
</Button>
|
|
62
|
+
|
|
63
|
+
<Stack direction="row" spacing={1} justify="center">
|
|
64
|
+
<Text fontSize="sm">
|
|
65
|
+
<FormattedMessage
|
|
66
|
+
defaultMessage="Already have an account?"
|
|
67
|
+
id="register_form.message.already_have_account"
|
|
68
|
+
/>
|
|
69
|
+
</Text>
|
|
70
|
+
<Button variant="link" size="sm" onClick={clickSignIn}>
|
|
71
|
+
<FormattedMessage
|
|
72
|
+
defaultMessage="Sign in"
|
|
73
|
+
id="register_form.action.sign_in"
|
|
74
|
+
/>
|
|
75
|
+
</Button>
|
|
76
|
+
</Stack>
|
|
77
|
+
|
|
78
|
+
<Text fontSize="sm" align="center">
|
|
79
|
+
<FormattedMessage
|
|
80
|
+
id="register_form.message.agree_to_policy_terms"
|
|
81
|
+
defaultMessage="By creating an account, you agree to Salesforce <policy>Privacy Policy</policy> and <terms>Terms & Conditions</terms>"
|
|
82
|
+
values={{
|
|
83
|
+
policy: (chunks) => (
|
|
84
|
+
<ChakraLink as={Link} to="/privacy-policy" color="blue.600">
|
|
85
|
+
{chunks}
|
|
86
|
+
</ChakraLink>
|
|
87
|
+
),
|
|
88
|
+
|
|
89
|
+
terms: (chunks) => (
|
|
90
|
+
<ChakraLink
|
|
91
|
+
as={Link}
|
|
92
|
+
to="/terms-conditions"
|
|
93
|
+
color="blue.600"
|
|
94
|
+
>
|
|
95
|
+
{chunks}
|
|
96
|
+
</ChakraLink>
|
|
97
|
+
)
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
</Text>
|
|
101
|
+
</Stack>
|
|
102
|
+
</Stack>
|
|
103
|
+
</form>
|
|
104
|
+
</Fragment>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
RegisterForm.propTypes = {
|
|
109
|
+
submitForm: PropTypes.func,
|
|
110
|
+
clickSignIn: PropTypes.func,
|
|
111
|
+
form: PropTypes.object
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default RegisterForm
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {Fragment} from 'react'
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
import {FormattedMessage} from 'react-intl'
|
|
11
|
+
import {Alert, Button, Stack, Text} from '@chakra-ui/react'
|
|
12
|
+
import {AlertIcon, BrandLogo} from '@salesforce/retail-react-app/app/components/icons'
|
|
13
|
+
import {noop} from '@salesforce/retail-react-app/app/utils/utils'
|
|
14
|
+
import ResetPasswordFields from '@salesforce/retail-react-app/app/components/forms/reset-password-fields'
|
|
15
|
+
|
|
16
|
+
const ResetPasswordForm = ({submitForm, clickSignIn = noop, form}) => {
|
|
17
|
+
return (
|
|
18
|
+
<Fragment>
|
|
19
|
+
<Stack justify="center" align="center" spacing={8}>
|
|
20
|
+
<BrandLogo width="60px" height="auto" />
|
|
21
|
+
<Stack spacing={2}>
|
|
22
|
+
<Text align="center" fontSize="xl" fontWeight="semibold">
|
|
23
|
+
<FormattedMessage
|
|
24
|
+
defaultMessage="Reset Password"
|
|
25
|
+
id="reset_password_form.title.reset_password"
|
|
26
|
+
/>
|
|
27
|
+
</Text>
|
|
28
|
+
<Text fontSize="sm" align="center" color="gray.700">
|
|
29
|
+
<FormattedMessage
|
|
30
|
+
defaultMessage="Enter your email to receive instructions on how to reset your password"
|
|
31
|
+
id="reset_password_form.message.enter_your_email"
|
|
32
|
+
/>
|
|
33
|
+
</Text>
|
|
34
|
+
</Stack>
|
|
35
|
+
</Stack>
|
|
36
|
+
<form onSubmit={form.handleSubmit(submitForm)} data-testid="sf-auth-modal-form">
|
|
37
|
+
<Stack paddingTop={8} spacing={8} paddingLeft={4} paddingRight={4}>
|
|
38
|
+
{form.formState.errors?.global && (
|
|
39
|
+
<Alert status="error">
|
|
40
|
+
<AlertIcon color="red.500" boxSize={4} />
|
|
41
|
+
<Text fontSize="sm" ml={3}>
|
|
42
|
+
{form.formState.errors.global.message}
|
|
43
|
+
</Text>
|
|
44
|
+
</Alert>
|
|
45
|
+
)}
|
|
46
|
+
<ResetPasswordFields form={form} />
|
|
47
|
+
<Stack spacing={6}>
|
|
48
|
+
<Button
|
|
49
|
+
type="submit"
|
|
50
|
+
onClick={() => form.clearErrors('global')}
|
|
51
|
+
isLoading={form.formState.isSubmitting}
|
|
52
|
+
>
|
|
53
|
+
<FormattedMessage
|
|
54
|
+
defaultMessage="Reset Password"
|
|
55
|
+
id="reset_password_form.button.reset_password"
|
|
56
|
+
/>
|
|
57
|
+
</Button>
|
|
58
|
+
|
|
59
|
+
<Stack direction="row" spacing={1} justify="center">
|
|
60
|
+
<Text fontSize="sm">
|
|
61
|
+
<FormattedMessage
|
|
62
|
+
defaultMessage="Or return to"
|
|
63
|
+
id="reset_password_form.message.return_to_sign_in"
|
|
64
|
+
description="Precedes link to return to sign in"
|
|
65
|
+
/>
|
|
66
|
+
</Text>
|
|
67
|
+
<Button variant="link" size="sm" onClick={clickSignIn}>
|
|
68
|
+
<FormattedMessage
|
|
69
|
+
defaultMessage="Sign in"
|
|
70
|
+
id="reset_password_form.action.sign_in"
|
|
71
|
+
/>
|
|
72
|
+
</Button>
|
|
73
|
+
</Stack>
|
|
74
|
+
</Stack>
|
|
75
|
+
</Stack>
|
|
76
|
+
</form>
|
|
77
|
+
</Fragment>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ResetPasswordForm.propTypes = {
|
|
82
|
+
submitForm: PropTypes.func,
|
|
83
|
+
clickSignIn: PropTypes.func,
|
|
84
|
+
form: PropTypes.object
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default ResetPasswordForm
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react'
|
|
8
|
+
import PropTypes from 'prop-types'
|
|
9
|
+
import {Box} from '@chakra-ui/react'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render the children in the DOM but visually hide them on desktop
|
|
13
|
+
* @param children - isomorphic components used within a responsive design
|
|
14
|
+
*/
|
|
15
|
+
export const HideOnDesktop = ({children}) => (
|
|
16
|
+
<Box display={{base: 'block', lg: 'none'}}>{children}</Box>
|
|
17
|
+
)
|
|
18
|
+
HideOnDesktop.propTypes = {children: PropTypes.node}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Render the children in the DOM but visually hide them on mobile
|
|
22
|
+
* @param children - isomorphic components used within a responsive design
|
|
23
|
+
*/
|
|
24
|
+
export const HideOnMobile = ({children}) => (
|
|
25
|
+
<Box display={{base: 'none', lg: 'block'}}>{children}</Box>
|
|
26
|
+
)
|
|
27
|
+
HideOnMobile.propTypes = {children: PropTypes.node}
|
|
28
|
+
|
|
29
|
+
export default {HideOnMobile, HideOnDesktop}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import {useEffect} from 'react'
|
|
8
|
+
import {useLocation} from 'react-router-dom'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ScrollToTop will scroll the viewport to the top whenever the current URL
|
|
12
|
+
* changes. If this component doesn't meet the needs of your layout, take a look
|
|
13
|
+
* at this [guide]{@link https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/guides/scroll-restoration.md}.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export default function ScrollToTop() {
|
|
17
|
+
const {pathname} = useLocation()
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
window.scrollTo(0, 0)
|
|
21
|
+
}, [pathname])
|
|
22
|
+
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2021, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react'
|
|
8
|
+
import {render, waitFor} from '@testing-library/react'
|
|
9
|
+
import ScrollToTop from '@salesforce/retail-react-app/app/components/scroll-to-top/index'
|
|
10
|
+
import {Router} from 'react-router-dom'
|
|
11
|
+
import {createMemoryHistory} from 'history'
|
|
12
|
+
|
|
13
|
+
global.scrollTo = jest.fn()
|
|
14
|
+
|
|
15
|
+
describe('ScrollToTop', () => {
|
|
16
|
+
let history = createMemoryHistory({initialEntries: ['/']})
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
render(
|
|
20
|
+
<Router history={history}>
|
|
21
|
+
<ScrollToTop />
|
|
22
|
+
</Router>
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
jest.clearAllMocks()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('calls window.scrollTo when route changes', async () => {
|
|
31
|
+
expect(global.scrollTo).toHaveBeenCalledTimes(1)
|
|
32
|
+
expect(global.scrollTo).toHaveBeenCalledWith(0, 0)
|
|
33
|
+
|
|
34
|
+
history.push('/new-url')
|
|
35
|
+
await waitFor(() => {
|
|
36
|
+
expect(global.scrollTo).toHaveBeenCalledTimes(2)
|
|
37
|
+
})
|
|
38
|
+
expect(global.scrollTo).toHaveBeenCalledWith(0, 0)
|
|
39
|
+
|
|
40
|
+
history.push('/new-url2')
|
|
41
|
+
await waitFor(() => {
|
|
42
|
+
expect(global.scrollTo).toHaveBeenCalledTimes(3)
|
|
43
|
+
})
|
|
44
|
+
expect(global.scrollTo).toHaveBeenCalledWith(0, 0)
|
|
45
|
+
})
|
|
46
|
+
})
|