@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,98 @@
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 ConfirmationModal from '@salesforce/retail-react-app/app/components/confirmation-modal/index'
9
+ import {Box, useDisclosure} from '@chakra-ui/react'
10
+ import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
11
+ import userEvent from '@testing-library/user-event'
12
+ import {screen} from '@testing-library/react'
13
+ import {REMOVE_CART_ITEM_CONFIRMATION_DIALOG_CONFIG} from '@salesforce/retail-react-app/app/pages/cart/partials/cart-secondary-button-group'
14
+
15
+ const MockedComponent = (props) => {
16
+ const modalProps = useDisclosure()
17
+
18
+ return (
19
+ <Box>
20
+ <button onClick={modalProps.onOpen}>Open Modal</button>
21
+ <ConfirmationModal {...modalProps} {...props} />
22
+ </Box>
23
+ )
24
+ }
25
+
26
+ afterEach(() => {
27
+ jest.resetModules()
28
+ })
29
+
30
+ test('Renders confirmation modal with default config', async () => {
31
+ const user = userEvent.setup()
32
+
33
+ renderWithProviders(<MockedComponent />)
34
+
35
+ // open the modal
36
+ const trigger = screen.getByText(/open modal/i)
37
+ await user.click(trigger)
38
+
39
+ expect(screen.getByText(/confirm action/i)).toBeInTheDocument()
40
+ expect(screen.getByText(/are you sure you want to continue/i)).toBeInTheDocument()
41
+ expect(screen.getByText(/yes/i)).toBeInTheDocument()
42
+ expect(screen.getByText(/no/i)).toBeInTheDocument()
43
+ })
44
+
45
+ test('Renders confirmation modal with the given config', async () => {
46
+ const user = userEvent.setup()
47
+
48
+ renderWithProviders(<MockedComponent {...REMOVE_CART_ITEM_CONFIRMATION_DIALOG_CONFIG} />)
49
+
50
+ // open the modal
51
+ const trigger = screen.getByText(/open modal/i)
52
+ await user.click(trigger)
53
+
54
+ expect(screen.getByText(/confirm remove item/i)).toBeInTheDocument()
55
+ expect(screen.getByText(/are you sure you want to remove this item/i)).toBeInTheDocument()
56
+ expect(screen.getByText(/yes, remove item/i)).toBeInTheDocument()
57
+ expect(screen.getByText(/no, keep item/i)).toBeInTheDocument()
58
+ })
59
+
60
+ test('Verify confirm action button click', async () => {
61
+ const user = userEvent.setup()
62
+
63
+ const onPrimaryAction = jest.fn()
64
+
65
+ renderWithProviders(<MockedComponent onPrimaryAction={onPrimaryAction} />)
66
+
67
+ // open the modal
68
+ const trigger = screen.getByText(/open modal/i)
69
+ await user.click(trigger)
70
+
71
+ const onPrimaryActionTrigger = screen.getByText(/yes/i)
72
+
73
+ expect(screen.getByText(/confirm action/i)).toBeInTheDocument()
74
+ expect(onPrimaryActionTrigger).toBeInTheDocument()
75
+
76
+ await user.click(onPrimaryActionTrigger)
77
+ expect(onPrimaryAction).toHaveBeenCalledTimes(1)
78
+ })
79
+
80
+ test('Verify cancel action button click', async () => {
81
+ const user = userEvent.setup()
82
+
83
+ const onAlternateAction = jest.fn()
84
+
85
+ renderWithProviders(<MockedComponent onAlternateAction={onAlternateAction} />)
86
+
87
+ // open the modal
88
+ const trigger = screen.getByText(/open modal/i)
89
+ await user.click(trigger)
90
+
91
+ const onAlternateActionTrigger = screen.getByText(/no/i)
92
+
93
+ expect(screen.getByText(/confirm action/i)).toBeInTheDocument()
94
+ expect(onAlternateActionTrigger).toBeInTheDocument()
95
+
96
+ await user.click(onAlternateActionTrigger)
97
+ expect(onAlternateAction).toHaveBeenCalledTimes(1)
98
+ })
@@ -0,0 +1,405 @@
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 {useIntl} from 'react-intl'
11
+
12
+ // Project Components
13
+ import LocaleSelector from '@salesforce/retail-react-app/app/components/locale-selector'
14
+ import NestedAccordion from '@salesforce/retail-react-app/app/components/nested-accordion'
15
+ import SocialIcons from '@salesforce/retail-react-app/app/components/social-icons'
16
+ // Components
17
+ import {
18
+ Box,
19
+ AccordionButton,
20
+ AccordionItem,
21
+ Button,
22
+ Center,
23
+ Divider,
24
+ Drawer,
25
+ DrawerBody,
26
+ DrawerFooter,
27
+ DrawerHeader,
28
+ DrawerOverlay,
29
+ DrawerContent,
30
+ DrawerCloseButton,
31
+ Fade,
32
+ HStack,
33
+ IconButton,
34
+ Flex,
35
+ Spinner,
36
+ Text,
37
+ VStack,
38
+
39
+ // Hooks
40
+ useBreakpointValue,
41
+ useMultiStyleConfig
42
+ } from '@chakra-ui/react'
43
+ import {AuthHelpers, useAuthHelper, useCustomerType} from '@salesforce/commerce-sdk-react'
44
+ import Link from '@salesforce/retail-react-app/app/components/link'
45
+ // Icons
46
+ import {
47
+ BrandLogo,
48
+ LocationIcon,
49
+ SignoutIcon,
50
+ UserIcon
51
+ } from '@salesforce/retail-react-app/app/components/icons'
52
+
53
+ // Others
54
+ import {noop} from '@salesforce/retail-react-app/app/utils/utils'
55
+ import {getPathWithLocale, categoryUrlBuilder} from '@salesforce/retail-react-app/app/utils/url'
56
+ import LoadingSpinner from '@salesforce/retail-react-app/app/components/loading-spinner'
57
+
58
+ import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation'
59
+ import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'
60
+
61
+ // The FONT_SIZES and FONT_WEIGHTS constants are used to control the styling for
62
+ // the accordion buttons as their current depth. In the below definition we assign
63
+ // values for depths 0 - 3, any depth deeper than that will use the default styling.
64
+ const FONT_SIZES = ['lg', 'md', 'md']
65
+ const FONT_WEIGHTS = ['semibold', 'semibold', 'regular']
66
+ const PHONE_DRAWER_SIZE = 'xs'
67
+ const TABLET_DRAWER_SIZE = 'lg'
68
+
69
+ const DrawerSeparator = () => (
70
+ <Box paddingTop="6" paddingBottom="6">
71
+ <Divider />
72
+ </Box>
73
+ )
74
+
75
+ // CUSTOMIZE YOUR NAVIGATION BY ALTERING THESE VALUES
76
+ const SIGN_IN_HREF = '/login'
77
+ const STORE_LOCATOR_HREF = '/store-locator'
78
+
79
+ /**
80
+ * This is the navigation component used for mobile devices (phone and tablet). It's
81
+ * main usage is to navigate from one category to the next, but also homes links to
82
+ * support, log in and out actions, as support links.
83
+ */
84
+ const DrawerMenu = ({root, isOpen, onClose = noop, onLogoClick = noop}) => {
85
+ const itemsKey = 'categories'
86
+ const intl = useIntl()
87
+ const {isRegistered} = useCustomerType()
88
+ const navigate = useNavigation()
89
+ const styles = useMultiStyleConfig('DrawerMenu')
90
+ const drawerSize = useBreakpointValue({sm: PHONE_DRAWER_SIZE, md: TABLET_DRAWER_SIZE})
91
+ const socialIconVariant = useBreakpointValue({base: 'flex', md: 'flex-start'})
92
+ const {site, buildUrl} = useMultiSite()
93
+ const {l10n} = site
94
+ const [showLoading, setShowLoading] = useState(false)
95
+ const [ariaBusy, setAriaBusy] = useState('true')
96
+ const logout = useAuthHelper(AuthHelpers.Logout)
97
+ const onSignoutClick = async () => {
98
+ setShowLoading(true)
99
+ await logout.mutateAsync()
100
+ navigate('/login')
101
+ setShowLoading(false)
102
+ }
103
+
104
+ const supportedLocaleIds = l10n?.supportedLocales.map((locale) => locale.id)
105
+ const showLocaleSelector = supportedLocaleIds?.length > 1
106
+
107
+ useEffect(() => {
108
+ setAriaBusy('false')
109
+ }, [])
110
+
111
+ return (
112
+ <Drawer isOpen={isOpen} onClose={onClose} placement="left" size={drawerSize}>
113
+ <DrawerOverlay>
114
+ <DrawerContent>
115
+ {/* Header Content */}
116
+ <DrawerHeader>
117
+ <IconButton
118
+ icon={<BrandLogo {...styles.logo} />}
119
+ variant="unstyled"
120
+ onClick={onLogoClick}
121
+ />
122
+
123
+ <DrawerCloseButton />
124
+ </DrawerHeader>
125
+
126
+ {/* Main Content */}
127
+ <DrawerBody>
128
+ <div
129
+ id="category-nav"
130
+ aria-live="polite"
131
+ aria-busy={ariaBusy}
132
+ aria-atomic="true"
133
+ >
134
+ {showLoading && <LoadingSpinner />}
135
+
136
+ {/* Category Navigation */}
137
+ {root?.[itemsKey] ? (
138
+ <Fade in={true}>
139
+ <NestedAccordion
140
+ allowMultiple={true}
141
+ item={root}
142
+ itemsKey={itemsKey}
143
+ itemsFilter="c_showInMenu"
144
+ fontSizes={FONT_SIZES}
145
+ fontWeights={FONT_WEIGHTS}
146
+ itemsBefore={({depth, item}) =>
147
+ depth > 0 ? (
148
+ [
149
+ <AccordionItem border="none" key="show-all">
150
+ <AccordionButton
151
+ paddingLeft={8}
152
+ as={Link}
153
+ to={categoryUrlBuilder(item)}
154
+ fontSize={FONT_SIZES[depth]}
155
+ fontWeight={FONT_WEIGHTS[depth]}
156
+ color="black"
157
+ >
158
+ {intl.formatMessage({
159
+ id: 'drawer_menu.link.shop_all',
160
+ defaultMessage: 'Shop All'
161
+ })}
162
+ </AccordionButton>
163
+ </AccordionItem>
164
+ ]
165
+ ) : (
166
+ <></>
167
+ )
168
+ }
169
+ urlBuilder={categoryUrlBuilder}
170
+ />
171
+ </Fade>
172
+ ) : (
173
+ <Center p="8">
174
+ <Spinner size="xl" />
175
+ </Center>
176
+ )}
177
+ </div>
178
+
179
+ <DrawerSeparator />
180
+
181
+ {/* Application Actions */}
182
+ <VStack align="stretch" spacing={0} {...styles.actions} px={0}>
183
+ <Box {...styles.actionsItem}>
184
+ {isRegistered ? (
185
+ <NestedAccordion
186
+ urlBuilder={(item, locale) =>
187
+ `/${locale}/account${item.path}`
188
+ }
189
+ itemsAfter={({depth}) =>
190
+ depth === 1 && (
191
+ <Button
192
+ {...styles.signout}
193
+ variant="unstyled"
194
+ onClick={onSignoutClick}
195
+ >
196
+ <Flex align={'center'}>
197
+ <SignoutIcon boxSize={5} />
198
+ <Text {...styles.signoutText} as="span">
199
+ {intl.formatMessage({
200
+ id: 'drawer_menu.button.log_out',
201
+ defaultMessage: 'Log Out'
202
+ })}
203
+ </Text>
204
+ </Flex>
205
+ </Button>
206
+ )
207
+ }
208
+ item={{
209
+ id: 'root',
210
+ items: [
211
+ {
212
+ id: 'my-account',
213
+ name: intl.formatMessage({
214
+ id: 'drawer_menu.button.my_account',
215
+ defaultMessage: 'My Account'
216
+ }),
217
+ items: [
218
+ {
219
+ id: 'profile',
220
+ path: '',
221
+ name: intl.formatMessage({
222
+ id: 'drawer_menu.button.account_details',
223
+ defaultMessage: 'Account Details'
224
+ })
225
+ },
226
+ {
227
+ id: 'orders',
228
+ path: '/orders',
229
+ name: intl.formatMessage({
230
+ id: 'drawer_menu.button.order_history',
231
+ defaultMessage: 'Order History'
232
+ })
233
+ },
234
+ {
235
+ id: 'addresses',
236
+ path: '/addresses',
237
+ name: intl.formatMessage({
238
+ id: 'drawer_menu.button.addresses',
239
+ defaultMessage: 'Addresses'
240
+ })
241
+ }
242
+ ]
243
+ }
244
+ ]
245
+ }}
246
+ />
247
+ ) : (
248
+ <Link to={SIGN_IN_HREF}>
249
+ <HStack>
250
+ <UserIcon {...styles.icon} />{' '}
251
+ <Text>
252
+ {intl.formatMessage({
253
+ id: 'drawer_menu.link.sign_in',
254
+ defaultMessage: 'Sign In'
255
+ })}
256
+ </Text>
257
+ </HStack>
258
+ </Link>
259
+ )}
260
+ </Box>
261
+ <Box {...styles.actionsItem}>
262
+ <Link to={STORE_LOCATOR_HREF}>
263
+ <HStack>
264
+ <LocationIcon {...styles.icon} />{' '}
265
+ <Text>
266
+ {intl.formatMessage({
267
+ id: 'drawer_menu.link.store_locator',
268
+ defaultMessage: 'Store Locator'
269
+ })}
270
+ </Text>
271
+ </HStack>
272
+ </Link>
273
+ </Box>
274
+ {showLocaleSelector && (
275
+ <Box>
276
+ <LocaleSelector
277
+ {...styles.localeSelector}
278
+ selectedLocale={intl.locale}
279
+ locales={supportedLocaleIds}
280
+ onSelect={(newLocale) => {
281
+ // Update the `locale` in the URL.
282
+ const newUrl = getPathWithLocale(newLocale, buildUrl, {
283
+ disallowParams: ['refine']
284
+ })
285
+ window.location = newUrl
286
+ }}
287
+ />
288
+ </Box>
289
+ )}
290
+ </VStack>
291
+
292
+ <DrawerSeparator />
293
+
294
+ {/* Support Links */}
295
+ <NestedAccordion
296
+ allowMultiple={true}
297
+ // NOTE: Modify this content and builder as you see fit.
298
+ urlBuilder={() => '/'}
299
+ item={{
300
+ id: 'links-root',
301
+ items: [
302
+ {
303
+ id: 'customersupport',
304
+ items: [
305
+ {
306
+ id: 'contactus',
307
+ name: intl.formatMessage({
308
+ id: 'drawer_menu.link.customer_support.contact_us',
309
+ defaultMessage: 'Contact Us'
310
+ })
311
+ },
312
+ {
313
+ id: 'shippingandreturns',
314
+ name: intl.formatMessage({
315
+ id: 'drawer_menu.link.customer_support.shipping_and_returns',
316
+ defaultMessage: 'Shipping & Returns'
317
+ })
318
+ }
319
+ ],
320
+ name: intl.formatMessage({
321
+ id: 'drawer_menu.link.customer_support',
322
+ defaultMessage: 'Customer Support'
323
+ })
324
+ },
325
+ {
326
+ id: 'ourcompany',
327
+ items: [
328
+ {
329
+ id: 'aboutus',
330
+ name: intl.formatMessage({
331
+ id: 'drawer_menu.link.about_us',
332
+ defaultMessage: 'About Us'
333
+ })
334
+ }
335
+ ],
336
+ name: intl.formatMessage({
337
+ id: 'drawer_menu.link.our_company',
338
+ defaultMessage: 'Our Company'
339
+ })
340
+ },
341
+ {
342
+ id: 'privacyandsecurity',
343
+ items: [
344
+ {
345
+ id: 'termsandconditions',
346
+ name: intl.formatMessage({
347
+ id: 'drawer_menu.link.terms_and_conditions',
348
+ defaultMessage: 'Terms & Conditions'
349
+ })
350
+ },
351
+ {
352
+ id: 'privacypolicy',
353
+ name: intl.formatMessage({
354
+ id: 'drawer_menu.link.privacy_policy',
355
+ defaultMessage: 'Privacy Policy'
356
+ })
357
+ },
358
+ {
359
+ id: 'sitemap',
360
+ name: intl.formatMessage({
361
+ id: 'drawer_menu.link.site_map',
362
+ defaultMessage: 'Site Map'
363
+ })
364
+ }
365
+ ],
366
+ name: intl.formatMessage({
367
+ id: 'drawer_menu.link.privacy_and_security',
368
+ defaultMessage: 'Privacy & Security'
369
+ })
370
+ }
371
+ ]
372
+ }}
373
+ />
374
+
375
+ <DrawerSeparator />
376
+ </DrawerBody>
377
+
378
+ <DrawerFooter>
379
+ <SocialIcons variant={socialIconVariant} />
380
+ </DrawerFooter>
381
+ </DrawerContent>
382
+ </DrawerOverlay>
383
+ </Drawer>
384
+ )
385
+ }
386
+
387
+ DrawerMenu.displayName = 'DrawerMenu'
388
+
389
+ DrawerMenu.propTypes = {
390
+ root: PropTypes.object,
391
+ /**
392
+ * The opened state of the drawer.
393
+ */
394
+ isOpen: PropTypes.bool,
395
+ /**
396
+ * Function called when the drawer is dismissed.
397
+ */
398
+ onClose: PropTypes.func,
399
+ /**
400
+ * Function called when the drawer logo is clicked.
401
+ */
402
+ onLogoClick: PropTypes.func
403
+ }
404
+
405
+ export default DrawerMenu
@@ -0,0 +1,33 @@
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 DrawerMenu from '@salesforce/retail-react-app/app/components/drawer-menu/index'
9
+ import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
10
+ import {mockCategories} from '@salesforce/retail-react-app/app/mocks/mock-data'
11
+
12
+ describe('DrawerMenu', () => {
13
+ test('Renders DrawerMenu without errors', async () => {
14
+ renderWithProviders(<DrawerMenu isOpen={true} root={mockCategories.root} />)
15
+
16
+ const drawer = document.querySelector('.chakra-portal')
17
+ const accordion = document.querySelector('.chakra-accordion')
18
+ const socialIcons = document.querySelector('.sf-social-icons')
19
+
20
+ expect(drawer).toBeInTheDocument()
21
+ expect(accordion).toBeInTheDocument()
22
+ expect(socialIcons).toBeInTheDocument()
23
+ })
24
+ test('Renders DrawerMenu Spinner without root', async () => {
25
+ renderWithProviders(<DrawerMenu isOpen={true} />, {
26
+ wrapperProps: {initialCategories: {}}
27
+ })
28
+
29
+ const spinner = document.querySelector('.chakra-spinner')
30
+
31
+ expect(spinner).toBeInTheDocument()
32
+ })
33
+ })
@@ -0,0 +1,56 @@
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, {useMemo} from 'react'
8
+ import PropTypes from 'prop-types'
9
+ import {Img, Box, useTheme} from '@chakra-ui/react'
10
+ import {getResponsiveImageAttributes} from '@salesforce/retail-react-app/app/utils/responsive-image'
11
+
12
+ /**
13
+ * Quickly create a responsive image using your dynamic image service
14
+ * @example
15
+ * // Widths without a unit are interpreted as px values
16
+ * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={[100, 360, 720]} />
17
+ * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={{base: 100, sm: 360, md: 720}} />
18
+ * // You can also use units of px or vw
19
+ * <DynamicImage src="http://example.com/image.jpg[?sw={width}&q=60]" widths={['50vw', '100vw', '500px']} />
20
+ */
21
+ const DynamicImage = ({src, widths, imageProps, as, ...rest}) => {
22
+ const Component = as ? as : Img
23
+ const theme = useTheme()
24
+
25
+ const responsiveImageProps = useMemo(
26
+ () => getResponsiveImageAttributes({src, widths, breakpoints: theme.breakpoints}),
27
+ [src, widths, theme.breakpoints]
28
+ )
29
+
30
+ return (
31
+ <Box {...rest}>
32
+ <Component {...responsiveImageProps} {...imageProps} />
33
+ </Box>
34
+ )
35
+ }
36
+
37
+ DynamicImage.propTypes = {
38
+ /**
39
+ * Dynamic src having an optional param that can vary with widths. For example: `image[_{width}].jpg` or `image.jpg[?sw={width}&q=60]`
40
+ */
41
+ src: PropTypes.string,
42
+ /**
43
+ * Image widths relative to the breakpoints, whose units can either be px or vw or unit-less. They will be mapped to the corresponding `sizes` and `srcSet`.
44
+ */
45
+ widths: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
46
+ /**
47
+ * Props to pass to the inner image component
48
+ */
49
+ imageProps: PropTypes.object,
50
+ /**
51
+ * Override with your chosen image component
52
+ */
53
+ as: PropTypes.elementType
54
+ }
55
+
56
+ export default DynamicImage