@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,382 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import React, {useState} from 'react'
9
+ import {defineMessage, FormattedMessage, useIntl} from 'react-intl'
10
+ import PropTypes from 'prop-types'
11
+
12
+ import {
13
+ Alert,
14
+ AlertIcon,
15
+ Badge,
16
+ Box,
17
+ Button,
18
+ Container,
19
+ Heading,
20
+ SimpleGrid,
21
+ Skeleton,
22
+ Stack,
23
+ Text
24
+ } from '@chakra-ui/react'
25
+ import FormActionButtons from '@salesforce/retail-react-app/app/components/forms/form-action-buttons'
26
+ import {useForm} from 'react-hook-form'
27
+ import {useToast} from '@salesforce/retail-react-app/app/hooks/use-toast'
28
+
29
+ import LoadingSpinner from '@salesforce/retail-react-app/app/components/loading-spinner'
30
+ import {LocationIcon, PlusIcon} from '@salesforce/retail-react-app/app/components/icons'
31
+ import ActionCard from '@salesforce/retail-react-app/app/components/action-card'
32
+ import AddressFields from '@salesforce/retail-react-app/app/components/forms/address-fields'
33
+ import AddressDisplay from '@salesforce/retail-react-app/app/components/address-display'
34
+ import PageActionPlaceHolder from '@salesforce/retail-react-app/app/components/page-action-placeholder'
35
+ import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
36
+ import {useShopperCustomersMutation} from '@salesforce/commerce-sdk-react'
37
+ import {nanoid} from 'nanoid'
38
+ import {API_ERROR_MESSAGE} from '@salesforce/retail-react-app/app/constants'
39
+
40
+ const DEFAULT_SKELETON_COUNT = 3
41
+
42
+ const BoxArrow = () => {
43
+ return (
44
+ <Box
45
+ width={3}
46
+ height={3}
47
+ borderLeft="1px solid"
48
+ borderTop="1px solid"
49
+ borderColor="blue.600"
50
+ position="absolute"
51
+ left="50%"
52
+ bottom="-23px"
53
+ zIndex={1}
54
+ background="white"
55
+ transform="rotate(45deg)"
56
+ />
57
+ )
58
+ }
59
+
60
+ const ShippingAddressForm = ({form, hasAddresses, selectedAddressId, toggleEdit, submitForm}) => {
61
+ return (
62
+ <Box
63
+ border="1px solid"
64
+ borderColor="gray.200"
65
+ borderRadius="base"
66
+ position="relative"
67
+ {...(hasAddresses && {
68
+ gridColumn: [1, 'span 2', 'span 2', 'span 2', 'span 3'],
69
+ paddingX: [4, 4, 6],
70
+ paddingY: 6,
71
+ rounded: 'base',
72
+ border: '1px solid',
73
+ borderColor: 'blue.600'
74
+ })}
75
+ >
76
+ {form.formState.isSubmitting && <LoadingSpinner />}
77
+ <Stack spacing={6} padding={6}>
78
+ <Heading as="h3" size="sm">
79
+ {selectedAddressId ? (
80
+ <FormattedMessage
81
+ defaultMessage="Edit Address"
82
+ id="shipping_address_form.heading.edit_address"
83
+ />
84
+ ) : (
85
+ <FormattedMessage
86
+ defaultMessage="Add New Address"
87
+ id="shipping_address_form.heading.new_address"
88
+ />
89
+ )}
90
+ </Heading>
91
+ <Box>
92
+ <Container variant="form">
93
+ <form onSubmit={form.handleSubmit(submitForm)}>
94
+ <Stack spacing={6}>
95
+ {form.formState.errors?.global && (
96
+ <Alert status="error">
97
+ <AlertIcon color="red.500" boxSize={4} />
98
+ <Text fontSize="sm" ml={3}>
99
+ {form.formState.errors.global.message}
100
+ </Text>
101
+ </Alert>
102
+ )}
103
+ <AddressFields form={form} />
104
+ <FormActionButtons onCancel={toggleEdit} />
105
+ </Stack>
106
+ </form>
107
+ </Container>
108
+ </Box>
109
+ </Stack>
110
+ </Box>
111
+ )
112
+ }
113
+
114
+ ShippingAddressForm.propTypes = {
115
+ form: PropTypes.object,
116
+ hasAddresses: PropTypes.bool,
117
+ selectedAddressId: PropTypes.string,
118
+ toggleEdit: PropTypes.func,
119
+ submitForm: PropTypes.func
120
+ }
121
+
122
+ const successfullyAddedAddress = defineMessage({
123
+ defaultMessage: 'New address saved',
124
+ id: 'account_addresses.info.new_address_saved'
125
+ })
126
+
127
+ const successfullyUpdatedAddress = defineMessage({
128
+ defaultMessage: 'Address updated',
129
+ id: 'account_addresses.info.address_updated'
130
+ })
131
+
132
+ const successfullyRemovedAddress = defineMessage({
133
+ defaultMessage: 'Address removed',
134
+ id: 'account_addresses.info.address_removed'
135
+ })
136
+ const AccountAddresses = () => {
137
+ const {formatMessage} = useIntl()
138
+ const {data: customer, isLoading} = useCurrentCustomer()
139
+ const {isRegistered, addresses, customerId} = customer
140
+
141
+ const addCustomerAddress = useShopperCustomersMutation('createCustomerAddress')
142
+ const updateSavedAddress = useShopperCustomersMutation('updateCustomerAddress')
143
+ const removeCustomerAddress = useShopperCustomersMutation('removeCustomerAddress')
144
+
145
+ const [isEditing, setIsEditing] = useState(false)
146
+ const [selectedAddressId, setSelectedAddressId] = useState(false)
147
+ const showToast = useToast()
148
+ const form = useForm()
149
+
150
+ const hasAddresses = addresses?.length > 0
151
+ const showError = () => {
152
+ showToast({
153
+ title: formatMessage(API_ERROR_MESSAGE),
154
+ status: 'error'
155
+ })
156
+ }
157
+ const submitForm = async (address) => {
158
+ try {
159
+ let data
160
+ form.clearErrors()
161
+ if (selectedAddressId) {
162
+ const body = {
163
+ ...address,
164
+ addressId: selectedAddressId
165
+ }
166
+ data = await updateSavedAddress.mutateAsync({
167
+ body,
168
+ parameters: {
169
+ customerId,
170
+ addressName: selectedAddressId
171
+ }
172
+ })
173
+ } else {
174
+ const body = {
175
+ addressId: nanoid(),
176
+ ...address
177
+ }
178
+ data = await addCustomerAddress.mutateAsync({
179
+ body,
180
+ parameters: {customerId: customer.customerId}
181
+ })
182
+ }
183
+ if (data) {
184
+ toggleEdit()
185
+ showToast({
186
+ title: selectedAddressId
187
+ ? formatMessage(successfullyUpdatedAddress)
188
+ : formatMessage(successfullyAddedAddress),
189
+ status: 'success',
190
+ isClosable: true
191
+ })
192
+ }
193
+ } catch (error) {
194
+ form.setError('global', {type: 'manual', message: error.message})
195
+ }
196
+ }
197
+
198
+ const removeAddress = async (addressId) => {
199
+ try {
200
+ if (addressId === selectedAddressId) {
201
+ setSelectedAddressId(undefined)
202
+ setIsEditing(false)
203
+ form.reset({addressId: ''})
204
+ }
205
+ await removeCustomerAddress.mutateAsync(
206
+ {
207
+ parameters: {
208
+ customerId,
209
+ addressName: addressId
210
+ }
211
+ },
212
+ {
213
+ onSuccess: () => {
214
+ showToast({
215
+ title: formatMessage(successfullyRemovedAddress),
216
+ status: 'success',
217
+ isClosable: true
218
+ })
219
+ }
220
+ }
221
+ )
222
+ } catch (error) {
223
+ showError()
224
+ throw error
225
+ }
226
+ }
227
+
228
+ const toggleEdit = (address) => {
229
+ form.reset({...address})
230
+
231
+ if (address?.addressId) {
232
+ setSelectedAddressId(address.addressId)
233
+ setIsEditing(true)
234
+ } else {
235
+ setSelectedAddressId(undefined)
236
+ setIsEditing(!isEditing)
237
+ }
238
+ }
239
+
240
+ return (
241
+ <Stack spacing={4} data-testid="account-addresses-page">
242
+ <Heading as="h1" fontSize="2xl">
243
+ <FormattedMessage
244
+ defaultMessage="Addresses"
245
+ id="account_addresses.title.addresses"
246
+ />
247
+ </Heading>
248
+
249
+ {isLoading && (
250
+ <SimpleGrid columns={[1, 2, 2, 2, 3]} spacing={4}>
251
+ {new Array(DEFAULT_SKELETON_COUNT).fill().map((_, index) => {
252
+ return (
253
+ <ActionCard key={index}>
254
+ <Stack spacing={2} marginBottom={7}>
255
+ <Skeleton height="23px" width="120px" />
256
+
257
+ <Skeleton height="23px" width="84px" />
258
+
259
+ <Skeleton height="23px" width="104px" />
260
+ </Stack>
261
+ </ActionCard>
262
+ )
263
+ })}
264
+ </SimpleGrid>
265
+ )}
266
+
267
+ {hasAddresses && (
268
+ <SimpleGrid columns={[1, 2, 2, 2, 3]} spacing={4} gridAutoFlow="row dense">
269
+ {
270
+ <Button
271
+ variant="outline"
272
+ border="1px dashed"
273
+ borderColor="gray.200"
274
+ color="blue.600"
275
+ height={{lg: 'full'}}
276
+ minHeight={11}
277
+ rounded="base"
278
+ fontWeight="medium"
279
+ leftIcon={<PlusIcon display="block" boxSize={'15px'} />}
280
+ onClick={() => toggleEdit()}
281
+ >
282
+ <FormattedMessage
283
+ defaultMessage="Add Address"
284
+ id="account_addresses.button.add_address"
285
+ />
286
+ {isEditing && !selectedAddressId && <BoxArrow />}
287
+ </Button>
288
+ }
289
+
290
+ {isEditing && !selectedAddressId && (
291
+ <>
292
+ <ShippingAddressForm
293
+ form={form}
294
+ hasAddresses={hasAddresses}
295
+ submitForm={submitForm}
296
+ selectedAddressId={selectedAddressId}
297
+ toggleEdit={toggleEdit}
298
+ />
299
+ </>
300
+ )}
301
+
302
+ {addresses.map((address) => (
303
+ <React.Fragment key={address.addressId}>
304
+ <ActionCard
305
+ borderColor="gray.200"
306
+ key={address.addressId}
307
+ onRemove={() => removeAddress(address.addressId)}
308
+ onEdit={() => toggleEdit(address)}
309
+ >
310
+ {address.preferred && (
311
+ <Badge
312
+ position="absolute"
313
+ fontSize="xs"
314
+ right={4}
315
+ variant="solid"
316
+ bg="gray.100"
317
+ color="gray.900"
318
+ >
319
+ <FormattedMessage
320
+ defaultMessage="Default"
321
+ id="account_addresses.badge.default"
322
+ />
323
+ </Badge>
324
+ )}
325
+ <AddressDisplay address={address} />
326
+ {isEditing && address.addressId === selectedAddressId && (
327
+ <BoxArrow />
328
+ )}
329
+ </ActionCard>
330
+
331
+ {isEditing && address.addressId === selectedAddressId && (
332
+ <ShippingAddressForm
333
+ form={form}
334
+ hasAddresses={hasAddresses}
335
+ submitForm={submitForm}
336
+ selectedAddressId={selectedAddressId}
337
+ toggleEdit={toggleEdit}
338
+ />
339
+ )}
340
+ </React.Fragment>
341
+ ))}
342
+ </SimpleGrid>
343
+ )}
344
+
345
+ {!hasAddresses && !isLoading && (
346
+ <>
347
+ {!isEditing && isRegistered && (
348
+ <PageActionPlaceHolder
349
+ icon={<LocationIcon boxSize={8} />}
350
+ heading={formatMessage({
351
+ defaultMessage: 'No Saved Addresses',
352
+ id: 'account_addresses.page_action_placeholder.heading.no_saved_addresses'
353
+ })}
354
+ text={formatMessage({
355
+ defaultMessage: 'Add a new address method for faster checkout.',
356
+ id: 'account_addresses.page_action_placeholder.message.add_new_address'
357
+ })}
358
+ buttonText={formatMessage({
359
+ defaultMessage: 'Add Address',
360
+ id: 'account_addresses.page_action_placeholder.button.add_address'
361
+ })}
362
+ onButtonClick={() => toggleEdit()}
363
+ />
364
+ )}
365
+ {isEditing && !selectedAddressId && (
366
+ <ShippingAddressForm
367
+ form={form}
368
+ hasAddresses={hasAddresses}
369
+ submitForm={submitForm}
370
+ selectedAddressId={selectedAddressId}
371
+ toggleEdit={toggleEdit}
372
+ />
373
+ )}
374
+ </>
375
+ )}
376
+ </Stack>
377
+ )
378
+ }
379
+
380
+ AccountAddresses.getTemplateName = () => 'account-addresses'
381
+
382
+ export default AccountAddresses
@@ -0,0 +1,120 @@
1
+ /*
2
+ * Copyright (c) 2023, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import React from 'react'
8
+ import {screen, waitFor} from '@testing-library/react'
9
+ import {
10
+ createPathWithDefaults,
11
+ renderWithProviders
12
+ } from '@salesforce/retail-react-app/app/utils/test-utils'
13
+ import {rest} from 'msw'
14
+ import AccountAddresses from '@salesforce/retail-react-app/app/pages/account/addresses'
15
+ import {
16
+ mockedRegisteredCustomerWithNoAddress,
17
+ mockedRegisteredCustomer
18
+ } from '@salesforce/retail-react-app/app/mocks/mock-data'
19
+
20
+ import {Route, Switch} from 'react-router-dom'
21
+ import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
22
+
23
+ let mockCustomer = {}
24
+
25
+ const MockedComponent = () => {
26
+ return (
27
+ <Switch>
28
+ <Route path={createPathWithDefaults('/account/addresses')}>
29
+ <AccountAddresses />
30
+ </Route>
31
+ </Switch>
32
+ )
33
+ }
34
+
35
+ // Set up and clean up
36
+ beforeEach(() => {
37
+ jest.resetModules()
38
+ mockCustomer = {
39
+ authType: 'registered',
40
+ customerId: 'registeredCustomerId',
41
+ customerNo: '00151503',
42
+ email: 'jkeane@64labs.com',
43
+ firstName: 'John',
44
+ lastName: 'Keane',
45
+ login: 'jkeane@64labs.com'
46
+ }
47
+ global.server.use(
48
+ rest.get('*/customers/:customerId', (req, res, ctx) =>
49
+ res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
50
+ ),
51
+ rest.post('*/customers/:customerId/addresses', (req, res, ctx) => {
52
+ mockCustomer.addresses = [req.body]
53
+ return res(ctx.delay(0), ctx.status(200), ctx.json(req.body))
54
+ }),
55
+ rest.patch('*/customers/:customerId/addresses/:addressName', (req, res, ctx) => {
56
+ mockCustomer.addresses[0] = req.body
57
+ return res(ctx.delay(0), ctx.status(200), ctx.json(req.body))
58
+ }),
59
+ rest.delete('*/customers/:customerId/addresses/:addressName', (req, res, ctx) => {
60
+ mockCustomer.addresses = undefined
61
+ return res(ctx.delay(0), ctx.status(200))
62
+ })
63
+ )
64
+ window.history.pushState({}, 'Account', createPathWithDefaults('/account/addresses'))
65
+ })
66
+ afterEach(() => {
67
+ jest.resetModules()
68
+ localStorage.clear()
69
+ })
70
+
71
+ test('Allows customer to add addresses', async () => {
72
+ global.server.use(
73
+ rest.get('*/customers/:customerId', (req, res, ctx) =>
74
+ res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomerWithNoAddress))
75
+ )
76
+ )
77
+ const {user} = renderWithProviders(<MockedComponent />, {
78
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
79
+ })
80
+
81
+ await waitFor(() => {
82
+ expect(screen.getByText(/no saved addresses/i)).toBeInTheDocument()
83
+ })
84
+
85
+ await user.click(screen.getByText(/add address/i))
86
+ await user.type(screen.getByLabelText('First Name'), 'Test')
87
+ await user.type(screen.getByLabelText('Last Name'), 'McTester')
88
+ await user.type(screen.getByLabelText('Phone'), '7275551234')
89
+ await user.type(screen.getByLabelText('Address'), '123 Main St')
90
+ await user.type(screen.getByLabelText('City'), 'Tampa')
91
+ await user.selectOptions(screen.getByLabelText(/state/i), ['FL'])
92
+ await user.type(screen.getByLabelText('Zip Code'), '33712')
93
+
94
+ global.server.use(
95
+ rest.get('*/customers/:customerId', (req, res, ctx) =>
96
+ res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
97
+ )
98
+ )
99
+ await user.click(screen.getByText(/^Save$/i))
100
+ expect(await screen.findByText(/123 Main St/i)).toBeInTheDocument()
101
+ })
102
+
103
+ test('Allows customer to remove addresses', async () => {
104
+ global.server.use(
105
+ rest.get('*/customers/:customerId', (req, res, ctx) =>
106
+ res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
107
+ )
108
+ )
109
+ const {user} = renderWithProviders(<MockedComponent />)
110
+ await waitFor(() => expect(screen.getByText('123 Main St')).toBeInTheDocument())
111
+
112
+ global.server.use(
113
+ rest.get('*/customers/:customerId', (req, res, ctx) =>
114
+ res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomerWithNoAddress))
115
+ )
116
+ )
117
+
118
+ await user.click(screen.getByText(/remove/i))
119
+ expect(await screen.findByText(/no saved addresses/i)).toBeInTheDocument()
120
+ })
@@ -0,0 +1,57 @@
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 {defineMessages} from 'react-intl'
9
+ import {
10
+ AccountIcon,
11
+ LocationIcon,
12
+ ReceiptIcon,
13
+ HeartIcon
14
+ } from '@salesforce/retail-react-app/app/components/icons'
15
+
16
+ export const messages = defineMessages({
17
+ profile: {defaultMessage: 'Account Details', id: 'global.account.link.account_details'},
18
+ addresses: {defaultMessage: 'Addresses', id: 'global.account.link.addresses'},
19
+ orders: {defaultMessage: 'Order History', id: 'global.account.link.order_history'},
20
+ wishlist: {defaultMessage: 'Wishlist', id: 'global.account.link.wishlist'}
21
+ })
22
+
23
+ export const navLinks = [
24
+ {
25
+ name: 'profile',
26
+ path: '',
27
+ icon: AccountIcon
28
+ },
29
+ {
30
+ name: 'wishlist',
31
+ path: '/wishlist',
32
+ icon: HeartIcon
33
+ },
34
+ {
35
+ name: 'orders',
36
+ path: '/orders',
37
+ icon: ReceiptIcon
38
+ },
39
+ {
40
+ name: 'addresses',
41
+ path: '/addresses',
42
+ icon: LocationIcon
43
+ }
44
+ ]
45
+
46
+ export const CONFIRMATION_DIALOG_DEFAULT_CONFIG = defineMessages({
47
+ dialogTitle: {
48
+ defaultMessage: 'Confirm Action',
49
+ id: 'confirmation_modal.default.title.confirm_action'
50
+ },
51
+ confirmationMessage: {
52
+ defaultMessage: 'Are you sure you want to continue?',
53
+ id: 'confirmation_modal.default.message.you_want_to_continue'
54
+ },
55
+ primaryActionLabel: {defaultMessage: 'Yes', id: 'confirmation_modal.default.action.yes'},
56
+ alternateActionLabel: {defaultMessage: 'No', id: 'confirmation_modal.default.action.no'}
57
+ })