@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,512 @@
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 {useMemo, useState} from 'react'
8
+ import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
9
+ import {useCommerceApi, useAccessToken, useUsid, useEncUserId} from '@salesforce/commerce-sdk-react'
10
+ import {keysToCamel} from '@salesforce/retail-react-app/app/utils/utils'
11
+
12
+ export class EinsteinAPI {
13
+ constructor({host, einsteinId, userId, cookieId, siteId, isProduction}) {
14
+ this.userId = userId
15
+ this.cookieId = cookieId
16
+ this.siteId = siteId
17
+ this.isProduction = isProduction
18
+ this.host = host
19
+ this.einsteinId = einsteinId
20
+ }
21
+
22
+ /**
23
+ * Given a POJO append the correct user and cookie identifier values using the current auth state.
24
+ *
25
+ * @param {object} params
26
+ * @returns {object} The decorated body object.
27
+ */
28
+ _buildBody(params) {
29
+ const instanceType_prd = 'prd'
30
+ const instanceType_sbx = 'sbx'
31
+
32
+ const body = {...params}
33
+
34
+ // If we have an encrypted user id (authenticaed users only) use it as the `userId` otherwise
35
+ // we won't send a `userId` param for guest users.
36
+ if (this.userId) {
37
+ body.userId = this.userId
38
+ }
39
+
40
+ // Append the `usid` as the `cookieId` value if present. (It should always be present as long
41
+ // as the user is initilized)
42
+ if (this.cookieId) {
43
+ body.cookieId = this.cookieId
44
+ } else {
45
+ console.warn('Missing `cookieId`. For optimal results this value must be defined.')
46
+ }
47
+
48
+ // The first part of the siteId is the realm
49
+ if (this.siteId) {
50
+ body.realm = this.siteId.split('-')[0]
51
+ }
52
+
53
+ if (this.isProduction) {
54
+ body.instanceType = instanceType_prd
55
+ } else {
56
+ body.instanceType = instanceType_sbx
57
+ }
58
+
59
+ return body
60
+ }
61
+
62
+ /**
63
+ * Given a product or item source, returns the product data that Einstein requires
64
+ */
65
+ _constructEinsteinProduct(product) {
66
+ if (product.type && (product.type.master || product.type.variant)) {
67
+ // handle variants for PDP / viewProduct
68
+ // Assumes product is a Product object from SCAPI Shopper-Products:
69
+ // https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=type%3AProduct
70
+ return {
71
+ id: product.master.masterId,
72
+ sku: product.id,
73
+ altId: '',
74
+ altIdType: ''
75
+ }
76
+ } else if (
77
+ product.productType &&
78
+ (product.productType.master ||
79
+ product.productType.variant ||
80
+ product.productType.set ||
81
+ product.productType.bundle ||
82
+ product.productType.variationGroup ||
83
+ product.productType.item)
84
+ ) {
85
+ // handle variants & sets for PLP / viewCategory & viewSearch
86
+ // Assumes product is a ProductSearchHit from SCAPI Shopper-Search:
87
+ // https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-search?meta=type%3AProductSearchHit
88
+ return {
89
+ id: product.productId,
90
+ sku: product.productId, //TODO: Should we switch this to product.representedProduct.id once we allow non-master products in search results?
91
+ altId: '',
92
+ altIdType: ''
93
+ }
94
+ } else {
95
+ // handles non-variants
96
+ return {
97
+ id: product.id,
98
+ sku: '',
99
+ altId: '',
100
+ altIdType: ''
101
+ }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Given a cart item, returns the data that Einstein requires
107
+ *
108
+ * Assumes item is a ProductItemfrom SCAPI Shopper-Baskets:
109
+ * https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=type%3AProductItem
110
+ */
111
+ _constructEinsteinItem(item) {
112
+ return {
113
+ id: item.productId,
114
+ sku: '',
115
+ price: item.price,
116
+ quantity: item.quantity
117
+ }
118
+ }
119
+
120
+ async einsteinFetch(endpoint, method, body) {
121
+ const headers = {
122
+ 'Content-Type': 'application/json',
123
+ 'x-cq-client-id': this.einsteinId
124
+ }
125
+
126
+ // Include `userId` and `cookieId` parameters.
127
+ if (body) {
128
+ body = this._buildBody(body)
129
+ }
130
+
131
+ let response
132
+ try {
133
+ response = await fetch(`${this.host}/v3${endpoint}`, {
134
+ method: method,
135
+ headers: headers,
136
+ ...(body && {
137
+ body: JSON.stringify(body)
138
+ })
139
+ })
140
+ } catch {
141
+ console.warn('Einstein request failed')
142
+ }
143
+
144
+ if (!response?.ok) {
145
+ return {}
146
+ }
147
+
148
+ const responseJson = await response.json()
149
+
150
+ return keysToCamel(responseJson)
151
+ }
152
+
153
+ /**
154
+ * Tells the Einstein engine when a user views a product.
155
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
156
+ **/
157
+ async sendViewProduct(product, args) {
158
+ const endpoint = `/activities/${this.siteId}/viewProduct`
159
+ const method = 'POST'
160
+ const body = {
161
+ product: this._constructEinsteinProduct(product),
162
+ ...args
163
+ }
164
+
165
+ return this.einsteinFetch(endpoint, method, body)
166
+ }
167
+
168
+ /**
169
+ * Tells the Einstein engine when a user views search results.
170
+ **/
171
+ async sendViewSearch(searchText, searchResults, args) {
172
+ const endpoint = `/activities/${this.siteId}/viewSearch`
173
+ const method = 'POST'
174
+
175
+ const products = searchResults?.hits?.map((product) =>
176
+ this._constructEinsteinProduct(product)
177
+ )
178
+
179
+ const body = {
180
+ searchText,
181
+ products,
182
+ showProducts: true, // Needed by Reports and Dashboards to differentiate searches with results vs no results
183
+ ...args
184
+ }
185
+
186
+ return this.einsteinFetch(endpoint, method, body)
187
+ }
188
+
189
+ /**
190
+ * Tells the Einstein engine when a user clicks on a search result.
191
+ **/
192
+ async sendClickSearch(searchText, product, args) {
193
+ const endpoint = `/activities/${this.siteId}/clickSearch`
194
+ const method = 'POST'
195
+ const body = {
196
+ searchText,
197
+ product: this._constructEinsteinProduct(product),
198
+ ...args
199
+ }
200
+
201
+ return this.einsteinFetch(endpoint, method, body)
202
+ }
203
+
204
+ /**
205
+ * Tells the Einstein engine when a user views a category.
206
+ **/
207
+ async sendViewCategory(category, searchResults, args) {
208
+ const endpoint = `/activities/${this.siteId}/viewCategory`
209
+ const method = 'POST'
210
+
211
+ const products = searchResults?.hits?.map((product) =>
212
+ this._constructEinsteinProduct(product)
213
+ )
214
+
215
+ const body = {
216
+ category: {
217
+ id: category.id
218
+ },
219
+ products,
220
+ showProducts: true, // Needed by Reports and Dashboards to differentiate searches with results vs no results
221
+ ...args
222
+ }
223
+
224
+ return this.einsteinFetch(endpoint, method, body)
225
+ }
226
+
227
+ /**
228
+ * Tells the Einstein engine when a user clicks a product from the category page.
229
+ * Not meant to be used when the user clicks a category from the nav bar.
230
+ **/
231
+ async sendClickCategory(category, product, args) {
232
+ const endpoint = `/activities/${this.siteId}/clickCategory`
233
+ const method = 'POST'
234
+ const body = {
235
+ category: {
236
+ id: category.id
237
+ },
238
+ product: this._constructEinsteinProduct(product),
239
+ ...args
240
+ }
241
+
242
+ return this.einsteinFetch(endpoint, method, body)
243
+ }
244
+
245
+ /**
246
+ * Tells the Einstein engine when a user views a set of recommendations
247
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
248
+ **/
249
+ async sendViewReco(recommenderDetails, products, args) {
250
+ const endpoint = `/activities/${this.siteId}/viewReco`
251
+ const method = 'POST'
252
+ const {__recoUUID, recommenderName} = recommenderDetails
253
+ const body = {
254
+ recommenderName,
255
+ __recoUUID,
256
+ products: products,
257
+ ...args
258
+ }
259
+
260
+ return this.einsteinFetch(endpoint, method, body)
261
+ }
262
+
263
+ /**
264
+ * Tells the Einstein engine when a user clicks on a recommendation
265
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
266
+ **/
267
+ async sendClickReco(recommenderDetails, product, args) {
268
+ const endpoint = `/activities/${this.siteId}/clickReco`
269
+ const method = 'POST'
270
+ const {__recoUUID, recommenderName} = recommenderDetails
271
+ const body = {
272
+ recommenderName,
273
+ __recoUUID,
274
+ product: this._constructEinsteinProduct(product),
275
+ ...args
276
+ }
277
+
278
+ return this.einsteinFetch(endpoint, method, body)
279
+ }
280
+
281
+ /**
282
+ * Tells the Einstein engine when a user views a page.
283
+ * Use this only for pages where another activity does not fit. (ie. on the PDP, use viewProduct rather than this)
284
+ **/
285
+ async sendViewPage(path, args) {
286
+ const endpoint = `/activities/${this.siteId}/viewPage`
287
+ const method = 'POST'
288
+ const body = {
289
+ currentLocation: path,
290
+ ...args
291
+ }
292
+
293
+ return this.einsteinFetch(endpoint, method, body)
294
+ }
295
+
296
+ /**
297
+ * Tells the Einstein engine when a user starts the checkout process.
298
+ **/
299
+ async sendBeginCheckout(basket, args) {
300
+ const endpoint = `/activities/${this.siteId}/beginCheckout`
301
+ const method = 'POST'
302
+ const products = basket.productItems.map((item) => this._constructEinsteinItem(item))
303
+ const subTotal = basket.productSubTotal
304
+ const body = {
305
+ products: products,
306
+ amount: subTotal,
307
+ ...args
308
+ }
309
+
310
+ return this.einsteinFetch(endpoint, method, body)
311
+ }
312
+
313
+ /**
314
+ * Tells the Einstein engine when a user reaches the given step during checkout.
315
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
316
+ **/
317
+ async sendCheckoutStep(stepName, stepNumber, basket, args) {
318
+ const endpoint = `/activities/${this.siteId}/checkoutStep`
319
+ const method = 'POST'
320
+ const body = {
321
+ stepName,
322
+ stepNumber,
323
+ basketId: basket.basketId,
324
+ ...args
325
+ }
326
+
327
+ return this.einsteinFetch(endpoint, method, body)
328
+ }
329
+
330
+ /**
331
+ * Tells the Einstein engine when a user adds an item to their cart.
332
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
333
+ **/
334
+ async sendAddToCart(items, args) {
335
+ const endpoint = `/activities/${this.siteId}/addToCart`
336
+ const method = 'POST'
337
+ const body = {
338
+ products: items.map((item) => this._constructEinsteinItem(item)),
339
+ ...args
340
+ }
341
+
342
+ return this.einsteinFetch(endpoint, method, body)
343
+ }
344
+
345
+ /**
346
+ * Get a list of recommenders that can be used in recommendation requests.
347
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
348
+ **/
349
+ async getRecommenders() {
350
+ const endpoint = `/personalization/recommenders/${this.siteId}`
351
+ const method = 'GET'
352
+ const body = null
353
+
354
+ return this.einsteinFetch(endpoint, method, body)
355
+ }
356
+
357
+ /**
358
+ * Get a set of recommendations
359
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
360
+ **/
361
+ async getRecommendations(recommenderName, products, args) {
362
+ const endpoint = `/personalization/recs/${this.siteId}/${recommenderName}`
363
+ const method = 'POST'
364
+ const body = {
365
+ products: products?.map((product) => this._constructEinsteinProduct(product)),
366
+ ...args
367
+ }
368
+
369
+ return this.einsteinFetch(endpoint, method, body)
370
+ }
371
+
372
+ /**
373
+ * Get a set of recommendations for a zone
374
+ * https://developer.salesforce.com/docs/commerce/einstein-api/references#einstein-recommendations:Summary
375
+ **/
376
+ async getZoneRecommendations(zoneName, products, args) {
377
+ const endpoint = `/personalization/${this.siteId}/zones/${zoneName}/recs`
378
+ const method = 'POST'
379
+
380
+ const body = {
381
+ products: products?.map((product) => this._constructEinsteinProduct(product)),
382
+ ...args
383
+ }
384
+
385
+ return this.einsteinFetch(endpoint, method, body)
386
+ }
387
+ }
388
+
389
+ const useEinstein = () => {
390
+ const api = useCommerceApi()
391
+ const {getTokenWhenReady} = useAccessToken()
392
+ const {
393
+ app: {einsteinAPI: config}
394
+ } = getConfig()
395
+ const {host, einsteinId, siteId, isProduction} = config
396
+ const usid = useUsid()
397
+ const encUserId = useEncUserId()
398
+
399
+ const einstein = useMemo(
400
+ () =>
401
+ new EinsteinAPI({
402
+ host,
403
+ einsteinId,
404
+ userId: encUserId,
405
+ cookieId: usid,
406
+ siteId,
407
+ isProduction
408
+ }),
409
+ [host, einsteinId, encUserId, usid, siteId, isProduction]
410
+ )
411
+ const [isLoading, setIsLoading] = useState(false)
412
+ const [recommendations, setRecommendations] = useState([])
413
+
414
+ const fetchRecProductDetails = async (reco) => {
415
+ const ids = reco.recs?.map((rec) => rec.id)
416
+ if (ids?.length > 0) {
417
+ const token = await getTokenWhenReady()
418
+ // Fetch the product details for the recommendations
419
+ const products = await api.shopperProducts.getProducts({
420
+ parameters: {ids: ids.join(',')},
421
+ headers: {
422
+ Authorization: `Bearer ${token}`
423
+ }
424
+ })
425
+
426
+ // Merge the product detail into the recommendations response
427
+ return {
428
+ ...reco,
429
+ recs: reco.recs.map((rec) => {
430
+ const product = products?.data?.find((product) => product.id === rec.id)
431
+ return {
432
+ ...rec,
433
+ ...product,
434
+ productId: rec.id,
435
+ image: {disBaseLink: rec.imageUrl, alt: rec.productName}
436
+ }
437
+ })
438
+ }
439
+ }
440
+ return reco
441
+ }
442
+
443
+ return {
444
+ isLoading,
445
+
446
+ recommendations,
447
+
448
+ async sendViewProduct(...args) {
449
+ return einstein.sendViewProduct(...args)
450
+ },
451
+ async sendViewSearch(...args) {
452
+ return einstein.sendViewSearch(...args)
453
+ },
454
+ async sendClickSearch(...args) {
455
+ return einstein.sendClickSearch(...args)
456
+ },
457
+ async sendViewCategory(...args) {
458
+ return einstein.sendViewCategory(...args)
459
+ },
460
+ async sendClickCategory(...args) {
461
+ return einstein.sendClickCategory(...args)
462
+ },
463
+ async sendViewPage(...args) {
464
+ return einstein.sendViewPage(...args)
465
+ },
466
+ async sendBeginCheckout(...args) {
467
+ return einstein.sendBeginCheckout(...args)
468
+ },
469
+ async sendCheckoutStep(...args) {
470
+ return einstein.sendCheckoutStep(...args)
471
+ },
472
+ async sendViewReco(...args) {
473
+ return einstein.sendViewReco(...args)
474
+ },
475
+ async sendClickReco(...args) {
476
+ return einstein.sendClickReco(...args)
477
+ },
478
+ async sendAddToCart(...args) {
479
+ return einstein.sendAddToCart(...args)
480
+ },
481
+ async getRecommenders(...args) {
482
+ return einstein.getRecommenders(...args)
483
+ },
484
+ async getRecommendations(recommenderName, products, args) {
485
+ setIsLoading(true)
486
+ try {
487
+ const reco = await einstein.getRecommendations(recommenderName, products, args)
488
+ reco.recommenderName = recommenderName
489
+ const recommendations = await fetchRecProductDetails(reco)
490
+ setRecommendations(recommendations)
491
+ } catch (err) {
492
+ console.error(err)
493
+ } finally {
494
+ setIsLoading(false)
495
+ }
496
+ },
497
+ async getZoneRecommendations(zoneName, products, args) {
498
+ setIsLoading(true)
499
+ try {
500
+ const reco = await einstein.getZoneRecommendations(zoneName, products, args)
501
+ const recommendations = await fetchRecProductDetails(reco)
502
+ setRecommendations(recommendations)
503
+ } catch (err) {
504
+ console.error(err)
505
+ } finally {
506
+ setIsLoading(false)
507
+ }
508
+ }
509
+ }
510
+ }
511
+
512
+ export default useEinstein