@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,401 @@
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, useEffect} from 'react'
9
+ import PropTypes from 'prop-types'
10
+ import {useHistory, useLocation} from 'react-router-dom'
11
+ import {getAssetUrl} from '@salesforce/pwa-kit-react-sdk/ssr/universal/utils'
12
+ import {getAppOrigin} from '@salesforce/pwa-kit-react-sdk/utils/url'
13
+ import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
14
+ import {useQuery, useQueries} from '@tanstack/react-query'
15
+ import {
16
+ useAccessToken,
17
+ useCategory,
18
+ useCommerceApi,
19
+ useCustomerType,
20
+ useCustomerBaskets,
21
+ useShopperBasketsMutation
22
+ } from '@salesforce/commerce-sdk-react'
23
+ import * as queryKeyHelpers from '@salesforce/commerce-sdk-react/hooks/ShopperProducts/queryKeyHelpers'
24
+ // Chakra
25
+ import {Box, useDisclosure, useStyleConfig} from '@chakra-ui/react'
26
+ import {SkipNavLink, SkipNavContent} from '@chakra-ui/skip-nav'
27
+
28
+ // Contexts
29
+ import {CurrencyProvider} from '@salesforce/retail-react-app/app/contexts'
30
+
31
+ // Local Project Components
32
+ import Header from '@salesforce/retail-react-app/app/components/header'
33
+ import OfflineBanner from '@salesforce/retail-react-app/app/components/offline-banner'
34
+ import OfflineBoundary from '@salesforce/retail-react-app/app/components/offline-boundary'
35
+ import ScrollToTop from '@salesforce/retail-react-app/app/components/scroll-to-top'
36
+ import Footer from '@salesforce/retail-react-app/app/components/footer'
37
+ import CheckoutHeader from '@salesforce/retail-react-app/app/pages/checkout/partials/checkout-header'
38
+ import CheckoutFooter from '@salesforce/retail-react-app/app/pages/checkout/partials/checkout-footer'
39
+ import DrawerMenu from '@salesforce/retail-react-app/app/components/drawer-menu'
40
+ import ListMenu from '@salesforce/retail-react-app/app/components/list-menu'
41
+ import {HideOnDesktop, HideOnMobile} from '@salesforce/retail-react-app/app/components/responsive'
42
+ import AboveHeader from '@salesforce/retail-react-app/app/components/_app/partials/above-header'
43
+
44
+ // Hooks
45
+ import {AuthModal, useAuthModal} from '@salesforce/retail-react-app/app/hooks/use-auth-modal'
46
+ import {AddToCartModalProvider} from '@salesforce/retail-react-app/app/hooks/use-add-to-cart-modal'
47
+ import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'
48
+ import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
49
+
50
+ // Localization
51
+ import {IntlProvider} from 'react-intl'
52
+
53
+ // Others
54
+ import {
55
+ watchOnlineStatus,
56
+ flatten,
57
+ mergeMatchedItems,
58
+ isServer
59
+ } from '@salesforce/retail-react-app/app/utils/utils'
60
+ import {getTargetLocale, fetchTranslations} from '@salesforce/retail-react-app/app/utils/locale'
61
+ import {
62
+ DEFAULT_SITE_TITLE,
63
+ HOME_HREF,
64
+ THEME_COLOR,
65
+ CAT_MENU_DEFAULT_NAV_SSR_DEPTH,
66
+ CAT_MENU_DEFAULT_ROOT_CATEGORY,
67
+ DEFAULT_LOCALE
68
+ } from '@salesforce/retail-react-app/app/constants'
69
+
70
+ import Seo from '@salesforce/retail-react-app/app/components/seo'
71
+
72
+ const onClient = typeof window !== 'undefined'
73
+
74
+ /*
75
+ The categories tree can be really large! For performance reasons,
76
+ we only load the level 0 categories on server side, and load the rest
77
+ on client side to reduce SSR page size.
78
+ */
79
+ const useLazyLoadCategories = () => {
80
+ const itemsKey = 'categories'
81
+
82
+ const levelZeroCategoriesQuery = useCategory({
83
+ parameters: {id: CAT_MENU_DEFAULT_ROOT_CATEGORY, levels: CAT_MENU_DEFAULT_NAV_SSR_DEPTH}
84
+ })
85
+
86
+ const ids = levelZeroCategoriesQuery.data?.[itemsKey].map((category) => category.id)
87
+ const queries = useCategoryBulk(ids, {
88
+ enabled: onClient && ids?.length > 0
89
+ })
90
+ const dataArray = queries.map((query) => query.data).filter(Boolean)
91
+ const isLoading = queries.some((query) => query.isLoading)
92
+ const isError = queries.some((query) => query.isError)
93
+ return {
94
+ isLoading,
95
+ isError,
96
+ data: {
97
+ ...levelZeroCategoriesQuery.data,
98
+ [itemsKey]: mergeMatchedItems(
99
+ levelZeroCategoriesQuery.data?.categories || [],
100
+ dataArray
101
+ )
102
+ }
103
+ }
104
+ }
105
+
106
+ const App = (props) => {
107
+ const {children} = props
108
+ const {data: categoriesTree} = useLazyLoadCategories()
109
+ const categories = flatten(categoriesTree || {}, 'categories')
110
+
111
+ const appOrigin = getAppOrigin()
112
+
113
+ const history = useHistory()
114
+ const location = useLocation()
115
+ const authModal = useAuthModal()
116
+ const {isRegistered} = useCustomerType()
117
+ const {site, locale, buildUrl} = useMultiSite()
118
+
119
+ const [isOnline, setIsOnline] = useState(true)
120
+ const styles = useStyleConfig('App')
121
+
122
+ const {isOpen, onOpen, onClose} = useDisclosure()
123
+
124
+ const targetLocale = getTargetLocale({
125
+ getUserPreferredLocales: () => {
126
+ // CONFIG: This function should return an array of preferred locales. They can be
127
+ // derived from various sources. Below are some examples of those:
128
+ //
129
+ // - client side: window.navigator.languages
130
+ // - the page URL they're on (example.com/en-GB/home)
131
+ // - cookie (if their previous preference is saved there)
132
+ //
133
+ // If this function returns an empty array (e.g. there isn't locale in the page url),
134
+ // then the app would use the default locale as the fallback.
135
+
136
+ // NOTE: Your implementation may differ, this is just what we did.
137
+ return [locale?.id || DEFAULT_LOCALE]
138
+ },
139
+ l10nConfig: site.l10n
140
+ })
141
+
142
+ // Fetch the translation message data using the target locale.
143
+ const {data: messages} = useQuery({
144
+ queryKey: ['app', 'translationas', 'messages', targetLocale],
145
+ queryFn: () => fetchTranslations(targetLocale),
146
+ enabled: isServer
147
+ })
148
+
149
+ // Used to conditionally render header/footer for checkout page
150
+ const isCheckout = /\/checkout$/.test(location?.pathname)
151
+
152
+ const {l10n} = site
153
+ // Get the current currency to be used through out the app
154
+ const currency = locale.preferredCurrency || l10n.defaultCurrency
155
+
156
+ // Handle creating a new basket if there isn't one already assigned to the current
157
+ // customer.
158
+ const {data: customer} = useCurrentCustomer()
159
+ const {data: baskets} = useCustomerBaskets(
160
+ {parameters: {customerId: customer.customerId}},
161
+ {enabled: !!customer.customerId && !isServer}
162
+ )
163
+ const createBasket = useShopperBasketsMutation('createBasket')
164
+ const updateBasket = useShopperBasketsMutation('updateBasket')
165
+
166
+ useEffect(() => {
167
+ // Create a new basket if the current customer doesn't have one.
168
+ if (baskets?.total === 0) {
169
+ createBasket.mutate({
170
+ body: {}
171
+ })
172
+ }
173
+ // update the basket currency if it doesn't match the current locale currency
174
+ if (baskets?.baskets?.[0]?.currency && baskets.baskets[0].currency !== currency) {
175
+ updateBasket.mutate({
176
+ parameters: {basketId: baskets.baskets[0].basketId},
177
+ body: {currency}
178
+ })
179
+ }
180
+ }, [baskets])
181
+
182
+ useEffect(() => {
183
+ // Listen for online status changes.
184
+ watchOnlineStatus((isOnline) => {
185
+ setIsOnline(isOnline)
186
+ })
187
+ }, [])
188
+
189
+ useEffect(() => {
190
+ // Lets automatically close the mobile navigation when the
191
+ // location path is changed.
192
+ onClose()
193
+ }, [location])
194
+
195
+ const onLogoClick = () => {
196
+ // Goto the home page.
197
+ const path = buildUrl(HOME_HREF)
198
+
199
+ history.push(path)
200
+
201
+ // Close the drawer.
202
+ onClose()
203
+ }
204
+
205
+ const onCartClick = () => {
206
+ const path = buildUrl('/cart')
207
+ history.push(path)
208
+
209
+ // Close the drawer.
210
+ onClose()
211
+ }
212
+
213
+ const onAccountClick = () => {
214
+ // Link to account page for registered customer, open auth modal otherwise
215
+ if (isRegistered) {
216
+ const path = buildUrl('/account')
217
+ history.push(path)
218
+ } else {
219
+ // if they already are at the login page, do not show login modal
220
+ if (new RegExp(`^/login$`).test(location.pathname)) return
221
+ authModal.onOpen()
222
+ }
223
+ }
224
+
225
+ const onWishlistClick = () => {
226
+ const path = buildUrl('/account/wishlist')
227
+ history.push(path)
228
+ }
229
+
230
+ return (
231
+ <Box className="sf-app" {...styles.container}>
232
+ <IntlProvider
233
+ onError={(err) => {
234
+ if (!messages) {
235
+ // During the ssr prepass phase the messages object has not loaded, so we can suppress
236
+ // errors during this time.
237
+ return
238
+ }
239
+ if (err.code === 'MISSING_TRANSLATION') {
240
+ // NOTE: Remove the console error for missing translations during development,
241
+ // as we knew translations would be added later.
242
+ console.warn('Missing translation', err.message)
243
+ return
244
+ }
245
+ throw err
246
+ }}
247
+ locale={targetLocale}
248
+ messages={messages}
249
+ // For react-intl, the _default locale_ refers to the locale that the inline `defaultMessage`s are written for.
250
+ // NOTE: if you update this value, please also update the following npm scripts in `template-retail-react-app/package.json`:
251
+ // - "extract-default-translations"
252
+ // - "compile-translations:pseudo"
253
+ defaultLocale={DEFAULT_LOCALE}
254
+ >
255
+ <CurrencyProvider currency={currency}>
256
+ <Seo>
257
+ <meta name="theme-color" content={THEME_COLOR} />
258
+ <meta name="apple-mobile-web-app-title" content={DEFAULT_SITE_TITLE} />
259
+ <link
260
+ rel="apple-touch-icon"
261
+ href={getAssetUrl('static/img/global/apple-touch-icon.png')}
262
+ />
263
+ <link rel="manifest" href={getAssetUrl('static/manifest.json')} />
264
+
265
+ {/* Urls for all localized versions of this page (including current page)
266
+ For more details on hrefLang, see https://developers.google.com/search/docs/advanced/crawling/localized-versions */}
267
+ {site.l10n?.supportedLocales.map((locale) => (
268
+ <link
269
+ rel="alternate"
270
+ hrefLang={locale.id.toLowerCase()}
271
+ href={`${appOrigin}${buildUrl(location.pathname)}`}
272
+ key={locale.id}
273
+ />
274
+ ))}
275
+ {/* A general locale as fallback. For example: "en" if default locale is "en-GB" */}
276
+ <link
277
+ rel="alternate"
278
+ hrefLang={site.l10n.defaultLocale.slice(0, 2)}
279
+ href={`${appOrigin}${buildUrl(location.pathname)}`}
280
+ />
281
+ {/* A wider fallback for user locales that the app does not support */}
282
+ <link rel="alternate" hrefLang="x-default" href={`${appOrigin}/`} />
283
+ </Seo>
284
+
285
+ <ScrollToTop />
286
+
287
+ <Box id="app" display="flex" flexDirection="column" flex={1}>
288
+ <SkipNavLink zIndex="skipLink">Skip to Content</SkipNavLink>
289
+
290
+ <Box {...styles.headerWrapper}>
291
+ {!isCheckout ? (
292
+ <>
293
+ <AboveHeader />
294
+ <Header
295
+ onMenuClick={onOpen}
296
+ onLogoClick={onLogoClick}
297
+ onMyCartClick={onCartClick}
298
+ onMyAccountClick={onAccountClick}
299
+ onWishlistClick={onWishlistClick}
300
+ >
301
+ <HideOnDesktop>
302
+ <DrawerMenu
303
+ isOpen={isOpen}
304
+ onClose={onClose}
305
+ onLogoClick={onLogoClick}
306
+ root={categories?.[CAT_MENU_DEFAULT_ROOT_CATEGORY]}
307
+ />
308
+ </HideOnDesktop>
309
+
310
+ <HideOnMobile>
311
+ <ListMenu
312
+ root={categories?.[CAT_MENU_DEFAULT_ROOT_CATEGORY]}
313
+ />
314
+ </HideOnMobile>
315
+ </Header>
316
+ </>
317
+ ) : (
318
+ <CheckoutHeader />
319
+ )}
320
+ </Box>
321
+ {!isOnline && <OfflineBanner />}
322
+ <AddToCartModalProvider>
323
+ <SkipNavContent
324
+ style={{
325
+ display: 'flex',
326
+ flexDirection: 'column',
327
+ flex: 1,
328
+ outline: 0
329
+ }}
330
+ >
331
+ <Box
332
+ as="main"
333
+ id="app-main"
334
+ role="main"
335
+ display="flex"
336
+ flexDirection="column"
337
+ flex="1"
338
+ >
339
+ <OfflineBoundary isOnline={false}>{children}</OfflineBoundary>
340
+ </Box>
341
+ </SkipNavContent>
342
+
343
+ {!isCheckout ? <Footer /> : <CheckoutFooter />}
344
+
345
+ <AuthModal {...authModal} />
346
+ </AddToCartModalProvider>
347
+ </Box>
348
+ </CurrencyProvider>
349
+ </IntlProvider>
350
+ </Box>
351
+ )
352
+ }
353
+
354
+ App.propTypes = {
355
+ children: PropTypes.node
356
+ }
357
+
358
+ /**
359
+ * a hook that parallelly and individually fetches category based on the given ids
360
+ * @param ids - list of categories ids to fetch
361
+ * @param queryOptions - react query options
362
+ * @return list of react query results
363
+ */
364
+ export const useCategoryBulk = (ids = [], queryOptions) => {
365
+ const api = useCommerceApi()
366
+ const {getTokenWhenReady} = useAccessToken()
367
+ const {
368
+ app: {commerceAPI}
369
+ } = getConfig()
370
+ const {
371
+ parameters: {organizationId}
372
+ } = commerceAPI
373
+ const {site} = useMultiSite()
374
+
375
+ const queries = ids.map((id) => {
376
+ return {
377
+ queryKey: queryKeyHelpers.getCategory.queryKey({
378
+ id,
379
+ levels: 2,
380
+ organizationId,
381
+ siteId: site.id
382
+ }),
383
+ queryFn: async () => {
384
+ const token = await getTokenWhenReady()
385
+ const res = await api.shopperProducts.getCategory({
386
+ parameters: {id, levels: 2},
387
+ headers: {
388
+ authorization: `Bearer ${token}`
389
+ }
390
+ })
391
+ return res
392
+ },
393
+ ...queryOptions,
394
+ enabled: queryOptions.enabled !== false && Boolean(id)
395
+ }
396
+ })
397
+ const res = useQueries({queries})
398
+ return res
399
+ }
400
+
401
+ export default App
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import React from 'react'
8
+ import {screen} from '@testing-library/react'
9
+ import {Helmet} from 'react-helmet'
10
+
11
+ import App from '@salesforce/retail-react-app/app/components/_app/index.jsx'
12
+ import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
13
+ import {DEFAULT_LOCALE} from '@salesforce/retail-react-app/app/utils/test-utils'
14
+ import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'
15
+ import messages from '@salesforce/retail-react-app/translations/compiled/en-GB.json'
16
+ import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
17
+ jest.mock('../../hooks/use-multi-site', () => jest.fn())
18
+ let windowSpy
19
+ beforeAll(() => {
20
+ jest.spyOn(console, 'log').mockImplementation(jest.fn())
21
+ jest.spyOn(console, 'groupCollapsed').mockImplementation(jest.fn())
22
+ })
23
+
24
+ afterAll(() => {
25
+ console.log.mockRestore()
26
+ console.groupCollapsed.mockRestore()
27
+ })
28
+ beforeEach(() => {
29
+ windowSpy = jest.spyOn(window, 'window', 'get')
30
+ })
31
+
32
+ afterEach(() => {
33
+ console.log.mockClear()
34
+ console.groupCollapsed.mockClear()
35
+ windowSpy.mockRestore()
36
+ })
37
+
38
+ describe('App', () => {
39
+ const site = {
40
+ ...mockConfig.app.sites[0],
41
+ alias: 'uk'
42
+ }
43
+
44
+ const locale = DEFAULT_LOCALE
45
+
46
+ const buildUrl = jest.fn().mockImplementation((href, site, locale) => {
47
+ return `${site ? `/${site}` : ''}${locale ? `/${locale}` : ''}${href}`
48
+ })
49
+
50
+ const resultUseMultiSite = {
51
+ site,
52
+ locale,
53
+ buildUrl
54
+ }
55
+
56
+ test('App component is rendered appropriately', () => {
57
+ useMultiSite.mockImplementation(() => resultUseMultiSite)
58
+ renderWithProviders(
59
+ <App targetLocale={DEFAULT_LOCALE} defaultLocale={DEFAULT_LOCALE} messages={messages}>
60
+ <p>Any children here</p>
61
+ </App>
62
+ )
63
+ screen.debug()
64
+ expect(screen.getByRole('main')).toBeInTheDocument()
65
+ expect(screen.getByText('Any children here')).toBeInTheDocument()
66
+ })
67
+
68
+ test('The localized hreflang links exist in the html head', () => {
69
+ useMultiSite.mockImplementation(() => resultUseMultiSite)
70
+ renderWithProviders(
71
+ <App targetLocale={DEFAULT_LOCALE} defaultLocale={DEFAULT_LOCALE} messages={messages} />
72
+ )
73
+
74
+ const helmet = Helmet.peek()
75
+ const hreflangLinks = helmet.linkTags.filter((link) => link.rel === 'alternate')
76
+
77
+ const hasGeneralLocale = ({hrefLang}) => hrefLang === DEFAULT_LOCALE.slice(0, 2)
78
+
79
+ // `length + 2` because one for a general locale and the other with x-default value
80
+ expect(hreflangLinks).toHaveLength(resultUseMultiSite.site.l10n.supportedLocales.length + 2)
81
+
82
+ expect(hreflangLinks.some((link) => hasGeneralLocale(link))).toBe(true)
83
+ expect(hreflangLinks.some((link) => link.hrefLang === 'x-default')).toBe(true)
84
+ })
85
+ })
@@ -0,0 +1,10 @@
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
+
8
+ const AboveHeader = () => null
9
+
10
+ export default AboveHeader
@@ -0,0 +1,125 @@
1
+ /*
2
+ * Copyright (c) 2021, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import React from 'react'
8
+ import PropTypes from 'prop-types'
9
+ import {ChakraProvider} from '@chakra-ui/react'
10
+
11
+ // Removes focus for non-keyboard interactions for the whole application
12
+ import 'focus-visible/dist/focus-visible'
13
+
14
+ import theme from '@salesforce/retail-react-app/app/theme'
15
+ import {MultiSiteProvider} from '@salesforce/retail-react-app/app/contexts'
16
+ import {
17
+ resolveSiteFromUrl,
18
+ resolveLocaleFromUrl
19
+ } from '@salesforce/retail-react-app/app/utils/site-utils'
20
+ import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
21
+ import {createUrlTemplate} from '@salesforce/retail-react-app/app/utils/url'
22
+
23
+ import {CommerceApiProvider} from '@salesforce/commerce-sdk-react'
24
+ import {withReactQuery} from '@salesforce/pwa-kit-react-sdk/ssr/universal/components/with-react-query'
25
+ import {useCorrelationId} from '@salesforce/pwa-kit-react-sdk/ssr/universal/hooks'
26
+ import {getAppOrigin} from '@salesforce/pwa-kit-react-sdk/utils/url'
27
+ import {ReactQueryDevtools} from '@tanstack/react-query-devtools'
28
+
29
+ /**
30
+ * Use the AppConfig component to inject extra arguments into the getProps
31
+ * methods for all Route Components in the app – typically you'd want to do this
32
+ * to inject a connector instance that can be used in all Pages.
33
+ *
34
+ * You can also use the AppConfig to configure a state-management library such
35
+ * as Redux, or Mobx, if you like.
36
+ */
37
+ const AppConfig = ({children, locals = {}}) => {
38
+ const {correlationId} = useCorrelationId()
39
+ const headers = {
40
+ 'correlation-id': correlationId
41
+ }
42
+
43
+ const commerceApiConfig = locals.appConfig.commerceAPI
44
+
45
+ const appOrigin = getAppOrigin()
46
+
47
+ return (
48
+ <CommerceApiProvider
49
+ shortCode={commerceApiConfig.parameters.shortCode}
50
+ clientId={commerceApiConfig.parameters.clientId}
51
+ organizationId={commerceApiConfig.parameters.organizationId}
52
+ siteId={locals.site?.id}
53
+ locale={locals.locale?.id}
54
+ currency={locals.locale?.preferredCurrency}
55
+ redirectURI={`${appOrigin}/callback`}
56
+ proxy={`${appOrigin}${commerceApiConfig.proxyPath}`}
57
+ headers={headers}
58
+ OCAPISessionsURL={`${appOrigin}/mobify/proxy/ocapi/s/${locals.site?.id}/dw/shop/v22_8/sessions`}
59
+ >
60
+ <MultiSiteProvider site={locals.site} locale={locals.locale} buildUrl={locals.buildUrl}>
61
+ <ChakraProvider theme={theme}>{children}</ChakraProvider>
62
+ </MultiSiteProvider>
63
+ <ReactQueryDevtools />
64
+ </CommerceApiProvider>
65
+ )
66
+ }
67
+
68
+ AppConfig.restore = (locals = {}) => {
69
+ const path =
70
+ typeof window === 'undefined'
71
+ ? locals.originalUrl
72
+ : `${window.location.pathname}${window.location.search}`
73
+ const site = resolveSiteFromUrl(path)
74
+ const locale = resolveLocaleFromUrl(path)
75
+
76
+ const {app: appConfig} = getConfig()
77
+ const apiConfig = {
78
+ ...appConfig.commerceAPI,
79
+ einsteinConfig: appConfig.einsteinAPI
80
+ }
81
+
82
+ apiConfig.parameters.siteId = site.id
83
+
84
+ locals.buildUrl = createUrlTemplate(appConfig, site.alias || site.id, locale.id)
85
+ locals.site = site
86
+ locals.locale = locale
87
+ locals.appConfig = appConfig
88
+ }
89
+
90
+ AppConfig.freeze = () => undefined
91
+
92
+ AppConfig.extraGetPropsArgs = (locals = {}) => {
93
+ return {
94
+ buildUrl: locals.buildUrl,
95
+ site: locals.site,
96
+ locale: locals.locale
97
+ }
98
+ }
99
+
100
+ AppConfig.propTypes = {
101
+ children: PropTypes.node,
102
+ locals: PropTypes.object
103
+ }
104
+
105
+ const isServerSide = typeof window === 'undefined'
106
+
107
+ // Recommended settings for PWA-Kit usages.
108
+ // NOTE: they will be applied on both server and client side.
109
+ const options = {
110
+ queryClientConfig: {
111
+ defaultOptions: {
112
+ queries: {
113
+ retry: false,
114
+ refetchOnWindowFocus: false,
115
+ staleTime: 10 * 1000,
116
+ ...(isServerSide ? {retryOnMount: false} : {})
117
+ },
118
+ mutations: {
119
+ retry: false
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ export default withReactQuery(AppConfig, options)