@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,336 @@
1
+ import React, { useContext, useEffect, useMemo, useState, useRef, createContext } from 'react';
2
+
3
+ import { getProductSearch, refineProductSearch } from '../api/search';
4
+ import {
5
+ CATEGORY_SORT_DEFAULT,
6
+ DEFAULT_MIN_QUERY_LENGTH,
7
+ DEFAULT_PAGE_SIZE,
8
+ DEFAULT_PAGE_SIZE_OPTIONS,
9
+ SEARCH_SORT_DEFAULT,
10
+ } from '../utils/constants';
11
+ import { moveToTop } from '../utils/dom';
12
+ import {
13
+ getFiltersFromUrl,
14
+ getValueFromUrl,
15
+ handleUrlPagination,
16
+ } from '../utils/handleUrlFilters';
17
+ import { useAttributeMetadata } from './attributeMetadata';
18
+ import { useSearch } from './search';
19
+ import { useStore } from './store';
20
+ import { useTranslation } from './translation';
21
+ import isEqual from 'lodash/isEqual';
22
+
23
+ const ProductsContext = createContext({
24
+ variables: { phrase: '' },
25
+ loading: false,
26
+ items: [],
27
+ setItems: () => {},
28
+ currentPage: 1,
29
+ setCurrentPage: () => {},
30
+ pageSize: DEFAULT_PAGE_SIZE,
31
+ setPageSize: () => {},
32
+ totalCount: 0,
33
+ setTotalCount: () => {},
34
+ totalPages: 0,
35
+ setTotalPages: () => {},
36
+ facets: [],
37
+ setFacets: () => {},
38
+ categoryName: '',
39
+ setCategoryName: () => {},
40
+ currencySymbol: '',
41
+ setCurrencySymbol: () => {},
42
+ currencyRate: '',
43
+ setCurrencyRate: () => {},
44
+ minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
45
+ minQueryLengthReached: false,
46
+ setMinQueryLengthReached: () => {},
47
+ pageSizeOptions: [],
48
+ setRoute: undefined,
49
+ refineProduct: () => {},
50
+ pageLoading: false,
51
+ setPageLoading: () => {},
52
+ categoryPath: undefined,
53
+ viewType: '',
54
+ setViewType: () => {},
55
+ listViewType: '',
56
+ setListViewType: () => {},
57
+ resolveCartId: () => Promise.resolve(''),
58
+ refreshCart: () => {},
59
+ addToCart: () => Promise.resolve(),
60
+ });
61
+
62
+ const ProductsContextProvider = ({ children }) => {
63
+ const urlValue = getValueFromUrl('p');
64
+ const pageDefault = urlValue ? Number(urlValue) : 1;
65
+
66
+ const searchCtx = useSearch();
67
+ const storeCtx = useStore();
68
+ const attributeMetadataCtx = useAttributeMetadata();
69
+
70
+ const pageSizeValue = getValueFromUrl('page_size');
71
+ const defaultPageSizeOption =
72
+ Number(storeCtx?.config?.perPageConfig?.defaultPageSizeOption) ||
73
+ DEFAULT_PAGE_SIZE;
74
+ const pageSizeDefault = pageSizeValue
75
+ ? Number(pageSizeValue)
76
+ : defaultPageSizeOption;
77
+
78
+ const translation = useTranslation();
79
+ const showAllLabel = translation.ProductContainers.showAll;
80
+
81
+ const [loading, setLoading] = useState(true);
82
+ const [pageLoading, setPageLoading] = useState(true);
83
+ const [items, setItems] = useState([]);
84
+ const [currentPage, setCurrentPage] = useState(pageDefault);
85
+ const [pageSize, setPageSize] = useState(pageSizeDefault);
86
+ const [totalCount, setTotalCount] = useState(0);
87
+ const [totalPages, setTotalPages] = useState(0);
88
+ const [facets, setFacets] = useState([]);
89
+ const [categoryName, setCategoryName] = useState(
90
+ storeCtx && storeCtx.config && storeCtx.config.categoryName
91
+ ? storeCtx.config.categoryName
92
+ : ''
93
+ );
94
+
95
+ const [pageSizeOptions, setPageSizeOptions] = useState([]);
96
+
97
+ const [currencySymbol, setCurrencySymbol] = useState(
98
+ storeCtx && storeCtx.config && storeCtx.config.currencySymbol
99
+ ? storeCtx.config.currencySymbol
100
+ : ''
101
+ );
102
+
103
+ const [currencyRate, setCurrencyRate] = useState(
104
+ storeCtx && storeCtx.config && storeCtx.config.currencyRate
105
+ ? storeCtx.config.currencyRate
106
+ : ''
107
+ );
108
+
109
+ const [minQueryLengthReached, setMinQueryLengthReached] = useState(false);
110
+
111
+ const minQueryLength = useMemo(() => {
112
+ return storeCtx?.config?.minQueryLength || DEFAULT_MIN_QUERY_LENGTH;
113
+ }, [storeCtx?.config?.minQueryLength]);
114
+
115
+ const categoryPath = storeCtx.config?.currentCategoryUrlPath;
116
+
117
+ const viewTypeFromUrl = getValueFromUrl('view_type');
118
+ const [viewType, setViewType] = useState(
119
+ viewTypeFromUrl ? viewTypeFromUrl : 'gridView'
120
+ );
121
+ const [listViewType, setListViewType] = useState('default');
122
+
123
+ const variables = useMemo(() => {
124
+ const isCategoryPage = Boolean(storeCtx.config?.currentCategoryUrlPath);
125
+ const baseSort = searchCtx.sort?.length
126
+ ? searchCtx.sort
127
+ : isCategoryPage
128
+ ? CATEGORY_SORT_DEFAULT
129
+ : SEARCH_SORT_DEFAULT;
130
+
131
+ return {
132
+ phrase: searchCtx.phrase,
133
+ filter: searchCtx.filters,
134
+ sort: baseSort,
135
+ context: storeCtx.context,
136
+ pageSize,
137
+ displayOutOfStock: storeCtx.config.displayOutOfStock,
138
+ currentPage,
139
+ categoryPath
140
+ };
141
+ }, [
142
+ searchCtx.phrase,
143
+ searchCtx.filters,
144
+ searchCtx.sort,
145
+ storeCtx.context,
146
+ storeCtx.config.displayOutOfStock,
147
+ categoryPath,
148
+ pageSize,
149
+ currentPage,
150
+ ]);
151
+
152
+ const handleRefineProductSearch = async (optionIds, sku) => {
153
+ const data = await refineProductSearch({ ...storeCtx, optionIds, sku });
154
+ return data;
155
+ };
156
+
157
+ const context = {
158
+ variables,
159
+ loading,
160
+ items,
161
+ setItems,
162
+ currentPage,
163
+ setCurrentPage,
164
+ pageSize,
165
+ setPageSize,
166
+ totalCount,
167
+ setTotalCount,
168
+ totalPages,
169
+ setTotalPages,
170
+ facets,
171
+ setFacets,
172
+ categoryName,
173
+ setCategoryName,
174
+ currencySymbol,
175
+ setCurrencySymbol,
176
+ currencyRate,
177
+ setCurrencyRate,
178
+ minQueryLength,
179
+ minQueryLengthReached,
180
+ setMinQueryLengthReached,
181
+ pageSizeOptions,
182
+ setRoute: storeCtx.route,
183
+ refineProduct: handleRefineProductSearch,
184
+ pageLoading,
185
+ setPageLoading,
186
+ categoryPath,
187
+ viewType,
188
+ setViewType,
189
+ listViewType,
190
+ setListViewType,
191
+ cartId: storeCtx.config.resolveCartId,
192
+ refreshCart: storeCtx.config.refreshCart,
193
+ resolveCartId: storeCtx.config.resolveCartId,
194
+ addToCart: storeCtx.config.addToCart,
195
+ };
196
+
197
+ const [hasInitialized, setHasInitialized] = useState(false);
198
+ const prevVariablesRef = useRef();
199
+
200
+ useEffect(() => {
201
+ searchProducts();
202
+ setHasInitialized(true);
203
+ prevVariablesRef.current = variables;
204
+ }, []);
205
+
206
+ useEffect(() => {
207
+ if (!hasInitialized) return;
208
+
209
+ const hasChanged = !isEqual(prevVariablesRef.current, variables);
210
+
211
+ if (hasChanged) {
212
+ prevVariablesRef.current = variables;
213
+ searchProducts();
214
+ }
215
+ }, [variables]);
216
+
217
+ const searchProducts = async () => {
218
+ try {
219
+ setLoading(true);
220
+ moveToTop();
221
+ if (checkMinQueryLength()) {
222
+ const filters = [...variables.filter];
223
+
224
+ handleCategorySearch(categoryPath, filters);
225
+
226
+ const data = await getProductSearch({
227
+ ...variables,
228
+ ...storeCtx,
229
+ apiUrl: storeCtx.apiUrl,
230
+ filter: filters,
231
+ categorySearch: !!categoryPath,
232
+ });
233
+
234
+ setItems(data?.productSearch?.items || []);
235
+ setFacets(data?.productSearch?.facets || []);
236
+ setTotalCount(data?.productSearch?.total_count || 0);
237
+ setTotalPages(data?.productSearch?.page_info?.total_pages || 1);
238
+ handleCategoryNames(data?.productSearch?.facets || []);
239
+
240
+ getPageSizeOptions(data?.productSearch?.total_count);
241
+
242
+ paginationCheck(
243
+ data?.productSearch?.total_count,
244
+ data?.productSearch?.page_info?.total_pages
245
+ );
246
+ }
247
+ setLoading(false);
248
+ setPageLoading(false);
249
+ } catch (error) {
250
+ setLoading(false);
251
+ setPageLoading(false);
252
+ }
253
+ };
254
+
255
+ const checkMinQueryLength = () => {
256
+ if (
257
+ !storeCtx.config?.currentCategoryUrlPath &&
258
+ searchCtx.phrase.trim().length <
259
+ (Number(storeCtx.config.minQueryLength) || DEFAULT_MIN_QUERY_LENGTH)
260
+ ) {
261
+ setItems([]);
262
+ setFacets([]);
263
+ setTotalCount(0);
264
+ setTotalPages(1);
265
+ setMinQueryLengthReached(false);
266
+ return false;
267
+ }
268
+ setMinQueryLengthReached(true);
269
+ return true;
270
+ };
271
+
272
+ const getPageSizeOptions = (totalCount) => {
273
+ const optionsArray = [];
274
+ const pageSizeString =
275
+ storeCtx?.config?.perPageConfig?.pageSizeOptions ||
276
+ DEFAULT_PAGE_SIZE_OPTIONS;
277
+ const pageSizeArray = pageSizeString.split(',');
278
+ pageSizeArray.forEach((option) => {
279
+ optionsArray.push({
280
+ label: option,
281
+ value: parseInt(option, 10),
282
+ });
283
+ });
284
+
285
+ if (storeCtx?.config?.allowAllProducts == '1') {
286
+ optionsArray.push({
287
+ label: showAllLabel,
288
+ value: totalCount !== null ? (totalCount > 500 ? 500 : totalCount) : 0,
289
+ });
290
+ }
291
+ setPageSizeOptions(optionsArray);
292
+ };
293
+
294
+ const paginationCheck = (totalCount, totalPages) => {
295
+ if (totalCount && totalCount > 0 && totalPages === 1) {
296
+ setCurrentPage(1);
297
+ handleUrlPagination(1);
298
+ }
299
+ };
300
+
301
+ const handleCategorySearch = (categoryPath, filters) => {
302
+ if (categoryPath) {
303
+ const categoryFilter = {
304
+ attribute: 'categoryPath',
305
+ eq: categoryPath,
306
+ };
307
+ filters.push(categoryFilter);
308
+ }
309
+ };
310
+
311
+ const handleCategoryNames = (facets) => {
312
+ facets.map((facet) => {
313
+ const bucketType = facet?.buckets[0]?.__typename;
314
+ if (bucketType === 'CategoryView') {
315
+ const names = facet.buckets.map((bucket) => {
316
+ if (bucket.__typename === 'CategoryView')
317
+ return {
318
+ name: bucket.name,
319
+ id: bucket.id,
320
+ };
321
+ });
322
+ setCategoryName(names?.[0]?.name);
323
+ }
324
+ });
325
+ };
326
+
327
+ return (
328
+ <ProductsContext.Provider value={context}>
329
+ {children}
330
+ </ProductsContext.Provider>
331
+ );
332
+ };
333
+
334
+ const useProducts = () => useContext(ProductsContext);
335
+
336
+ export { ProductsContextProvider, useProducts };
@@ -0,0 +1,35 @@
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, { createContext, useContext } from 'react';
11
+
12
+ const ResultsModifierContext = createContext({
13
+ baseUrl: '',
14
+ baseUrlWithoutProtocol: ''
15
+ });
16
+
17
+ const ResultsModifierProvider = ({
18
+ baseUrl,
19
+ baseUrlWithoutProtocol,
20
+ children
21
+ }) => {
22
+ return (
23
+ <ResultsModifierContext.Provider
24
+ value={{ baseUrl, baseUrlWithoutProtocol }}
25
+ >
26
+ {children}
27
+ </ResultsModifierContext.Provider>
28
+ );
29
+ };
30
+
31
+ const useResultsModifier = () => {
32
+ return useContext(ResultsModifierContext);
33
+ };
34
+
35
+ export { ResultsModifierProvider, useResultsModifier };
@@ -0,0 +1,127 @@
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, { createContext, useState, useEffect, useContext } from 'react';
11
+ import { useLocation } from 'react-router-dom';
12
+
13
+ import { SEARCH_SORT_DEFAULT } from '../utils/constants';
14
+ import {
15
+ addUrlFilter,
16
+ getValueFromUrl,
17
+ removeAllUrlFilters,
18
+ removeUrlFilter,
19
+ } from '../utils/handleUrlFilters';
20
+ import { generateGQLSortInput } from '../utils/sort';
21
+ import { useStore } from './store';
22
+
23
+ export const SearchContext = createContext({});
24
+
25
+ const SearchProvider = ({ children }) => {
26
+ const storeCtx = useStore();
27
+ const location = useLocation(); // watches changes in URL query params
28
+
29
+ const getSearchPhrase = () => getValueFromUrl(storeCtx.searchQuery || 'q');
30
+ const getSortFromUrl = () => getValueFromUrl('product_list_order');
31
+
32
+ const [phrase, setPhrase] = useState(getSearchPhrase());
33
+ const [categoryPath, setCategoryPath] = useState('');
34
+ const [filters, setFilters] = useState([]);
35
+ const [categoryNames, setCategoryNames] = useState([]);
36
+ const [sort, setSort] = useState(generateGQLSortInput(getSortFromUrl()) || SEARCH_SORT_DEFAULT);
37
+ const [filterCount, setFilterCount] = useState(0);
38
+
39
+ // Update phrase and sort when URL changes
40
+ useEffect(() => {
41
+ setPhrase(getSearchPhrase());
42
+ setSort(generateGQLSortInput(getSortFromUrl()) || SEARCH_SORT_DEFAULT);
43
+ }, [location.search]);
44
+
45
+ const createFilter = (filter) => {
46
+ const newFilters = [...filters, filter];
47
+ setFilters(newFilters);
48
+ addUrlFilter(filter);
49
+ };
50
+
51
+ const updateFilter = (filter) => {
52
+ const newFilters = [...filters];
53
+ const index = newFilters.findIndex((e) => e.attribute === filter.attribute);
54
+ newFilters[index] = filter;
55
+ setFilters(newFilters);
56
+ addUrlFilter(filter);
57
+ };
58
+
59
+ const removeFilter = (name, option) => {
60
+ const newFilters = filters.filter((e) => e.attribute !== name);
61
+ setFilters(newFilters);
62
+ removeUrlFilter(name, option);
63
+ };
64
+
65
+ const clearFilters = () => {
66
+ removeAllUrlFilters();
67
+ setFilters([]);
68
+ };
69
+
70
+ const updateFilterOptions = (facetFilter, option) => {
71
+ const newFilters = filters.filter((e) => e.attribute !== facetFilter.attribute);
72
+ const newOptions = facetFilter.in?.filter((e) => e !== option);
73
+
74
+ newFilters.push({
75
+ attribute: facetFilter.attribute,
76
+ in: newOptions,
77
+ });
78
+
79
+ if (newOptions?.length) {
80
+ setFilters(newFilters);
81
+ removeUrlFilter(facetFilter.attribute, option);
82
+ } else {
83
+ removeFilter(facetFilter.attribute, option);
84
+ }
85
+ };
86
+
87
+ const getFilterCount = (filters) => {
88
+ return filters.reduce((count, filter) => {
89
+ return count + (filter.in ? filter.in.length : 1);
90
+ }, 0);
91
+ };
92
+
93
+ useEffect(() => {
94
+ setFilterCount(getFilterCount(filters));
95
+ }, [filters]);
96
+
97
+ const context = {
98
+ phrase,
99
+ categoryPath,
100
+ filters,
101
+ sort,
102
+ categoryNames,
103
+ filterCount,
104
+ setPhrase,
105
+ setCategoryPath,
106
+ setFilters,
107
+ setCategoryNames,
108
+ setSort,
109
+ createFilter,
110
+ updateFilter,
111
+ updateFilterOptions,
112
+ removeFilter,
113
+ clearFilters,
114
+ };
115
+
116
+ return (
117
+ <SearchContext.Provider value={context}>
118
+ {children}
119
+ </SearchContext.Provider>
120
+ );
121
+ };
122
+
123
+ const useSearch = () => {
124
+ return useContext(SearchContext);
125
+ };
126
+
127
+ export { SearchProvider, useSearch };
@@ -0,0 +1,93 @@
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 { createContext } from 'react';
11
+ import { useContext, useMemo } from 'react';
12
+ import React from 'react';
13
+
14
+ // Define default URLs and keys (you can move these to a constants file)
15
+ const API_URL = 'https://catalog-service.adobe.io/graphql';
16
+ const TEST_URL = 'https://catalog-service-sandbox.adobe.io/graphql';
17
+ const SANDBOX_KEY = 'storefront-widgets'; // Replace with your actual sandbox key if needed
18
+
19
+ const StoreContext = createContext({
20
+ environmentId: '',
21
+ environmentType: '',
22
+ websiteCode: '',
23
+ storeCode: '',
24
+ storeViewCode: '',
25
+ apiUrl: '',
26
+ apiKey: '',
27
+ config: {},
28
+ context: {},
29
+ route: undefined,
30
+ searchQuery: 'q',
31
+ });
32
+
33
+ const StoreContextProvider = ({
34
+ children,
35
+ environmentId,
36
+ environmentType,
37
+ websiteCode,
38
+ storeCode,
39
+ storeViewCode,
40
+ config,
41
+ context,
42
+ apiKey,
43
+ route,
44
+ searchQuery = 'q',
45
+ }) => {
46
+ const storeProps = useMemo(() => {
47
+ const isTesting = environmentType?.toLowerCase() === 'testing';
48
+ return {
49
+ environmentId,
50
+ environmentType,
51
+ websiteCode,
52
+ storeCode,
53
+ storeViewCode,
54
+ config,
55
+ context: {
56
+ //getting error because the nullish coalescing operator (??) isn't supported by your Babel/Webpack setup yet.
57
+ // customerGroup: context?.customerGroup ?? '',
58
+ // userViewHistory: context?.userViewHistory ?? [],
59
+
60
+ //workaround
61
+ customerGroup: context && context.customerGroup != null ? context.customerGroup : '',
62
+ userViewHistory: context && context.userViewHistory != null ? context.userViewHistory : [],
63
+ },
64
+ apiUrl: isTesting ? TEST_URL : API_URL,
65
+ apiKey: isTesting && !apiKey ? SANDBOX_KEY : apiKey,
66
+ route,
67
+ searchQuery,
68
+ };
69
+ }, [
70
+ environmentId,
71
+ environmentType,
72
+ websiteCode,
73
+ storeCode,
74
+ storeViewCode,
75
+ config,
76
+ context,
77
+ apiKey,
78
+ route,
79
+ searchQuery,
80
+ ]);
81
+
82
+ return (
83
+ <StoreContext.Provider value={storeProps}>
84
+ {children}
85
+ </StoreContext.Provider>
86
+ );
87
+ };
88
+
89
+ const useStore = () => {
90
+ return useContext(StoreContext);
91
+ };
92
+
93
+ export { StoreContextProvider, useStore };
@@ -0,0 +1,125 @@
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, { createContext, useContext } from 'react';
11
+
12
+ import {
13
+ bg_BG,
14
+ ca_ES,
15
+ cs_CZ,
16
+ da_DK,
17
+ de_DE,
18
+ el_GR,
19
+ en_GB,
20
+ en_US,
21
+ es_ES,
22
+ et_EE,
23
+ eu_ES,
24
+ fa_IR,
25
+ fi_FI,
26
+ fr_FR,
27
+ gl_ES,
28
+ hi_IN,
29
+ hu_HU,
30
+ id_ID,
31
+ it_IT,
32
+ ja_JP,
33
+ ko_KR,
34
+ lt_LT,
35
+ lv_LV,
36
+ nb_NO,
37
+ nl_NL,
38
+ pt_BR,
39
+ pt_PT,
40
+ ro_RO,
41
+ ru_RU,
42
+ sv_SE,
43
+ th_TH,
44
+ tr_TR,
45
+ zh_Hans_CN,
46
+ zh_Hant_TW,
47
+ } from '../i18n';
48
+ import { useStore } from './store';
49
+
50
+ export const languages = {
51
+ default: en_US,
52
+ bg_BG,
53
+ ca_ES,
54
+ cs_CZ,
55
+ da_DK,
56
+ de_DE,
57
+ el_GR,
58
+ en_GB,
59
+ en_US,
60
+ es_ES,
61
+ et_EE,
62
+ eu_ES,
63
+ fa_IR,
64
+ fi_FI,
65
+ fr_FR,
66
+ gl_ES,
67
+ hi_IN,
68
+ hu_HU,
69
+ id_ID,
70
+ it_IT,
71
+ ja_JP,
72
+ ko_KR,
73
+ lt_LT,
74
+ lv_LV,
75
+ nb_NO,
76
+ nl_NL,
77
+ pt_BR,
78
+ pt_PT,
79
+ ro_RO,
80
+ ru_RU,
81
+ sv_SE,
82
+ th_TH,
83
+ tr_TR,
84
+ zh_Hans_CN,
85
+ zh_Hant_TW,
86
+ };
87
+
88
+ export const TranslationContext = createContext(languages.default);
89
+
90
+ const useTranslation = () => {
91
+ const translation = useContext(TranslationContext);
92
+ return translation;
93
+ };
94
+
95
+ const getCurrLanguage = (languageDetected) => {
96
+ const langKeys = Object.keys(languages);
97
+ if (langKeys.includes(languageDetected)) {
98
+ return languageDetected;
99
+ }
100
+ return 'default';
101
+ };
102
+
103
+ const Translation = ({ children }) => {
104
+ const storeCtx = useStore();
105
+
106
+ //getting error because the nullish coalescing operator (??) isn't supported by your Babel/Webpack setup yet.
107
+ //const currLanguage = getCurrLanguage(storeCtx?.config?.locale ?? '');
108
+
109
+ //workaround
110
+ const currLanguage = getCurrLanguage(
111
+ storeCtx && storeCtx.config && storeCtx.config.locale
112
+ ? storeCtx.config.locale
113
+ : ''
114
+ );
115
+
116
+
117
+ return (
118
+ <TranslationContext.Provider value={languages[currLanguage]}>
119
+ {children}
120
+ </TranslationContext.Provider>
121
+ );
122
+ };
123
+
124
+ export default Translation;
125
+ export { getCurrLanguage, useTranslation };