@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.
Files changed (425) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.js +25 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc.yaml +7 -0
  5. package/CHANGELOG.md +173 -0
  6. package/LICENSE +14 -0
  7. package/README.md +48 -0
  8. package/app/assets/svg/account.svg +3 -0
  9. package/app/assets/svg/alert.svg +3 -0
  10. package/app/assets/svg/basket.svg +3 -0
  11. package/app/assets/svg/brand-logo.svg +10 -0
  12. package/app/assets/svg/cc-amex.svg +7 -0
  13. package/app/assets/svg/cc-cvv.svg +8 -0
  14. package/app/assets/svg/cc-discover.svg +14 -0
  15. package/app/assets/svg/cc-mastercard.svg +8 -0
  16. package/app/assets/svg/cc-visa.svg +11 -0
  17. package/app/assets/svg/check-circle.svg +3 -0
  18. package/app/assets/svg/check.svg +3 -0
  19. package/app/assets/svg/chevron-down.svg +3 -0
  20. package/app/assets/svg/chevron-left.svg +3 -0
  21. package/app/assets/svg/chevron-right.svg +3 -0
  22. package/app/assets/svg/chevron-up.svg +3 -0
  23. package/app/assets/svg/close.svg +3 -0
  24. package/app/assets/svg/dashboard.svg +4 -0
  25. package/app/assets/svg/figma-logo.svg +14 -0
  26. package/app/assets/svg/file.svg +3 -0
  27. package/app/assets/svg/filter.svg +3 -0
  28. package/app/assets/svg/flag-ca.svg +5 -0
  29. package/app/assets/svg/flag-cn.svg +19 -0
  30. package/app/assets/svg/flag-fr.svg +19 -0
  31. package/app/assets/svg/flag-gb.svg +16 -0
  32. package/app/assets/svg/flag-it.svg +29 -0
  33. package/app/assets/svg/flag-jp.svg +10 -0
  34. package/app/assets/svg/flag-us.svg +7 -0
  35. package/app/assets/svg/github-logo.svg +40 -0
  36. package/app/assets/svg/hamburger.svg +8 -0
  37. package/app/assets/svg/heart-solid.svg +7 -0
  38. package/app/assets/svg/heart.svg +3 -0
  39. package/app/assets/svg/info.svg +3 -0
  40. package/app/assets/svg/like.svg +4 -0
  41. package/app/assets/svg/location.svg +3 -0
  42. package/app/assets/svg/lock.svg +3 -0
  43. package/app/assets/svg/paypal.svg +19 -0
  44. package/app/assets/svg/plug.svg +3 -0
  45. package/app/assets/svg/plus.svg +3 -0
  46. package/app/assets/svg/receipt.svg +3 -0
  47. package/app/assets/svg/search.svg +8 -0
  48. package/app/assets/svg/signout.svg +3 -0
  49. package/app/assets/svg/social-facebook.svg +3 -0
  50. package/app/assets/svg/social-instagram.svg +3 -0
  51. package/app/assets/svg/social-pinterest.svg +4 -0
  52. package/app/assets/svg/social-twitter.svg +3 -0
  53. package/app/assets/svg/social-youtube.svg +3 -0
  54. package/app/assets/svg/user.svg +3 -0
  55. package/app/assets/svg/visibility-off.svg +5 -0
  56. package/app/assets/svg/visibility.svg +3 -0
  57. package/app/components/_app/index.jsx +401 -0
  58. package/app/components/_app/index.test.js +85 -0
  59. package/app/components/_app/partials/above-header.jsx +10 -0
  60. package/app/components/_app-config/index.jsx +125 -0
  61. package/app/components/_app-config/index.test.js +77 -0
  62. package/app/components/_error/index.jsx +142 -0
  63. package/app/components/_error/index.test.js +25 -0
  64. package/app/components/action-card/index.jsx +75 -0
  65. package/app/components/address-display/index.jsx +30 -0
  66. package/app/components/basic-tile/index.jsx +65 -0
  67. package/app/components/basic-tile/index.test.js +23 -0
  68. package/app/components/breadcrumb/index.jsx +67 -0
  69. package/app/components/breadcrumb/index.test.js +30 -0
  70. package/app/components/confirmation-modal/index.jsx +111 -0
  71. package/app/components/confirmation-modal/index.test.js +98 -0
  72. package/app/components/drawer-menu/index.jsx +405 -0
  73. package/app/components/drawer-menu/index.test.js +33 -0
  74. package/app/components/dynamic-image/index.jsx +56 -0
  75. package/app/components/field/index.jsx +161 -0
  76. package/app/components/footer/index.jsx +269 -0
  77. package/app/components/footer/index.test.js +22 -0
  78. package/app/components/forms/address-fields.jsx +49 -0
  79. package/app/components/forms/credit-card-fields.jsx +149 -0
  80. package/app/components/forms/form-action-buttons.jsx +55 -0
  81. package/app/components/forms/login-fields.jsx +31 -0
  82. package/app/components/forms/password-requirements.jsx +99 -0
  83. package/app/components/forms/post-checkout-registration-fields.jsx +43 -0
  84. package/app/components/forms/profile-fields.jsx +36 -0
  85. package/app/components/forms/promo-code-fields.jsx +43 -0
  86. package/app/components/forms/registration-fields.jsx +42 -0
  87. package/app/components/forms/reset-password-fields.jsx +31 -0
  88. package/app/components/forms/state-province-options.jsx +75 -0
  89. package/app/components/forms/update-password-fields.jsx +49 -0
  90. package/app/components/forms/useAddressFields.jsx +196 -0
  91. package/app/components/forms/useCreditCardFields.jsx +146 -0
  92. package/app/components/forms/useLoginFields.jsx +52 -0
  93. package/app/components/forms/useProfileFields.jsx +95 -0
  94. package/app/components/forms/usePromoCodeFields.jsx +39 -0
  95. package/app/components/forms/useRegistrationFields.jsx +136 -0
  96. package/app/components/forms/useResetPasswordFields.jsx +40 -0
  97. package/app/components/forms/useUpdatePasswordFields.jsx +89 -0
  98. package/app/components/header/index.jsx +290 -0
  99. package/app/components/header/index.test.js +217 -0
  100. package/app/components/hero/index.jsx +84 -0
  101. package/app/components/hero/index.test.js +40 -0
  102. package/app/components/icons/index.jsx +158 -0
  103. package/app/components/icons/index.test.js +20 -0
  104. package/app/components/image-gallery/index.jsx +176 -0
  105. package/app/components/image-gallery/index.test.js +485 -0
  106. package/app/components/item-variant/index.jsx +33 -0
  107. package/app/components/item-variant/item-attributes.jsx +107 -0
  108. package/app/components/item-variant/item-image.jsx +73 -0
  109. package/app/components/item-variant/item-name.jsx +28 -0
  110. package/app/components/item-variant/item-price.jsx +117 -0
  111. package/app/components/link/index.jsx +32 -0
  112. package/app/components/link/index.test.js +72 -0
  113. package/app/components/links-list/index.jsx +89 -0
  114. package/app/components/links-list/index.test.js +62 -0
  115. package/app/components/list-menu/index.jsx +280 -0
  116. package/app/components/list-menu/index.test.js +44 -0
  117. package/app/components/loading-spinner/index.jsx +46 -0
  118. package/app/components/locale-selector/index.jsx +124 -0
  119. package/app/components/locale-selector/index.test.js +37 -0
  120. package/app/components/locale-text/index.jsx +97 -0
  121. package/app/components/locale-text/index.test.js +36 -0
  122. package/app/components/login/index.jsx +96 -0
  123. package/app/components/nested-accordion/index.jsx +185 -0
  124. package/app/components/nested-accordion/index.test.js +98 -0
  125. package/app/components/offline-banner/index.jsx +40 -0
  126. package/app/components/offline-banner/index.test.js +15 -0
  127. package/app/components/offline-boundary/index.jsx +104 -0
  128. package/app/components/offline-boundary/index.test.js +123 -0
  129. package/app/components/order-summary/index.jsx +331 -0
  130. package/app/components/page-action-placeholder/index.jsx +50 -0
  131. package/app/components/pagination/index.jsx +134 -0
  132. package/app/components/pagination/index.test.js +25 -0
  133. package/app/components/product-item/index.jsx +146 -0
  134. package/app/components/product-item/index.test.js +38 -0
  135. package/app/components/product-scroller/index.jsx +172 -0
  136. package/app/components/product-scroller/index.test.js +98 -0
  137. package/app/components/product-tile/index.jsx +195 -0
  138. package/app/components/product-tile/index.test.js +96 -0
  139. package/app/components/product-view/index.jsx +538 -0
  140. package/app/components/product-view/index.test.js +224 -0
  141. package/app/components/product-view-modal/index.jsx +48 -0
  142. package/app/components/product-view-modal/index.test.js +72 -0
  143. package/app/components/promo-code/index.jsx +162 -0
  144. package/app/components/promo-popover/index.jsx +83 -0
  145. package/app/components/quantity-picker/index.jsx +58 -0
  146. package/app/components/radio-card/index.jsx +75 -0
  147. package/app/components/recommended-products/index.jsx +227 -0
  148. package/app/components/register/index.jsx +114 -0
  149. package/app/components/reset-password/index.jsx +87 -0
  150. package/app/components/responsive/index.jsx +29 -0
  151. package/app/components/scroll-to-top/index.jsx +24 -0
  152. package/app/components/scroll-to-top/index.test.js +46 -0
  153. package/app/components/search/index.jsx +279 -0
  154. package/app/components/search/index.test.js +127 -0
  155. package/app/components/search/partials/recent-searches.jsx +76 -0
  156. package/app/components/search/partials/search-suggestions.jsx +45 -0
  157. package/app/components/search/partials/suggestions.jsx +43 -0
  158. package/app/components/section/index.jsx +68 -0
  159. package/app/components/seo/index.jsx +33 -0
  160. package/app/components/social-icons/index.jsx +101 -0
  161. package/app/components/social-icons/index.test.js +30 -0
  162. package/app/components/swatch-group/index.jsx +77 -0
  163. package/app/components/swatch-group/index.test.js +136 -0
  164. package/app/components/swatch-group/swatch.jsx +94 -0
  165. package/app/components/toggle-card/index.jsx +97 -0
  166. package/app/components/with-registration/index.jsx +58 -0
  167. package/app/components/with-registration/index.test.js +85 -0
  168. package/app/constants.js +109 -0
  169. package/app/contexts/index.js +92 -0
  170. package/app/hooks/einstein-mock-data.js +916 -0
  171. package/app/hooks/index.js +17 -0
  172. package/app/hooks/use-add-to-cart-modal.js +344 -0
  173. package/app/hooks/use-add-to-cart-modal.test.js +625 -0
  174. package/app/hooks/use-auth-modal.js +337 -0
  175. package/app/hooks/use-auth-modal.test.js +365 -0
  176. package/app/hooks/use-currency.js +20 -0
  177. package/app/hooks/use-currency.test.js +41 -0
  178. package/app/hooks/use-current-basket.js +39 -0
  179. package/app/hooks/use-current-customer.js +29 -0
  180. package/app/hooks/use-derived-product.js +77 -0
  181. package/app/hooks/use-derived-product.test.js +69 -0
  182. package/app/hooks/use-einstein.js +512 -0
  183. package/app/hooks/use-einstein.test.js +224 -0
  184. package/app/hooks/use-intersection-observer.js +64 -0
  185. package/app/hooks/use-limit-urls.js +31 -0
  186. package/app/hooks/use-limit-urls.test.js +40 -0
  187. package/app/hooks/use-multi-site.js +36 -0
  188. package/app/hooks/use-multi-site.test.js +53 -0
  189. package/app/hooks/use-navigation.js +37 -0
  190. package/app/hooks/use-navigation.test.js +109 -0
  191. package/app/hooks/use-page-urls.js +35 -0
  192. package/app/hooks/use-page-urls.test.js +39 -0
  193. package/app/hooks/use-pdp-search-params.js +16 -0
  194. package/app/hooks/use-pdp-search-params.test.js +52 -0
  195. package/app/hooks/use-previous.js +17 -0
  196. package/app/hooks/use-product-view-modal.js +93 -0
  197. package/app/hooks/use-product-view-modal.test.js +172 -0
  198. package/app/hooks/use-search-params.js +96 -0
  199. package/app/hooks/use-search-params.test.js +91 -0
  200. package/app/hooks/use-sort-urls.js +33 -0
  201. package/app/hooks/use-sort-urls.test.js +42 -0
  202. package/app/hooks/use-toast.js +68 -0
  203. package/app/hooks/use-toast.test.js +58 -0
  204. package/app/hooks/use-variant.js +32 -0
  205. package/app/hooks/use-variant.test.js +81 -0
  206. package/app/hooks/use-variation-attributes.js +138 -0
  207. package/app/hooks/use-variation-attributes.test.js +119 -0
  208. package/app/hooks/use-variation-params.js +31 -0
  209. package/app/hooks/use-variation-params.test.js +73 -0
  210. package/app/hooks/use-wish-list.js +42 -0
  211. package/app/main.jsx +14 -0
  212. package/app/mocks/basket-with-suit.js +146 -0
  213. package/app/mocks/empty-basket.js +39 -0
  214. package/app/mocks/mock-data.js +5632 -0
  215. package/app/mocks/product-set-winter-lookM.js +1224 -0
  216. package/app/mocks/searchResults.js +144 -0
  217. package/app/mocks/variant-750518699578M.js +434 -0
  218. package/app/page-designer/README.md +102 -0
  219. package/app/page-designer/assets/image-tile/index.jsx +51 -0
  220. package/app/page-designer/assets/image-tile/index.test.js +30 -0
  221. package/app/page-designer/assets/image-with-text/index.jsx +140 -0
  222. package/app/page-designer/assets/image-with-text/index.test.js +38 -0
  223. package/app/page-designer/assets/index.js +9 -0
  224. package/app/page-designer/index.js +10 -0
  225. package/app/page-designer/layouts/carousel/index.jsx +222 -0
  226. package/app/page-designer/layouts/carousel/index.test.js +43 -0
  227. package/app/page-designer/layouts/index.js +14 -0
  228. package/app/page-designer/layouts/mobileGrid1r1c/index.jsx +36 -0
  229. package/app/page-designer/layouts/mobileGrid1r1c/index.test.js +35 -0
  230. package/app/page-designer/layouts/mobileGrid2r1c/index.jsx +37 -0
  231. package/app/page-designer/layouts/mobileGrid2r1c/index.test.js +47 -0
  232. package/app/page-designer/layouts/mobileGrid2r2c/index.jsx +37 -0
  233. package/app/page-designer/layouts/mobileGrid2r2c/index.test.js +71 -0
  234. package/app/page-designer/layouts/mobileGrid2r3c/index.jsx +37 -0
  235. package/app/page-designer/layouts/mobileGrid2r3c/index.test.js +95 -0
  236. package/app/page-designer/layouts/mobileGrid3r1c/index.jsx +37 -0
  237. package/app/page-designer/layouts/mobileGrid3r1c/index.test.js +59 -0
  238. package/app/page-designer/layouts/mobileGrid3r2c/index.jsx +37 -0
  239. package/app/page-designer/layouts/mobileGrid3r2c/index.test.js +95 -0
  240. package/app/page-designer/utils.js +14 -0
  241. package/app/pages/account/addresses.jsx +382 -0
  242. package/app/pages/account/addresses.test.js +120 -0
  243. package/app/pages/account/constant.js +57 -0
  244. package/app/pages/account/index.jsx +237 -0
  245. package/app/pages/account/index.test.js +188 -0
  246. package/app/pages/account/order-detail.jsx +397 -0
  247. package/app/pages/account/order-history.jsx +264 -0
  248. package/app/pages/account/orders.jsx +30 -0
  249. package/app/pages/account/orders.test.js +95 -0
  250. package/app/pages/account/profile.jsx +357 -0
  251. package/app/pages/account/wishlist/index.jsx +195 -0
  252. package/app/pages/account/wishlist/index.mock.js +1481 -0
  253. package/app/pages/account/wishlist/index.test.js +56 -0
  254. package/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +170 -0
  255. package/app/pages/account/wishlist/partials/wishlist-primary-action.mock.js +1623 -0
  256. package/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +99 -0
  257. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +120 -0
  258. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +391 -0
  259. package/app/pages/cart/index.jsx +476 -0
  260. package/app/pages/cart/index.test.js +481 -0
  261. package/app/pages/cart/partials/cart-cta.jsx +46 -0
  262. package/app/pages/cart/partials/cart-secondary-button-group.jsx +135 -0
  263. package/app/pages/cart/partials/cart-secondary-button-group.test.js +103 -0
  264. package/app/pages/cart/partials/cart-skeleton.jsx +93 -0
  265. package/app/pages/cart/partials/cart-title.jsx +27 -0
  266. package/app/pages/cart/partials/empty-cart.jsx +86 -0
  267. package/app/pages/checkout/confirmation.jsx +541 -0
  268. package/app/pages/checkout/confirmation.mock.js +450 -0
  269. package/app/pages/checkout/confirmation.test.js +114 -0
  270. package/app/pages/checkout/index.jsx +169 -0
  271. package/app/pages/checkout/index.test.js +582 -0
  272. package/app/pages/checkout/partials/cc-radio-group.jsx +122 -0
  273. package/app/pages/checkout/partials/checkout-footer.jsx +140 -0
  274. package/app/pages/checkout/partials/checkout-footer.test.js +16 -0
  275. package/app/pages/checkout/partials/checkout-header.jsx +54 -0
  276. package/app/pages/checkout/partials/checkout-header.test.js +16 -0
  277. package/app/pages/checkout/partials/checkout-skeleton.jsx +52 -0
  278. package/app/pages/checkout/partials/contact-info.jsx +251 -0
  279. package/app/pages/checkout/partials/contact-info.test.js +43 -0
  280. package/app/pages/checkout/partials/payment-form.jsx +97 -0
  281. package/app/pages/checkout/partials/payment.jsx +276 -0
  282. package/app/pages/checkout/partials/shipping-address-selection.jsx +377 -0
  283. package/app/pages/checkout/partials/shipping-address.jsx +132 -0
  284. package/app/pages/checkout/partials/shipping-options.jsx +232 -0
  285. package/app/pages/checkout/util/checkout-context.js +94 -0
  286. package/app/pages/home/data.js +134 -0
  287. package/app/pages/home/index.jsx +301 -0
  288. package/app/pages/home/index.test.js +23 -0
  289. package/app/pages/login/index.jsx +123 -0
  290. package/app/pages/login/index.test.js +229 -0
  291. package/app/pages/login-redirect/index.jsx +23 -0
  292. package/app/pages/login-redirect/index.test.js +16 -0
  293. package/app/pages/page-not-found/index.jsx +90 -0
  294. package/app/pages/page-not-found/index.test.js +31 -0
  295. package/app/pages/product-detail/index.jsx +394 -0
  296. package/app/pages/product-detail/index.mock.js +197 -0
  297. package/app/pages/product-detail/index.test.js +162 -0
  298. package/app/pages/product-detail/partials/information-accordion.jsx +121 -0
  299. package/app/pages/product-list/index.jsx +735 -0
  300. package/app/pages/product-list/index.test.js +180 -0
  301. package/app/pages/product-list/partials/above-page-header.jsx +10 -0
  302. package/app/pages/product-list/partials/checkbox-refinements.jsx +41 -0
  303. package/app/pages/product-list/partials/checkbox-refinements.test.js +53 -0
  304. package/app/pages/product-list/partials/color-refinements.jsx +88 -0
  305. package/app/pages/product-list/partials/empty-results.jsx +118 -0
  306. package/app/pages/product-list/partials/link-refinements.jsx +38 -0
  307. package/app/pages/product-list/partials/page-header.jsx +42 -0
  308. package/app/pages/product-list/partials/radio-refinements.jsx +60 -0
  309. package/app/pages/product-list/partials/refinements.jsx +144 -0
  310. package/app/pages/product-list/partials/selected-refinements.jsx +100 -0
  311. package/app/pages/product-list/partials/size-refinements.jsx +55 -0
  312. package/app/pages/registration/index.jsx +87 -0
  313. package/app/pages/registration/index.test.jsx +132 -0
  314. package/app/pages/reset-password/index.jsx +112 -0
  315. package/app/pages/reset-password/index.test.jsx +141 -0
  316. package/app/request-processor.js +118 -0
  317. package/app/request-processor.test.js +23 -0
  318. package/app/routes.jsx +111 -0
  319. package/app/routes.test.js +13 -0
  320. package/app/ssr.js +70 -0
  321. package/app/static/ico/favicon.ico +0 -0
  322. package/app/static/img/global/app-icon-192.png +0 -0
  323. package/app/static/img/global/app-icon-512.png +0 -0
  324. package/app/static/img/global/apple-touch-icon.png +0 -0
  325. package/app/static/img/hero.png +0 -0
  326. package/app/static/manifest.json +19 -0
  327. package/app/static/robots.txt +2 -0
  328. package/app/theme/components/base/accordion.js +21 -0
  329. package/app/theme/components/base/alert.js +17 -0
  330. package/app/theme/components/base/badge.js +25 -0
  331. package/app/theme/components/base/button.js +77 -0
  332. package/app/theme/components/base/checkbox.js +30 -0
  333. package/app/theme/components/base/container.js +17 -0
  334. package/app/theme/components/base/drawer.js +26 -0
  335. package/app/theme/components/base/formLabel.js +13 -0
  336. package/app/theme/components/base/icon.js +13 -0
  337. package/app/theme/components/base/input.js +44 -0
  338. package/app/theme/components/base/modal.js +11 -0
  339. package/app/theme/components/base/popover.js +61 -0
  340. package/app/theme/components/base/radio.js +33 -0
  341. package/app/theme/components/base/select.js +15 -0
  342. package/app/theme/components/base/skeleton.js +12 -0
  343. package/app/theme/components/base/tooltip.js +19 -0
  344. package/app/theme/components/project/_app.js +25 -0
  345. package/app/theme/components/project/breadcrumb.js +25 -0
  346. package/app/theme/components/project/checkout-footer.js +35 -0
  347. package/app/theme/components/project/drawer-menu.js +66 -0
  348. package/app/theme/components/project/footer.js +84 -0
  349. package/app/theme/components/project/header.js +84 -0
  350. package/app/theme/components/project/image-gallery.js +59 -0
  351. package/app/theme/components/project/links-list.js +43 -0
  352. package/app/theme/components/project/list-menu.js +91 -0
  353. package/app/theme/components/project/locale-selector.js +42 -0
  354. package/app/theme/components/project/nested-accordion.js +26 -0
  355. package/app/theme/components/project/offline-banner.js +25 -0
  356. package/app/theme/components/project/pagination.js +22 -0
  357. package/app/theme/components/project/product-tile.js +32 -0
  358. package/app/theme/components/project/social-icons.js +52 -0
  359. package/app/theme/components/project/swatch-group.js +115 -0
  360. package/app/theme/foundations/colors.js +170 -0
  361. package/app/theme/foundations/gradients.js +9 -0
  362. package/app/theme/foundations/layerStyles.js +41 -0
  363. package/app/theme/foundations/shadows.js +9 -0
  364. package/app/theme/foundations/sizes.js +18 -0
  365. package/app/theme/foundations/space.js +9 -0
  366. package/app/theme/foundations/styles.js +21 -0
  367. package/app/theme/index.js +104 -0
  368. package/app/utils/cc-utils.js +112 -0
  369. package/app/utils/cc-utils.test.js +41 -0
  370. package/app/utils/image-groups-utils.js +62 -0
  371. package/app/utils/image-groups-utils.test.js +65 -0
  372. package/app/utils/locale.js +78 -0
  373. package/app/utils/locale.test.js +112 -0
  374. package/app/utils/password-utils.js +21 -0
  375. package/app/utils/phone-utils.js +22 -0
  376. package/app/utils/phone-utils.test.js +15 -0
  377. package/app/utils/product-utils.js +35 -0
  378. package/app/utils/product-utils.test.js +51 -0
  379. package/app/utils/responsive-image.js +198 -0
  380. package/app/utils/responsive-image.test.js +170 -0
  381. package/app/utils/routes-utils.js +111 -0
  382. package/app/utils/routes-utils.test.js +291 -0
  383. package/app/utils/site-utils.js +222 -0
  384. package/app/utils/site-utils.test.js +376 -0
  385. package/app/utils/test-utils.js +257 -0
  386. package/app/utils/url.js +291 -0
  387. package/app/utils/url.test.js +421 -0
  388. package/app/utils/utils.js +201 -0
  389. package/app/utils/utils.test.js +182 -0
  390. package/babel.config.js +7 -0
  391. package/cache-hash-config.json +8 -0
  392. package/config/default.js +64 -0
  393. package/config/mocks/default.js +131 -0
  394. package/config/sites.js +78 -0
  395. package/jest-setup.js +191 -0
  396. package/jest.config.js +50 -0
  397. package/jsconfig.json +13 -0
  398. package/package.json +105 -0
  399. package/scripts/extract-default-messages.js +92 -0
  400. package/tests/lighthouserc.js +37 -0
  401. package/translations/README.md +127 -0
  402. package/translations/compiled/de-DE.json +3212 -0
  403. package/translations/compiled/en-GB.json +3212 -0
  404. package/translations/compiled/en-US.json +3212 -0
  405. package/translations/compiled/en-XA.json +6948 -0
  406. package/translations/compiled/es-MX.json +3216 -0
  407. package/translations/compiled/fr-FR.json +3216 -0
  408. package/translations/compiled/it-IT.json +3188 -0
  409. package/translations/compiled/ja-JP.json +3200 -0
  410. package/translations/compiled/ko-KR.json +3180 -0
  411. package/translations/compiled/pt-BR.json +3220 -0
  412. package/translations/compiled/zh-CN.json +3212 -0
  413. package/translations/compiled/zh-TW.json +3208 -0
  414. package/translations/de-DE.json +1417 -0
  415. package/translations/en-GB.json +1417 -0
  416. package/translations/en-US.json +1417 -0
  417. package/translations/es-MX.json +1417 -0
  418. package/translations/fr-FR.json +1417 -0
  419. package/translations/it-IT.json +1417 -0
  420. package/translations/ja-JP.json +1417 -0
  421. package/translations/ko-KR.json +1417 -0
  422. package/translations/pt-BR.json +1417 -0
  423. package/translations/zh-CN.json +1417 -0
  424. package/translations/zh-TW.json +1417 -0
  425. 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
+ }