@magento/venia-pwa-live-search 1.0.0-alpha6

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 (202) hide show
  1. package/package.json +31 -0
  2. package/postcss.config.js +3 -0
  3. package/src/api/fragments.js +193 -0
  4. package/src/api/graphql.js +26 -0
  5. package/src/api/mutations.js +94 -0
  6. package/src/api/queries.js +225 -0
  7. package/src/api/search.js +222 -0
  8. package/src/components/AddToCartButton/AddToCartButton.jsx +32 -0
  9. package/src/components/AddToCartButton/AddToCartButton.stories.mdx +14 -0
  10. package/src/components/AddToCartButton/index.js +10 -0
  11. package/src/components/Alert/Alert.jsx +155 -0
  12. package/src/components/Alert/index.js +11 -0
  13. package/src/components/Breadcrumbs/Breadcrumbs.jsx +34 -0
  14. package/src/components/Breadcrumbs/MockPages.js +14 -0
  15. package/src/components/Breadcrumbs/index.js +11 -0
  16. package/src/components/ButtonShimmer/ButtonShimmer.css +32 -0
  17. package/src/components/ButtonShimmer/ButtonShimmer.jsx +23 -0
  18. package/src/components/ButtonShimmer/index.js +11 -0
  19. package/src/components/CategoryFilters/CategoryFilters.jsx +59 -0
  20. package/src/components/CategoryFilters/index.js +10 -0
  21. package/src/components/Facets/Facets.jsx +50 -0
  22. package/src/components/Facets/Range/RangeFacet.js +20 -0
  23. package/src/components/Facets/Scalar/ScalarFacet.js +29 -0
  24. package/src/components/Facets/SelectedFilters.js +80 -0
  25. package/src/components/Facets/format.js +52 -0
  26. package/src/components/Facets/index.js +14 -0
  27. package/src/components/Facets/mocks.js +119 -0
  28. package/src/components/FacetsShimmer/FacetsShimmer.css +49 -0
  29. package/src/components/FacetsShimmer/FacetsShimmer.jsx +25 -0
  30. package/src/components/FacetsShimmer/index.js +11 -0
  31. package/src/components/FilterButton/FilterButton.jsx +40 -0
  32. package/src/components/FilterButton/index.js +11 -0
  33. package/src/components/ImageCarousel/Image.jsx +34 -0
  34. package/src/components/ImageCarousel/ImageCarousel.jsx +103 -0
  35. package/src/components/ImageCarousel/index.js +11 -0
  36. package/src/components/InputButtonGroup/InputButtonGroup.jsx +120 -0
  37. package/src/components/InputButtonGroup/index.js +11 -0
  38. package/src/components/LabelledInput/LabelledInput.jsx +51 -0
  39. package/src/components/LabelledInput/index.js +11 -0
  40. package/src/components/Loading/Loading.jsx +32 -0
  41. package/src/components/Loading/index.js +11 -0
  42. package/src/components/NoResults/NoResults.jsx +55 -0
  43. package/src/components/NoResults/index.js +11 -0
  44. package/src/components/Pagination/Pagination.jsx +105 -0
  45. package/src/components/Pagination/index.js +10 -0
  46. package/src/components/PerPagePicker/PerPagePicker.jsx +114 -0
  47. package/src/components/PerPagePicker/index.js +11 -0
  48. package/src/components/Pill/Pill.jsx +34 -0
  49. package/src/components/Pill/index.js +11 -0
  50. package/src/components/Pill/mock.js +23 -0
  51. package/src/components/ProductCardShimmer/ProductCardShimmer.css +72 -0
  52. package/src/components/ProductCardShimmer/ProductCardShimmer.jsx +28 -0
  53. package/src/components/ProductCardShimmer/index.js +11 -0
  54. package/src/components/ProductItem/MockData.js +508 -0
  55. package/src/components/ProductItem/ProductItem.css +84 -0
  56. package/src/components/ProductItem/ProductItem.jsx +347 -0
  57. package/src/components/ProductItem/ProductPrice.jsx +181 -0
  58. package/src/components/ProductItem/index.js +11 -0
  59. package/src/components/ProductList/MockData.js +190 -0
  60. package/src/components/ProductList/ProductList.jsx +127 -0
  61. package/src/components/ProductList/index.js +11 -0
  62. package/src/components/ProductList/product-list.css +18 -0
  63. package/src/components/SearchBar/SearchBar.jsx +33 -0
  64. package/src/components/SearchBar/index.js +11 -0
  65. package/src/components/Shimmer/Shimmer.css +82 -0
  66. package/src/components/Shimmer/Shimmer.jsx +66 -0
  67. package/src/components/Shimmer/index.js +11 -0
  68. package/src/components/Slider/Slider.css +61 -0
  69. package/src/components/Slider/Slider.jsx +103 -0
  70. package/src/components/Slider/index.jsx +11 -0
  71. package/src/components/SliderDoubleControl/SliderDoubleControl.css +83 -0
  72. package/src/components/SliderDoubleControl/SliderDoubleControl.jsx +220 -0
  73. package/src/components/SliderDoubleControl/index.js +11 -0
  74. package/src/components/SortDropdown/SortDropdown.jsx +126 -0
  75. package/src/components/SortDropdown/index.js +11 -0
  76. package/src/components/SwatchButton/SwatchButton.jsx +72 -0
  77. package/src/components/SwatchButton/index.js +11 -0
  78. package/src/components/SwatchButtonGroup/SwatchButtonGroup.jsx +86 -0
  79. package/src/components/SwatchButtonGroup/index.js +11 -0
  80. package/src/components/ViewSwitcher/ViewSwitcher.jsx +46 -0
  81. package/src/components/ViewSwitcher/index.js +11 -0
  82. package/src/components/WishlistButton/WishlistButton.jsx +67 -0
  83. package/src/components/WishlistButton/index.js +11 -0
  84. package/src/containers/App.jsx +145 -0
  85. package/src/containers/LiveSearchPLPLoader.jsx +24 -0
  86. package/src/containers/LiveSearchPopoverLoader.jsx +190 -0
  87. package/src/containers/LiveSearchSRLPLoader.jsx +24 -0
  88. package/src/containers/ProductListingPage.jsx +66 -0
  89. package/src/containers/ProductsContainer.jsx +145 -0
  90. package/src/containers/ProductsHeader.jsx +123 -0
  91. package/src/context/attributeMetadata.js +63 -0
  92. package/src/context/cart.js +97 -0
  93. package/src/context/displayChange.js +90 -0
  94. package/src/context/events.js +160 -0
  95. package/src/context/index.js +19 -0
  96. package/src/context/products.jsx +336 -0
  97. package/src/context/resultsModifierContext.js +35 -0
  98. package/src/context/search.jsx +127 -0
  99. package/src/context/store.jsx +93 -0
  100. package/src/context/translation.jsx +125 -0
  101. package/src/context/widgetConfig.jsx +120 -0
  102. package/src/context/wishlist.jsx +97 -0
  103. package/src/hooks/eventing/useEventListener.js +13 -0
  104. package/src/hooks/eventing/useLocation.js +21 -0
  105. package/src/hooks/eventing/useMagentoExtensionContext.js +28 -0
  106. package/src/hooks/eventing/usePageView.js +36 -0
  107. package/src/hooks/eventing/useShopperContext.js +33 -0
  108. package/src/hooks/eventing/useStorefrontInstanceContext.js +46 -0
  109. package/src/hooks/eventing/useViewedOffsets.js +74 -0
  110. package/src/hooks/useAccessibleDropdown.js +148 -0
  111. package/src/hooks/useLiveSearchPLPConfig.js +112 -0
  112. package/src/hooks/useLiveSearchPopoverConfig.js +83 -0
  113. package/src/hooks/useLiveSearchSRLPConfig.js +97 -0
  114. package/src/hooks/usePagination.js +83 -0
  115. package/src/hooks/useRangeFacet.js +62 -0
  116. package/src/hooks/useScalarFacet.js +61 -0
  117. package/src/hooks/useSliderFacet.js +43 -0
  118. package/src/i18n/Sorani.js +60 -0
  119. package/src/i18n/ar_AE.js +60 -0
  120. package/src/i18n/bg_BG.js +60 -0
  121. package/src/i18n/bn_IN.js +60 -0
  122. package/src/i18n/ca_ES.js +60 -0
  123. package/src/i18n/cs_CZ.js +60 -0
  124. package/src/i18n/da_DK.js +60 -0
  125. package/src/i18n/de_DE.js +60 -0
  126. package/src/i18n/el_GR.js +60 -0
  127. package/src/i18n/en_GA.js +60 -0
  128. package/src/i18n/en_GB.js +60 -0
  129. package/src/i18n/en_US.js +70 -0
  130. package/src/i18n/es_ES.js +60 -0
  131. package/src/i18n/et_EE.js +60 -0
  132. package/src/i18n/eu_ES.js +60 -0
  133. package/src/i18n/fa_IR.js +60 -0
  134. package/src/i18n/fi_FI.js +60 -0
  135. package/src/i18n/fr_FR.js +60 -0
  136. package/src/i18n/gl_ES.js +60 -0
  137. package/src/i18n/hi_IN.js +60 -0
  138. package/src/i18n/hu_HU.js +60 -0
  139. package/src/i18n/hy_AM.js +60 -0
  140. package/src/i18n/id_ID.js +60 -0
  141. package/src/i18n/index.js +89 -0
  142. package/src/i18n/it_IT.js +60 -0
  143. package/src/i18n/ja_JP.js +60 -0
  144. package/src/i18n/ko_KR.js +60 -0
  145. package/src/i18n/lt_LT.js +60 -0
  146. package/src/i18n/lv_LV.js +60 -0
  147. package/src/i18n/nb_NO.js +60 -0
  148. package/src/i18n/nl_NL.js +60 -0
  149. package/src/i18n/pt_BR.js +60 -0
  150. package/src/i18n/pt_PT.js +60 -0
  151. package/src/i18n/ro_RO.js +60 -0
  152. package/src/i18n/ru_RU.js +60 -0
  153. package/src/i18n/sv_SE.js +60 -0
  154. package/src/i18n/th_TH.js +60 -0
  155. package/src/i18n/tr_TR.js +60 -0
  156. package/src/i18n/zh_Hans_CN.js +60 -0
  157. package/src/i18n/zh_Hant_TW.js +60 -0
  158. package/src/icons/NoImage.svg +1 -0
  159. package/src/icons/adjustments.svg +3 -0
  160. package/src/icons/cart.svg +3 -0
  161. package/src/icons/checkmark.svg +3 -0
  162. package/src/icons/chevron.svg +3 -0
  163. package/src/icons/emptyHeart.svg +3 -0
  164. package/src/icons/error.svg +3 -0
  165. package/src/icons/filledHeart.svg +3 -0
  166. package/src/icons/filter.svg +29 -0
  167. package/src/icons/gridView.svg +11 -0
  168. package/src/icons/info.svg +3 -0
  169. package/src/icons/listView.svg +5 -0
  170. package/src/icons/loading.svg +6 -0
  171. package/src/icons/plus.svg +4 -0
  172. package/src/icons/sort.svg +18 -0
  173. package/src/icons/warning.svg +3 -0
  174. package/src/icons/x.svg +3 -0
  175. package/src/index.jsx +65 -0
  176. package/src/queries/customerGroupCode.gql.js +10 -0
  177. package/src/queries/eventing/getMagentoExtensionContext.gql.js +13 -0
  178. package/src/queries/eventing/getPageType.gql.js +14 -0
  179. package/src/queries/eventing/getStorefrontContext.gql.js +27 -0
  180. package/src/queries/index.js +3 -0
  181. package/src/queries/liveSearchPlpConfigs.gql.js +30 -0
  182. package/src/queries/liveSearchPopoverConfigs.gql.js +28 -0
  183. package/src/styles/autocomplete.module.css +56 -0
  184. package/src/styles/index.css +1638 -0
  185. package/src/styles/searchBar.module.css +119 -0
  186. package/src/styles/tokens.css +99 -0
  187. package/src/targets/intercept.js +21 -0
  188. package/src/utils/constants.js +26 -0
  189. package/src/utils/decodeHtmlString.js +13 -0
  190. package/src/utils/dom.js +14 -0
  191. package/src/utils/eventing/getCookie.js +9 -0
  192. package/src/utils/eventing/usePageTypeFromUrl.js +26 -0
  193. package/src/utils/getProductImage.js +94 -0
  194. package/src/utils/getProductPrice.js +83 -0
  195. package/src/utils/getUserViewHistory.js +27 -0
  196. package/src/utils/handleUrlFilters.js +164 -0
  197. package/src/utils/htmlStringDecode.js +13 -0
  198. package/src/utils/modifyResults.js +164 -0
  199. package/src/utils/sort.js +95 -0
  200. package/src/utils/useIntersectionObserver.js +27 -0
  201. package/src/utils/validateStoreDetails.js +39 -0
  202. package/src/wrappers/wrapUseApp.js +28 -0
@@ -0,0 +1,84 @@
1
+ .grid-container {
2
+ display: grid;
3
+ gap: 1px;
4
+ height: auto;
5
+ grid-template-columns: auto 1fr 1fr;
6
+ border-top: 2px solid #e5e7eb;
7
+ padding: 10px;
8
+ grid-template-areas:
9
+ 'product-image product-details product-price'
10
+ 'product-image product-description product-description'
11
+ 'product-image product-ratings product-add-to-cart';
12
+ }
13
+
14
+ .product-image {
15
+ grid-area: product-image;
16
+ width: fit-content;
17
+ }
18
+
19
+ .product-details {
20
+ white-space: nowrap;
21
+ grid-area: product-details;
22
+ }
23
+
24
+ .product-price {
25
+ grid-area: product-price;
26
+ display: grid;
27
+ width: 100%;
28
+ height: 100%;
29
+ justify-content: end;
30
+ }
31
+
32
+ .product-description {
33
+ grid-area: product-description;
34
+ }
35
+
36
+ .product-description:hover {
37
+ text-decoration: underline;
38
+ }
39
+
40
+ .product-ratings {
41
+ grid-area: product-ratings;
42
+ }
43
+
44
+ .product-add-to-cart {
45
+ grid-area: product-add-to-cart;
46
+ display: grid;
47
+ justify-content: end;
48
+ }
49
+
50
+ @media screen and (max-width: 767px) {
51
+ .grid-container {
52
+ display: grid;
53
+ gap: 10px;
54
+ height: auto;
55
+ border-top: 2px solid #e5e7eb;
56
+ padding: 10px;
57
+ grid-template-areas:
58
+ 'product-image product-image product-image'
59
+ 'product-details product-details product-details'
60
+ 'product-price product-price product-price'
61
+ 'product-description product-description product-description'
62
+ 'product-ratings product-ratings product-ratings'
63
+ 'product-add-to-cart product-add-to-cart product-add-to-cart';
64
+ }
65
+
66
+ .product-image {
67
+ display: flex;
68
+ justify-content: center;
69
+ align-items: center;
70
+ width: auto;
71
+ }
72
+
73
+ .product-price {
74
+ justify-content: start;
75
+ }
76
+
77
+ .product-add-to-cart {
78
+ justify-content: center;
79
+ }
80
+
81
+ .product-details {
82
+ justify-content: center;
83
+ }
84
+ }
@@ -0,0 +1,347 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ import React, { useState } from 'react';
11
+ import '../ProductItem/ProductItem.css';
12
+
13
+ import { useCart, useProducts, useSensor, useStore } from '../../context';
14
+ import NoImage from '../../icons/NoImage.svg';
15
+ import {
16
+ generateOptimizedImages,
17
+ getProductImageURLs,
18
+ } from '../../utils/getProductImage';
19
+ import { htmlStringDecode } from '../../utils/htmlStringDecode';
20
+ import AddToCartButton from '../AddToCartButton';
21
+ import { ImageCarousel } from '../ImageCarousel';
22
+ import { SwatchButtonGroup } from '../SwatchButtonGroup';
23
+ import ProductPrice from './ProductPrice';
24
+ import { SEARCH_UNIT_ID } from '../../utils/constants';
25
+ import { sanitizeRefinedImages } from '../../utils/modifyResults';
26
+ import { useResultsModifier } from '../../context/resultsModifierContext';
27
+
28
+ const ProductItem = ({
29
+ item,
30
+ currencySymbol,
31
+ currencyRate,
32
+ setRoute,
33
+ refineProduct,
34
+ setCartUpdated,
35
+ setItemAdded,
36
+ setError,
37
+ addToCart,
38
+ }) => {
39
+ const { product, productView } = item;
40
+ const [carouselIndex, setCarouselIndex] = useState(0);
41
+ const [selectedSwatch, setSelectedSwatch] = useState('');
42
+ const [imagesFromRefinedProduct, setImagesFromRefinedProduct] = useState(null);
43
+ const [refinedProduct, setRefinedProduct] = useState();
44
+ const [isHovering, setIsHovering] = useState(false);
45
+ const { addToCartGraphQL, refreshCart } = useCart();
46
+ const { viewType } = useProducts();
47
+ const {
48
+ config: { optimizeImages, imageBaseWidth, imageCarousel, listview },
49
+ } = useStore();
50
+
51
+ const { screenSize } = useSensor();
52
+
53
+ const handleMouseOver = () => {
54
+ setIsHovering(true);
55
+ };
56
+
57
+ const handleMouseOut = () => {
58
+ setIsHovering(false);
59
+ };
60
+
61
+ const { baseUrl, baseUrlWithoutProtocol } = useResultsModifier();
62
+
63
+ const handleSelection = async (optionIds, sku) => {
64
+ const data = await refineProduct(optionIds, sku);
65
+ setSelectedSwatch(optionIds[0]);
66
+ setImagesFromRefinedProduct(sanitizeRefinedImages(data.refineProduct.images, baseUrl, baseUrlWithoutProtocol, true));
67
+ setRefinedProduct(data);
68
+ setCarouselIndex(0);
69
+ };
70
+
71
+ const isSelected = (id) => {
72
+ const selected = selectedSwatch ? selectedSwatch === id : false;
73
+ return selected;
74
+ };
75
+
76
+ const productImageArray = imagesFromRefinedProduct
77
+ ? getProductImageURLs(imagesFromRefinedProduct.length ? imagesFromRefinedProduct : [], imageCarousel ? 3 : 1)
78
+ : getProductImageURLs(
79
+ productView.images && productView.images.length ? productView.images : [],
80
+ imageCarousel ? 3 : 1, // number of images to display in carousel
81
+ product.image && product.image.url ? product.image.url : undefined
82
+ );
83
+
84
+
85
+ let optimizedImageArray = [];
86
+
87
+ if (optimizeImages) {
88
+ optimizedImageArray = generateOptimizedImages(
89
+ productImageArray,
90
+ imageBaseWidth !== undefined && imageBaseWidth !== null ? imageBaseWidth : 200
91
+ );
92
+ }
93
+
94
+ const discount = refinedProduct
95
+ ? refinedProduct.refineProduct?.priceRange?.minimum?.regular?.amount
96
+ ?.value >
97
+ refinedProduct.refineProduct?.priceRange?.minimum?.final?.amount?.value
98
+ : product?.price_range?.minimum_price?.regular_price?.value >
99
+ product?.price_range?.minimum_price?.final_price?.value ||
100
+ productView?.price?.regular?.amount?.value >
101
+ productView?.price?.final?.amount?.value;
102
+
103
+ const isSimple = product?.__typename === 'SimpleProduct';
104
+ const isComplexProductView = productView?.__typename === 'ComplexProductView';
105
+ const isBundle = product?.__typename === 'BundleProduct';
106
+ const isGrouped = product?.__typename === 'GroupedProduct';
107
+ const isGiftCard = product?.__typename === 'GiftCardProduct';
108
+ const isConfigurable = product?.__typename === 'ConfigurableProduct';
109
+
110
+ const onProductClick = () => {
111
+ window.magentoStorefrontEvents?.publish.searchProductClick(
112
+ SEARCH_UNIT_ID,
113
+ product?.sku
114
+ );
115
+ };
116
+
117
+ const productUrl = setRoute
118
+ ? setRoute({ sku: productView?.sku, urlKey: productView?.urlKey })
119
+ : product?.canonical_url;
120
+
121
+ const handleAddToCart = async () => {
122
+ setError(false);
123
+ if (isSimple) {
124
+ if (addToCart) {
125
+ await addToCart(productView.sku, [], 1);
126
+ } else {
127
+ const response = await addToCartGraphQL(productView.sku);
128
+
129
+ if (
130
+ response?.errors ||
131
+ response?.data?.addProductsToCart?.user_errors.length > 0
132
+ ) {
133
+ setError(true);
134
+ return;
135
+ }
136
+
137
+ setItemAdded(product.name);
138
+ refreshCart && refreshCart();
139
+ setCartUpdated(true);
140
+ }
141
+ } else if (productUrl) {
142
+ window.open(productUrl, '_self');
143
+ }
144
+ };
145
+
146
+ if (listview && viewType === 'listview') {
147
+ return (
148
+ <>
149
+ <div className="grid-container">
150
+ <div
151
+ className={`product-image ds-sdk-product-item__image relative rounded-md overflow-hidden}`}
152
+ >
153
+ <a
154
+ href={productUrl}
155
+ onClick={onProductClick}
156
+ className="!text-primary hover:no-underline hover:text-primary"
157
+ >
158
+ {/* Image */}
159
+ {productImageArray.length ? (
160
+ <ImageCarousel
161
+ images={
162
+ optimizedImageArray.length
163
+ ? optimizedImageArray
164
+ : productImageArray
165
+ }
166
+ productName={product.name}
167
+ carouselIndex={carouselIndex}
168
+ setCarouselIndex={setCarouselIndex}
169
+ />
170
+ ) : (
171
+ <img src={NoImage} className={`max-h-[250px] max-w-[200px] pr-5 m-auto object-cover object-center lg:w-full`} />
172
+ // <NoImage
173
+ // className={`max-h-[250px] max-w-[200px] pr-5 m-auto object-cover object-center lg:w-full`}
174
+ // />
175
+ )}
176
+ </a>
177
+ </div>
178
+ <div className="product-details">
179
+ <div className="flex flex-col w-1/3">
180
+ {/* Product name */}
181
+ <a
182
+ href={productUrl}
183
+ onClick={onProductClick}
184
+ className="!text-primary hover:no-underline hover:text-primary"
185
+ >
186
+ <div className="ds-sdk-product-item__product-name mt-xs text-sm text-primary">
187
+ {product.name !== null && htmlStringDecode(product.name)}
188
+ </div>
189
+ <div className="ds-sdk-product-item__product-sku mt-xs text-sm text-primary">
190
+ SKU:
191
+ {product.sku !== null && htmlStringDecode(product.sku)}
192
+ </div>
193
+ </a>
194
+
195
+ {/* Swatch */}
196
+ {!screenSize.mobile &&
197
+ productView?.options &&
198
+ productView.options?.length > 0 && (
199
+ <div className="ds-sdk-product-item__product-swatch flex flex-row mt-sm text-sm text-primary pb-6">
200
+ {productView?.options?.map(
201
+ (swatches) =>
202
+ swatches.id === 'color' && (
203
+ <SwatchButtonGroup
204
+ key={`${productView?.sku}-${option.id}`}
205
+ isSelected={isSelected}
206
+ swatches={swatches.values && swatches.values.length ? swatches.values : []}
207
+ showMore={onProductClick}
208
+ productUrl={productUrl}
209
+ onClick={handleSelection}
210
+ sku={productView?.sku}
211
+ />
212
+ )
213
+ )}
214
+ </div>
215
+ )}
216
+
217
+ </div>
218
+ </div>
219
+ <div className="product-price">
220
+ <a
221
+ href={productUrl}
222
+ onClick={onProductClick}
223
+ className="!text-primary hover:no-underline hover:text-primary"
224
+ >
225
+ <ProductPrice
226
+ item={refinedProduct !== undefined && refinedProduct !== null ? refinedProduct : item}
227
+ isBundle={isBundle}
228
+ isGrouped={isGrouped}
229
+ isGiftCard={isGiftCard}
230
+ isConfigurable={isConfigurable}
231
+ isComplexProductView={isComplexProductView}
232
+ discount={discount}
233
+ currencySymbol={currencySymbol}
234
+ currencyRate={currencyRate}
235
+ />
236
+ </a>
237
+ </div>
238
+ <div className="product-description text-sm text-primary mt-xs">
239
+ <a
240
+ href={productUrl}
241
+ onClick={onProductClick}
242
+ className="!text-primary hover:no-underline hover:text-primary"
243
+ >
244
+ {product.short_description?.html ? (
245
+ <>
246
+ <span
247
+ dangerouslySetInnerHTML={{
248
+ __html: product.short_description.html,
249
+ }}
250
+ />
251
+ </>
252
+ ) : (
253
+ <span />
254
+ )}
255
+ </a>
256
+ </div>
257
+
258
+ <div className="product-ratings" />
259
+ <div className="product-add-to-cart">
260
+ <div className="pb-4 h-[38px] w-96">
261
+ <AddToCartButton onClick={handleAddToCart} />
262
+ </div>
263
+ </div>
264
+ </div>
265
+ </>
266
+ );
267
+ }
268
+
269
+ return (
270
+ <div
271
+ className="ds-sdk-product-item group relative flex flex-col max-w-sm justify-between h-full hover:border-[1.5px] border-solid hover:shadow-lg border-offset-2 p-5"
272
+ style={{
273
+ 'borderColor': '#D5D5D5',
274
+ }}
275
+ onMouseEnter={handleMouseOver}
276
+ onMouseLeave={handleMouseOut}
277
+ >
278
+ <a
279
+ href={productUrl}
280
+ onClick={onProductClick}
281
+ className="!text-primary hover:no-underline hover:text-primary"
282
+ >
283
+ <div className="ds-sdk-product-item__main relative flex flex-col justify-between h-full">
284
+ <div className="ds-sdk-product-item__image relative w-full h-full rounded-md overflow-hidden">
285
+ {productImageArray.length ? (
286
+ <ImageCarousel
287
+ images={
288
+ optimizedImageArray.length
289
+ ? optimizedImageArray
290
+ : productImageArray
291
+ }
292
+ productName={product.name}
293
+ carouselIndex={carouselIndex}
294
+ setCarouselIndex={setCarouselIndex}
295
+ />
296
+ ) : (
297
+ <img src={NoImage} className={`max-h-[45rem] w-full object-cover object-center lg:w-full`} />
298
+ // <NoImage
299
+ // className={`max-h-[45rem] w-full object-cover object-center lg:w-full`}
300
+ // />
301
+ )}
302
+ </div>
303
+ <div className="flex flex-row">
304
+ <div className="flex flex-col">
305
+ <div className="ds-sdk-product-item__product-name mt-md text-sm text-primary">
306
+ {product.name !== null && htmlStringDecode(product.name)}
307
+ </div>
308
+ <ProductPrice
309
+ item={refinedProduct !== undefined && refinedProduct !== null ? refinedProduct : item}
310
+ isBundle={isBundle}
311
+ isGrouped={isGrouped}
312
+ isGiftCard={isGiftCard}
313
+ isConfigurable={isConfigurable}
314
+ isComplexProductView={isComplexProductView}
315
+ discount={discount}
316
+ currencySymbol={currencySymbol}
317
+ currencyRate={currencyRate}
318
+ />
319
+ </div>
320
+ </div>
321
+ </div>
322
+ </a>
323
+
324
+ {!screenSize.mobile && productView?.options && productView.options?.length > 0 && (
325
+ <div className="ds-sdk-product-item__product-swatch mt-sm text-sm text-primary pb-6">
326
+ {productView?.options.map((option) => (
327
+ <SwatchButtonGroup
328
+ key={`${productView?.sku}-${option.id}`}
329
+ isSelected={isSelected}
330
+ swatches={option.values && option.values.length ? option.values : []}
331
+ showMore={onProductClick}
332
+ productUrl={productUrl}
333
+ onClick={handleSelection}
334
+ sku={productView?.sku}
335
+ />
336
+ ))}
337
+ </div>
338
+ )}
339
+
340
+ <div className="ds-sdk-product-item__add-to-cart py-2">
341
+ <AddToCartButton onClick={handleAddToCart} />
342
+ </div>
343
+ </div>
344
+ );
345
+ };
346
+
347
+ export default ProductItem;
@@ -0,0 +1,181 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ import React, { useContext } from 'react';
11
+ import { TranslationContext } from '../../context/translation';
12
+ import { getProductPrice } from '../../utils/getProductPrice';
13
+
14
+ const ProductPrice = ({
15
+ isComplexProductView,
16
+ item,
17
+ isBundle,
18
+ isGrouped,
19
+ isGiftCard,
20
+ isConfigurable,
21
+ discount,
22
+ currencySymbol,
23
+ currencyRate
24
+ }) => {
25
+ const translation = useContext(TranslationContext);
26
+ let price;
27
+
28
+ if ('product' in item) {
29
+ price =
30
+ item && item.product && item.product.price_range && item.product.price_range.minimum_price && item.product.price_range.minimum_price.final_price
31
+ ? item.product.price_range.minimum_price.final_price : item && item.product && item.product.price_range && item.product.price_range.minimum_price && item.product.price_range.minimum_price.regular_price
32
+ ? item.product.price_range.minimum_price.regular_price : undefined;
33
+ } else {
34
+ price =
35
+ item && item.refineProduct && item.refineProduct.priceRange && item.refineProduct.priceRange.minimum && item.refineProduct.priceRange.minimum.final
36
+ ? item.refineProduct.priceRange.minimum.final
37
+ : item && item.refineProduct && item.refineProduct.price && item.refineProduct.price.final
38
+ ? item.refineProduct.price.final : undefined;
39
+ }
40
+
41
+ const getBundledPrice = (item, currencySymbol, currencyRate) => {
42
+ const bundlePriceTranslationOrder =
43
+ translation.ProductCard.bundlePrice.split(' ');
44
+ return bundlePriceTranslationOrder.map((word, index) =>
45
+ word === '{fromBundlePrice}' ? (
46
+ `${getProductPrice(item, currencySymbol, currencyRate, false, true)} `
47
+ ) : word === '{toBundlePrice}' ? (
48
+ getProductPrice(item, currencySymbol, currencyRate, true, true)
49
+ ) : (
50
+ <span className="text-gray-500 text-xs font-normal mr-xs" key={index}>
51
+ {word}
52
+ </span>
53
+ )
54
+ );
55
+ };
56
+
57
+ const getPriceFormat = (item, currencySymbol, currencyRate, isGiftCard) => {
58
+ const priceTranslation = isGiftCard
59
+ ? translation.ProductCard.from
60
+ : translation.ProductCard.startingAt;
61
+ const startingAtTranslationOrder = priceTranslation.split('{productPrice}');
62
+ return startingAtTranslationOrder.map((word, index) =>
63
+ word === '' ? (
64
+ getProductPrice(item, currencySymbol, currencyRate, false, true)
65
+ ) : (
66
+ <span className="text-gray-500 text-xs font-normal mr-xs" key={index}>
67
+ {word}
68
+ </span>
69
+ )
70
+ );
71
+ };
72
+
73
+ const getDiscountedPrice = (discount) => {
74
+ const discountPrice = discount ? (
75
+ <>
76
+ <span className="line-through pr-2">
77
+ {getProductPrice(item, currencySymbol, currencyRate, false, false)}
78
+ </span>
79
+ <span className="text-secondary">
80
+ {getProductPrice(item, currencySymbol, currencyRate, false, true)}
81
+ </span>
82
+ </>
83
+ ) : (
84
+ getProductPrice(item, currencySymbol, currencyRate, false, true)
85
+ );
86
+ const discountedPriceTranslation = translation.ProductCard.asLowAs;
87
+ const discountedPriceTranslationOrder =
88
+ discountedPriceTranslation.split('{discountPrice}');
89
+ return discountedPriceTranslationOrder.map((word, index) =>
90
+ word === '' ? (
91
+ discountPrice
92
+ ) : (
93
+ <span className="text-gray-500 text-xs font-normal mr-xs" key={index}>
94
+ {word}
95
+ </span>
96
+ )
97
+ );
98
+ };
99
+
100
+ return (
101
+ <>
102
+ {price && (
103
+ <div className="ds-sdk-product-price">
104
+ {!isBundle &&
105
+ !isGrouped &&
106
+ !isConfigurable &&
107
+ !isComplexProductView &&
108
+ discount && (
109
+ <p className="ds-sdk-product-price--discount mt-xs text-sm font-medium text-gray-900 my-auto">
110
+ <span className="line-through pr-2">
111
+ {getProductPrice(
112
+ item,
113
+ currencySymbol,
114
+ currencyRate,
115
+ false,
116
+ false
117
+ )}
118
+ </span>
119
+ <span className="text-secondary">
120
+ {getProductPrice(
121
+ item,
122
+ currencySymbol,
123
+ currencyRate,
124
+ false,
125
+ true
126
+ )}
127
+ </span>
128
+ </p>
129
+ )}
130
+
131
+ {!isBundle &&
132
+ !isGrouped &&
133
+ !isGiftCard &&
134
+ !isConfigurable &&
135
+ !isComplexProductView &&
136
+ !discount && (
137
+ <p className="ds-sdk-product-price--no-discount mt-xs text-sm font-medium text-gray-900 my-auto">
138
+ {getProductPrice(
139
+ item,
140
+ currencySymbol,
141
+ currencyRate,
142
+ false,
143
+ true
144
+ )}
145
+ </p>
146
+ )}
147
+
148
+ {isBundle && (
149
+ <div className="ds-sdk-product-price--bundle">
150
+ <p className="mt-xs text-sm font-medium text-gray-900 my-auto">
151
+ {getBundledPrice(item, currencySymbol, currencyRate)}
152
+ </p>
153
+ </div>
154
+ )}
155
+
156
+ {isGrouped && (
157
+ <p className="ds-sdk-product-price--grouped mt-xs text-sm font-medium text-gray-900 my-auto">
158
+ {getPriceFormat(item, currencySymbol, currencyRate, false)}
159
+ </p>
160
+ )}
161
+
162
+ {isGiftCard && (
163
+ <p className="ds-sdk-product-price--gift-card mt-xs text-sm font-medium text-gray-900 my-auto">
164
+ {getPriceFormat(item, currencySymbol, currencyRate, true)}
165
+ </p>
166
+ )}
167
+
168
+ {!isGrouped &&
169
+ !isBundle &&
170
+ (isConfigurable || isComplexProductView) && (
171
+ <p className="ds-sdk-product-price--configurable mt-xs text-sm font-medium text-gray-900 my-auto">
172
+ {getDiscountedPrice(discount)}
173
+ </p>
174
+ )}
175
+ </div>
176
+ )}
177
+ </>
178
+ );
179
+ };
180
+
181
+ export default ProductPrice;
@@ -0,0 +1,11 @@
1
+ /*
2
+ Copyright 2024 Adobe
3
+ All Rights Reserved.
4
+
5
+ NOTICE: Adobe permits you to use, modify, and distribute this file in
6
+ accordance with the terms of the Adobe license agreement accompanying
7
+ it.
8
+ */
9
+
10
+ //export * from './ProductItem';
11
+ export { default as ProductItem } from './ProductItem';