@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,18 @@
|
|
|
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
|
+
export default {
|
|
8
|
+
11: '2.75rem',
|
|
9
|
+
|
|
10
|
+
container: {
|
|
11
|
+
sm: '640px',
|
|
12
|
+
md: '768px',
|
|
13
|
+
lg: '1024px',
|
|
14
|
+
xl: '1200px',
|
|
15
|
+
xxl: '1440px',
|
|
16
|
+
xxxl: '1560px'
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
export default {
|
|
8
|
+
11: '2.75rem'
|
|
9
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
export default {
|
|
8
|
+
global: {
|
|
9
|
+
'html, body': {
|
|
10
|
+
backgroundColor: 'white',
|
|
11
|
+
color: 'gray.900'
|
|
12
|
+
},
|
|
13
|
+
body: {
|
|
14
|
+
minHeight: '100vh'
|
|
15
|
+
},
|
|
16
|
+
'.react-target': {
|
|
17
|
+
display: 'flex',
|
|
18
|
+
minHeight: '100vh'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
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 {extendTheme} from '@chakra-ui/react'
|
|
8
|
+
|
|
9
|
+
// Foundational style overrides
|
|
10
|
+
import styles from '@salesforce/retail-react-app/app/theme/foundations/styles'
|
|
11
|
+
import colors from '@salesforce/retail-react-app/app/theme/foundations/colors'
|
|
12
|
+
import gradients from '@salesforce/retail-react-app/app/theme/foundations/gradients'
|
|
13
|
+
import sizes from '@salesforce/retail-react-app/app/theme/foundations/sizes'
|
|
14
|
+
import space from '@salesforce/retail-react-app/app/theme/foundations/space'
|
|
15
|
+
import layerStyles from '@salesforce/retail-react-app/app/theme/foundations/layerStyles'
|
|
16
|
+
import shadows from '@salesforce/retail-react-app/app/theme/foundations/shadows'
|
|
17
|
+
|
|
18
|
+
// Base component style overrides
|
|
19
|
+
import Alert from '@salesforce/retail-react-app/app/theme/components/base/alert'
|
|
20
|
+
import Accordion from '@salesforce/retail-react-app/app/theme/components/base/accordion'
|
|
21
|
+
import Badge from '@salesforce/retail-react-app/app/theme/components/base/badge'
|
|
22
|
+
import Button from '@salesforce/retail-react-app/app/theme/components/base/button'
|
|
23
|
+
import Checkbox from '@salesforce/retail-react-app/app/theme/components/base/checkbox'
|
|
24
|
+
import Container from '@salesforce/retail-react-app/app/theme/components/base/container'
|
|
25
|
+
import Drawer from '@salesforce/retail-react-app/app/theme/components/base/drawer'
|
|
26
|
+
import FormLabel from '@salesforce/retail-react-app/app/theme/components/base/formLabel'
|
|
27
|
+
import Icon from '@salesforce/retail-react-app/app/theme/components/base/icon'
|
|
28
|
+
import Input from '@salesforce/retail-react-app/app/theme/components/base/input'
|
|
29
|
+
import Modal from '@salesforce/retail-react-app/app/theme/components/base/modal'
|
|
30
|
+
import Radio from '@salesforce/retail-react-app/app/theme/components/base/radio'
|
|
31
|
+
import Select from '@salesforce/retail-react-app/app/theme/components/base/select'
|
|
32
|
+
import Skeleton from '@salesforce/retail-react-app/app/theme/components/base/skeleton'
|
|
33
|
+
import Tooltip from '@salesforce/retail-react-app/app/theme/components/base/tooltip'
|
|
34
|
+
import Popover from '@salesforce/retail-react-app/app/theme/components/base/popover'
|
|
35
|
+
|
|
36
|
+
// Project Component style overrides
|
|
37
|
+
import App from '@salesforce/retail-react-app/app/theme/components/project/_app'
|
|
38
|
+
import Breadcrumb from '@salesforce/retail-react-app/app/theme/components/project/breadcrumb'
|
|
39
|
+
import Header from '@salesforce/retail-react-app/app/theme/components/project/header'
|
|
40
|
+
import ListMenu from '@salesforce/retail-react-app/app/theme/components/project/list-menu'
|
|
41
|
+
import Footer from '@salesforce/retail-react-app/app/theme/components/project/footer'
|
|
42
|
+
import CheckoutFooter from '@salesforce/retail-react-app/app/theme/components/project/checkout-footer'
|
|
43
|
+
import LinksList from '@salesforce/retail-react-app/app/theme/components/project/links-list'
|
|
44
|
+
import DrawerMenu from '@salesforce/retail-react-app/app/theme/components/project/drawer-menu'
|
|
45
|
+
import NestedAccordion from '@salesforce/retail-react-app/app/theme/components/project/nested-accordion'
|
|
46
|
+
import LocaleSelector from '@salesforce/retail-react-app/app/theme/components/project/locale-selector'
|
|
47
|
+
import OfflineBanner from '@salesforce/retail-react-app/app/theme/components/project/offline-banner'
|
|
48
|
+
import Pagination from '@salesforce/retail-react-app/app/theme/components/project/pagination'
|
|
49
|
+
import ProductTile from '@salesforce/retail-react-app/app/theme/components/project/product-tile'
|
|
50
|
+
import SocialIcons from '@salesforce/retail-react-app/app/theme/components/project/social-icons'
|
|
51
|
+
import SwatchGroup from '@salesforce/retail-react-app/app/theme/components/project/swatch-group'
|
|
52
|
+
import ImageGallery from '@salesforce/retail-react-app/app/theme/components/project/image-gallery'
|
|
53
|
+
|
|
54
|
+
// Please refer to the Chakra-Ui theme customization docs found
|
|
55
|
+
// here https://chakra-ui.com/docs/theming/customize-theme to learn
|
|
56
|
+
// more about extending and overriding themes for your project.
|
|
57
|
+
export const overrides = {
|
|
58
|
+
styles,
|
|
59
|
+
layerStyles,
|
|
60
|
+
colors,
|
|
61
|
+
sizes,
|
|
62
|
+
space,
|
|
63
|
+
gradients,
|
|
64
|
+
shadows,
|
|
65
|
+
components: {
|
|
66
|
+
// base components
|
|
67
|
+
Accordion,
|
|
68
|
+
Alert,
|
|
69
|
+
Badge,
|
|
70
|
+
Button,
|
|
71
|
+
Checkbox,
|
|
72
|
+
Container,
|
|
73
|
+
Drawer,
|
|
74
|
+
FormLabel,
|
|
75
|
+
Icon,
|
|
76
|
+
Input,
|
|
77
|
+
Modal,
|
|
78
|
+
Popover,
|
|
79
|
+
Radio,
|
|
80
|
+
Select,
|
|
81
|
+
Skeleton,
|
|
82
|
+
Tooltip,
|
|
83
|
+
|
|
84
|
+
// project components
|
|
85
|
+
App,
|
|
86
|
+
Breadcrumb,
|
|
87
|
+
Header,
|
|
88
|
+
Footer,
|
|
89
|
+
CheckoutFooter,
|
|
90
|
+
LinksList,
|
|
91
|
+
ListMenu,
|
|
92
|
+
DrawerMenu,
|
|
93
|
+
NestedAccordion,
|
|
94
|
+
LocaleSelector,
|
|
95
|
+
OfflineBanner,
|
|
96
|
+
SocialIcons,
|
|
97
|
+
Pagination,
|
|
98
|
+
ProductTile,
|
|
99
|
+
SwatchGroup,
|
|
100
|
+
ImageGallery
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default extendTheme(overrides)
|
|
@@ -0,0 +1,112 @@
|
|
|
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 {
|
|
8
|
+
AmexIcon,
|
|
9
|
+
DiscoverIcon,
|
|
10
|
+
MastercardIcon,
|
|
11
|
+
VisaIcon
|
|
12
|
+
} from '@salesforce/retail-react-app/app/components/icons'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Formats a credit card number against given criteria
|
|
16
|
+
* @param {string} cardNumber - The number to be formatted
|
|
17
|
+
* @param {Object} opts
|
|
18
|
+
* @param {number[]} opts.gaps - Indices for space insertion
|
|
19
|
+
* @param {number[]} opts.length - Max number lengths for output
|
|
20
|
+
* @returns {string} Formatted card number for display
|
|
21
|
+
*/
|
|
22
|
+
export const formatCreditCardNumber = (cardNumber = '', opts = {gaps: [], lengths: []}) => {
|
|
23
|
+
let trimmedNumber = cardNumber.replace(/[^0-9]/g, '')
|
|
24
|
+
let numberLength = trimmedNumber.length
|
|
25
|
+
|
|
26
|
+
if (numberLength === opts.lengths[0] + 1) {
|
|
27
|
+
trimmedNumber = trimmedNumber.substr(0, opts.lengths[0])
|
|
28
|
+
numberLength = trimmedNumber.length
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let numbers = trimmedNumber.split('')
|
|
32
|
+
|
|
33
|
+
opts.gaps.forEach((gapIndex, idx) => {
|
|
34
|
+
if (numberLength > gapIndex) {
|
|
35
|
+
numbers.splice(gapIndex + idx, 0, ' ')
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return numbers.join('')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns the icon component for a given card type
|
|
44
|
+
* @param {string} type - The card type
|
|
45
|
+
* @returns {Function|undefined} React component
|
|
46
|
+
*/
|
|
47
|
+
export const getCreditCardIcon = (type) => {
|
|
48
|
+
if (!type) {
|
|
49
|
+
return undefined
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
// Visa
|
|
53
|
+
visa: VisaIcon,
|
|
54
|
+
|
|
55
|
+
// MasterCard
|
|
56
|
+
mastercard: MastercardIcon,
|
|
57
|
+
'master card': MastercardIcon,
|
|
58
|
+
|
|
59
|
+
// American Express
|
|
60
|
+
'american express': AmexIcon,
|
|
61
|
+
'american-express': AmexIcon,
|
|
62
|
+
amex: AmexIcon,
|
|
63
|
+
|
|
64
|
+
// Discover
|
|
65
|
+
discover: DiscoverIcon
|
|
66
|
+
}[type.toLowerCase()]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns the card type string in the format the SDK expects.
|
|
71
|
+
* @param {string} - The card type as given by our cc validator
|
|
72
|
+
* @returns {string|undefined} - The card type in a format expected by the SDK
|
|
73
|
+
*/
|
|
74
|
+
export const getPaymentInstrumentCardType = (type) => {
|
|
75
|
+
if (!type) {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
visa: 'Visa',
|
|
80
|
+
mastercard: 'Master Card',
|
|
81
|
+
'american-express': 'Amex',
|
|
82
|
+
discover: 'Discover'
|
|
83
|
+
}[type]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const createCreditCardPaymentBodyFromForm = (paymentFormData) => {
|
|
87
|
+
// Using destructuring to omit properties
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
89
|
+
const {expiry, paymentInstrumentId, ...selectedPayment} = paymentFormData
|
|
90
|
+
|
|
91
|
+
// The form gives us the expiration date as `MM/YY` - so we need to split it into
|
|
92
|
+
// month and year to submit them as individual fields.
|
|
93
|
+
const [expirationMonth, expirationYear] = expiry.split('/')
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
paymentMethodId: 'CREDIT_CARD',
|
|
97
|
+
paymentCard: {
|
|
98
|
+
...selectedPayment,
|
|
99
|
+
number: selectedPayment.number.replace(/ /g, ''),
|
|
100
|
+
cardType: getPaymentInstrumentCardType(selectedPayment.cardType),
|
|
101
|
+
expirationMonth: parseInt(expirationMonth),
|
|
102
|
+
expirationYear: parseInt(`20${expirationYear}`),
|
|
103
|
+
|
|
104
|
+
// TODO: These fields are required for saving the card to the customer's
|
|
105
|
+
// account. Im not sure what they are for or how to get them, so for now
|
|
106
|
+
// we're just passing some values to make it work. Need to investigate.
|
|
107
|
+
issueNumber: '',
|
|
108
|
+
validFromMonth: 1,
|
|
109
|
+
validFromYear: 2020
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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 * as utils from '@salesforce/retail-react-app/app/utils/cc-utils'
|
|
8
|
+
|
|
9
|
+
test('formatCreditCardNumber returns number with proper spaces', () => {
|
|
10
|
+
const opts = {
|
|
11
|
+
gaps: [4, 8, 12],
|
|
12
|
+
lengths: [16]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
expect(utils.formatCreditCardNumber('41111111111111111', opts)).toBe('4111 1111 1111 1111')
|
|
16
|
+
expect(utils.formatCreditCardNumber('4111111111111111', opts)).toBe('4111 1111 1111 1111')
|
|
17
|
+
expect(utils.formatCreditCardNumber('411111111111', opts)).toBe('4111 1111 1111')
|
|
18
|
+
expect(utils.formatCreditCardNumber('4', opts)).toBe('4')
|
|
19
|
+
expect(utils.formatCreditCardNumber('', opts)).toBe('')
|
|
20
|
+
expect(utils.formatCreditCardNumber('')).toBe('')
|
|
21
|
+
expect(utils.formatCreditCardNumber()).toBe('')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('getCreditCardIcon returns icon component for given card type', () => {
|
|
25
|
+
expect(utils.getCreditCardIcon()).toBeUndefined()
|
|
26
|
+
expect(utils.getCreditCardIcon('visa').displayName).toBe('CcVisaIcon')
|
|
27
|
+
expect(utils.getCreditCardIcon('mastercard').displayName).toBe('CcMastercardIcon')
|
|
28
|
+
expect(utils.getCreditCardIcon('master card').displayName).toBe('CcMastercardIcon')
|
|
29
|
+
expect(utils.getCreditCardIcon('amex').displayName).toBe('CcAmexIcon')
|
|
30
|
+
expect(utils.getCreditCardIcon('american express').displayName).toBe('CcAmexIcon')
|
|
31
|
+
expect(utils.getCreditCardIcon('american-express').displayName).toBe('CcAmexIcon')
|
|
32
|
+
expect(utils.getCreditCardIcon('discover').displayName).toBe('CcDiscoverIcon')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('getPaymentInstrumentCardType maps card type names to API requirements', () => {
|
|
36
|
+
expect(utils.getPaymentInstrumentCardType()).toBeUndefined()
|
|
37
|
+
expect(utils.getPaymentInstrumentCardType('visa')).toBe('Visa')
|
|
38
|
+
expect(utils.getPaymentInstrumentCardType('mastercard')).toBe('Master Card')
|
|
39
|
+
expect(utils.getPaymentInstrumentCardType('american-express')).toBe('Amex')
|
|
40
|
+
expect(utils.getPaymentInstrumentCardType('discover')).toBe('Discover')
|
|
41
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Find the ImageGroup that matches the criteria supplied
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} imageGroups - The product/variations image groups you want to search.
|
|
12
|
+
* @param {Object} options - Search criteria to match on the ImageGroup object.
|
|
13
|
+
* @param {string} options.viewType - Typically this refers to image sizes like small, medium, large. But can vary based on your back-end configuration.
|
|
14
|
+
* @param {Object} options.selectedVariationAttributes - A key/value object consisting of attibute id's and their values.
|
|
15
|
+
* @returns {Object} - The ImageGroup matching the search criteria
|
|
16
|
+
*/
|
|
17
|
+
export const findImageGroupBy = (imageGroups = [], options) => {
|
|
18
|
+
let {viewType, selectedVariationAttributes = {}} = options
|
|
19
|
+
|
|
20
|
+
// Start by filtering out any imageGroup that isn't the correct viewType.
|
|
21
|
+
imageGroups = imageGroups.filter(
|
|
22
|
+
({viewType: imageGroupViewType}) => imageGroupViewType === viewType
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Not all variation attributes are reflected in images. For example, you probably
|
|
26
|
+
// won't have a separate image group for various sizes, but you might for colors. For that
|
|
27
|
+
// reason we need to know what are valid attribute values to filter on.
|
|
28
|
+
const refinableAttributeIds = [
|
|
29
|
+
...new Set(
|
|
30
|
+
imageGroups
|
|
31
|
+
.reduce((acc, {variationAttributes = []}) => [...acc, ...variationAttributes], [])
|
|
32
|
+
.map(({id}) => id)
|
|
33
|
+
)
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
// Update the `selectedVariationAttributes` by filtering out the attributes that have no
|
|
37
|
+
// representation in this imageGroup.
|
|
38
|
+
selectedVariationAttributes = Object.keys(selectedVariationAttributes).reduce((acc, curr) => {
|
|
39
|
+
return refinableAttributeIds.includes(curr)
|
|
40
|
+
? {
|
|
41
|
+
...acc,
|
|
42
|
+
[`${curr}`]: selectedVariationAttributes[curr]
|
|
43
|
+
}
|
|
44
|
+
: acc
|
|
45
|
+
}, {})
|
|
46
|
+
|
|
47
|
+
// Find the image group that has all the all the selected variation value attributes.
|
|
48
|
+
imageGroups = imageGroups.find(({variationAttributes = []}) => {
|
|
49
|
+
const selectedIds = Object.keys(selectedVariationAttributes)
|
|
50
|
+
|
|
51
|
+
return selectedIds.every((selectedId) => {
|
|
52
|
+
const selectedValue = selectedVariationAttributes[selectedId]
|
|
53
|
+
|
|
54
|
+
return variationAttributes.find(
|
|
55
|
+
({id, values}) =>
|
|
56
|
+
id === selectedId && values.every(({value}) => value === selectedValue)
|
|
57
|
+
)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return imageGroups
|
|
62
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 {findImageGroupBy} from '@salesforce/retail-react-app/app/utils/image-groups-utils'
|
|
9
|
+
|
|
10
|
+
it.each([undefined, []])('returns undefined', (groups) => {
|
|
11
|
+
expect(findImageGroupBy(groups, {})).toBeUndefined()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('returns undefined for image groups with no size match', () => {
|
|
15
|
+
expect(findImageGroupBy([{viewType: 'small'}], {viewType: 'large'})).toBeUndefined()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('returns first match for image groups with no variationAttributes', () => {
|
|
19
|
+
const groups = [{viewType: 'small'}]
|
|
20
|
+
expect(findImageGroupBy(groups, {viewType: 'small'})).toBe(groups[0])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('returns first match for image groups with empty variationAttributes', () => {
|
|
24
|
+
const groups = [{viewType: 'small', variationAttributes: []}]
|
|
25
|
+
expect(findImageGroupBy(groups, {viewType: 'small'})).toBe(groups[0])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('returns first match for image groups with no selectedVariationAttributes', () => {
|
|
29
|
+
const groups = [
|
|
30
|
+
{
|
|
31
|
+
viewType: 'small',
|
|
32
|
+
variationAttributes: [
|
|
33
|
+
{
|
|
34
|
+
id: 'color',
|
|
35
|
+
values: [
|
|
36
|
+
{
|
|
37
|
+
value: 'JJ825XX'
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
expect(findImageGroupBy(groups, {viewType: 'small'})).toBe(groups[0])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('returns first match for image groups with matching selectedVariationAttributes', () => {
|
|
48
|
+
const variation = {
|
|
49
|
+
id: 'color',
|
|
50
|
+
values: [
|
|
51
|
+
{
|
|
52
|
+
value: 'JJ825XX'
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
const groups = [
|
|
57
|
+
{
|
|
58
|
+
viewType: 'small',
|
|
59
|
+
variationAttributes: [variation]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
expect(findImageGroupBy(groups, {viewType: 'small', selectedVariationValues: variation})).toBe(
|
|
63
|
+
groups[0]
|
|
64
|
+
)
|
|
65
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
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 PropTypes from 'prop-types'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Dynamically import the translations/messages for a given locale
|
|
12
|
+
* @param {string} locale
|
|
13
|
+
* @returns {Promise<Object>} The messages (compiled in AST format) in the given locale.
|
|
14
|
+
* If the translation file is not found, return an empty object (so react-intl would fall back to the inline messages)
|
|
15
|
+
*/
|
|
16
|
+
export const fetchTranslations = async (locale) => {
|
|
17
|
+
const targetLocale =
|
|
18
|
+
typeof window === 'undefined'
|
|
19
|
+
? process.env.USE_PSEUDOLOCALE === 'true'
|
|
20
|
+
? 'en-XA'
|
|
21
|
+
: locale
|
|
22
|
+
: locale
|
|
23
|
+
|
|
24
|
+
let module
|
|
25
|
+
try {
|
|
26
|
+
module = await import(
|
|
27
|
+
`@salesforce/retail-react-app/translations/compiled/${targetLocale}.json`
|
|
28
|
+
)
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(err)
|
|
31
|
+
console.log(
|
|
32
|
+
'Loading empty messages, so that react-intl would fall back to the inline default messages'
|
|
33
|
+
)
|
|
34
|
+
return {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return module.default
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Determine the target locale by comparing the users' preferred locales with the app's supported locales
|
|
42
|
+
* @param {Object} options
|
|
43
|
+
* @param {function} [options.getUserPreferredLocales] - Identify what set of locales the user prefers
|
|
44
|
+
* @param {object} [options.l10nConfig] - The current site's l10n configuration object
|
|
45
|
+
* @returns {string} The target locale's short code
|
|
46
|
+
*/
|
|
47
|
+
export const getTargetLocale = ({getUserPreferredLocales, l10nConfig = {}} = {}) => {
|
|
48
|
+
const userPreferredLocales = getUserPreferredLocales ? getUserPreferredLocales() : []
|
|
49
|
+
const supportedLocales = l10nConfig.supportedLocales.map((locale) => locale.id)
|
|
50
|
+
const defaultLocale = l10nConfig.defaultLocale
|
|
51
|
+
|
|
52
|
+
const targetLocale = determineTargetLocale(
|
|
53
|
+
userPreferredLocales,
|
|
54
|
+
supportedLocales,
|
|
55
|
+
defaultLocale
|
|
56
|
+
)
|
|
57
|
+
return targetLocale
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Decide which locale to load
|
|
62
|
+
* @private
|
|
63
|
+
* @param {string[]} preferredLocales - All locales that the user prefers
|
|
64
|
+
* @param {string[]} supportedLocales - All locales that your app supports
|
|
65
|
+
* @param {string} fallbackLocale - App's default locale
|
|
66
|
+
* @returns {string} The target locale if there's a match. Otherwise, returns `fallbackLocale`.
|
|
67
|
+
*/
|
|
68
|
+
export const determineTargetLocale = (preferredLocales, supportedLocales, fallbackLocale) => {
|
|
69
|
+
const targetLocale = preferredLocales.filter((locale) => supportedLocales.includes(locale))[0]
|
|
70
|
+
return targetLocale || fallbackLocale
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const MESSAGE_PROPTYPE = PropTypes.shape({
|
|
74
|
+
// NOTE: defaultMessage is typically written as a string
|
|
75
|
+
// but its value can be an array when it comes from the compiled AST version
|
|
76
|
+
defaultMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired,
|
|
77
|
+
id: PropTypes.string
|
|
78
|
+
})
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
// CAUTION: This test file relies on config values that may get changed in generated projects
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
determineTargetLocale,
|
|
12
|
+
fetchTranslations,
|
|
13
|
+
getTargetLocale
|
|
14
|
+
} from '@salesforce/retail-react-app/app/utils/locale'
|
|
15
|
+
|
|
16
|
+
import {DEFAULT_LOCALE, SUPPORTED_LOCALES} from '@salesforce/retail-react-app/app/utils/test-utils'
|
|
17
|
+
|
|
18
|
+
const supportedLocales = SUPPORTED_LOCALES.map((locale) => locale.id)
|
|
19
|
+
const isMultiLocales = supportedLocales.length > 1
|
|
20
|
+
const nonSupportedLocale = 'nl-NL'
|
|
21
|
+
// Make sure this supported locale is not the default locale.
|
|
22
|
+
// Otherwise, our code would fall back to default and incorrectly pass the tests
|
|
23
|
+
const supportedLocale = isMultiLocales
|
|
24
|
+
? supportedLocales.find((locale) => locale !== DEFAULT_LOCALE)
|
|
25
|
+
: supportedLocales[0]
|
|
26
|
+
|
|
27
|
+
const testId1 = 'footer.link.privacy_policy'
|
|
28
|
+
const testId2 = 'account.accordion.button.my_account'
|
|
29
|
+
|
|
30
|
+
test('our assumptions before further testing', () => {
|
|
31
|
+
expect(supportedLocales).not.toContain(nonSupportedLocale)
|
|
32
|
+
if (isMultiLocales) {
|
|
33
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
|
34
|
+
expect(DEFAULT_LOCALE).toBe('en-GB')
|
|
35
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
|
36
|
+
expect(supportedLocale).not.toBe(DEFAULT_LOCALE)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('determineTargetLocale', () => {
|
|
41
|
+
test('default to fallback locale', () => {
|
|
42
|
+
const locale = determineTargetLocale([nonSupportedLocale], supportedLocales, DEFAULT_LOCALE)
|
|
43
|
+
expect(locale).toBe(DEFAULT_LOCALE)
|
|
44
|
+
})
|
|
45
|
+
test('matches one of the supported locales', () => {
|
|
46
|
+
const locale = determineTargetLocale([supportedLocale], supportedLocales, DEFAULT_LOCALE)
|
|
47
|
+
expect(locale).toBe(supportedLocale)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('fetchTranslations', () => {
|
|
52
|
+
test('loading the target locale', async () => {
|
|
53
|
+
const messages = await fetchTranslations(supportedLocale)
|
|
54
|
+
expect(messages[testId2]).toBeDefined()
|
|
55
|
+
})
|
|
56
|
+
test('loading the pseudo locale', async () => {
|
|
57
|
+
const messages = await fetchTranslations('en-XA')
|
|
58
|
+
expect(messages[testId1][1].value).toMatch(/Ƥřīṽȧȧƈẏ Ƥǿǿŀīƈẏ/)
|
|
59
|
+
})
|
|
60
|
+
test('handling a not-found translation file', async () => {
|
|
61
|
+
const messages = await fetchTranslations('xx-XX')
|
|
62
|
+
const emptyMessages = {}
|
|
63
|
+
expect(messages).toEqual(emptyMessages)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('getTargetLocale', () => {
|
|
68
|
+
const originalEnv = {...process.env}
|
|
69
|
+
let windowSpy
|
|
70
|
+
|
|
71
|
+
const l10nConfig = {
|
|
72
|
+
defaultLocale: DEFAULT_LOCALE,
|
|
73
|
+
supportedLocales: SUPPORTED_LOCALES
|
|
74
|
+
}
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
windowSpy = jest.spyOn(window, 'window', 'get')
|
|
77
|
+
})
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
// Reset
|
|
80
|
+
process.env = {...originalEnv}
|
|
81
|
+
windowSpy.mockRestore()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('without getUserPreferredLocales parameter', () => {
|
|
85
|
+
const targetLocale = getTargetLocale({l10nConfig})
|
|
86
|
+
expect(targetLocale).toBe(DEFAULT_LOCALE)
|
|
87
|
+
})
|
|
88
|
+
test('with getUserPreferredLocales parameter', () => {
|
|
89
|
+
const locale = supportedLocale
|
|
90
|
+
if (isMultiLocales) {
|
|
91
|
+
// eslint-disable-next-line jest/no-conditional-expect
|
|
92
|
+
expect(locale).not.toBe(DEFAULT_LOCALE)
|
|
93
|
+
}
|
|
94
|
+
const targetLocale = getTargetLocale({
|
|
95
|
+
getUserPreferredLocales: () => [locale],
|
|
96
|
+
l10nConfig
|
|
97
|
+
})
|
|
98
|
+
expect(targetLocale).toBe(locale)
|
|
99
|
+
})
|
|
100
|
+
test('with pseudo locale', async () => {
|
|
101
|
+
process.env.USE_PSEUDOLOCALE = 'true'
|
|
102
|
+
// Simulate server side
|
|
103
|
+
windowSpy.mockImplementation(() => undefined)
|
|
104
|
+
|
|
105
|
+
const targetLocale = getTargetLocale({l10nConfig})
|
|
106
|
+
const messages = await fetchTranslations(targetLocale)
|
|
107
|
+
// The app should still think its target locale is the default one
|
|
108
|
+
expect(targetLocale).toBe(DEFAULT_LOCALE)
|
|
109
|
+
// But the actual translation should be using the pseudo locale
|
|
110
|
+
expect(messages[testId1][1].value).toMatch(/Ƥřīṽȧȧƈẏ Ƥǿǿŀīƈẏ/)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Provides mapping of password requirements that have/haven't been met
|
|
10
|
+
* @param {string} value - The password to validate
|
|
11
|
+
* @returns {Object} - True/false for each password validation rule
|
|
12
|
+
*/
|
|
13
|
+
export const validatePassword = (value) => {
|
|
14
|
+
return {
|
|
15
|
+
hasMinChars: value && value.length >= 8 ? true : false,
|
|
16
|
+
hasUppercase: value && /[A-Z]/.test(value) ? true : false,
|
|
17
|
+
hasLowercase: value && /[a-z]/.test(value) ? true : false,
|
|
18
|
+
hasNumber: value && /\d/.test(value) ? true : false,
|
|
19
|
+
hasSpecialChar: value && /[!@#$%^&*(),.?":{}|<>]/.test(value) ? true : false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Formats the given phone number to add spaces and symbols
|
|
10
|
+
* @param {string} - Phone number to be formatted
|
|
11
|
+
* @returns {string} - Formatted phone number
|
|
12
|
+
*/
|
|
13
|
+
export const formatPhoneNumber = (value) => {
|
|
14
|
+
if (!value) return value
|
|
15
|
+
const phoneNumber = value.replace(/[^\d]/g, '')
|
|
16
|
+
const phoneNumberLength = phoneNumber.length
|
|
17
|
+
if (phoneNumberLength < 4) return phoneNumber
|
|
18
|
+
if (phoneNumberLength < 7) {
|
|
19
|
+
return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3)}`
|
|
20
|
+
}
|
|
21
|
+
return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6, 10)}`
|
|
22
|
+
}
|