@tapcart/mobile-components 0.12.8 → 0.12.10
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.
- package/dist/components/hooks/use-block-conditional-rendering.d.ts.map +1 -1
- package/dist/components/hooks/use-block-conditional-rendering.js +30 -17
- package/dist/components/hooks/use-infinite-scroll.d.ts.map +1 -1
- package/dist/components/hooks/use-infinite-scroll.js +26 -14
- package/dist/components/hooks/use-product-options.d.ts +7 -1
- package/dist/components/hooks/use-product-options.d.ts.map +1 -1
- package/dist/components/hooks/use-product-options.js +7 -1
- package/dist/components/hooks/use-recommendations.d.ts.map +1 -1
- package/dist/components/hooks/use-recommendations.js +7 -1
- package/dist/components/hooks/use-recommendations.test.d.ts +2 -0
- package/dist/components/hooks/use-recommendations.test.d.ts.map +1 -0
- package/dist/components/hooks/use-recommendations.test.js +85 -0
- package/dist/lib/variablesCart.util.d.ts.map +1 -1
- package/dist/lib/variablesCart.util.js +3 -0
- package/dist/styles.css +3 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-block-conditional-rendering.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-block-conditional-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAGb,MAAM,kBAAkB,CAAA;AAezB,eAAO,MAAM,4BAA4B,WAC/B;IACN,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,GAAG,CAAA;IACpB,iBAAiB,EAAE,GAAG,CAAA;CACvB,UACO,YAAY,6BACO,GAAG;;;
|
|
1
|
+
{"version":3,"file":"use-block-conditional-rendering.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-block-conditional-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAGb,MAAM,kBAAkB,CAAA;AAezB,eAAO,MAAM,4BAA4B,WAC/B;IACN,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,GAAG,CAAA;IACpB,iBAAiB,EAAE,GAAG,CAAA;CACvB,UACO,YAAY,6BACO,GAAG;;;CAiT/B,CAAA"}
|
|
@@ -29,11 +29,7 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
|
|
|
29
29
|
const { id: deviceId = "" } = deviceVariables || {};
|
|
30
30
|
const productId = (_f = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("productId")) !== null && _f !== void 0 ? _f : undefined;
|
|
31
31
|
const productHandle = (_g = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("productHandle")) !== null && _g !== void 0 ? _g : undefined;
|
|
32
|
-
const location = {
|
|
33
|
-
country,
|
|
34
|
-
language,
|
|
35
|
-
deviceId,
|
|
36
|
-
};
|
|
32
|
+
const location = useMemo(() => ({ country, language, deviceId }), [country, language, deviceId]);
|
|
37
33
|
const productMetafieldsQuery = useMemo(() => {
|
|
38
34
|
var _a;
|
|
39
35
|
if (!isConditionalsEnabled) {
|
|
@@ -67,7 +63,7 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
|
|
|
67
63
|
]);
|
|
68
64
|
const shouldFetchProduct = isConditionalsEnabled && (productId || productHandle);
|
|
69
65
|
const useProducts = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts) || mclUseProducts;
|
|
70
|
-
const useProductsArguments = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
|
|
66
|
+
const useProductsArguments = useMemo(() => (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
|
|
71
67
|
? {
|
|
72
68
|
productIds: productId ? [gidFromId(productId)] : [],
|
|
73
69
|
productHandles: productHandle ? [productHandle] : [],
|
|
@@ -82,13 +78,20 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
|
|
|
82
78
|
language,
|
|
83
79
|
country,
|
|
84
80
|
},
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
}, [
|
|
82
|
+
mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts,
|
|
83
|
+
productId,
|
|
84
|
+
productHandle,
|
|
85
|
+
apiUrl,
|
|
86
|
+
language,
|
|
87
|
+
country,
|
|
88
|
+
productMetafieldsQuery,
|
|
89
|
+
]);
|
|
90
|
+
const useProductsSkipArguments = useMemo(() => (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
|
|
91
|
+
? { skip: true }
|
|
92
|
+
: null, [mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts]);
|
|
93
|
+
const useProductsInput = useMemo(() => shouldFetchProduct ? useProductsArguments : useProductsSkipArguments, [shouldFetchProduct, useProductsArguments, useProductsSkipArguments]);
|
|
94
|
+
const { products, error: useProductsError, isLoading: isProductsLoading, } = useProducts(useProductsInput);
|
|
92
95
|
const blockCollectionMetafields = useMemo(() => {
|
|
93
96
|
var _a;
|
|
94
97
|
if (!isConditionalsEnabled) {
|
|
@@ -144,20 +147,30 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
|
|
|
144
147
|
const collectionHandle = (_j = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("collectionHandle")) !== null && _j !== void 0 ? _j : undefined;
|
|
145
148
|
const shouldFetchCollection = isConditionalsEnabled && (collectionId || collectionHandle);
|
|
146
149
|
const useCollection = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useCollection) || mclUseCollection;
|
|
147
|
-
const
|
|
150
|
+
const useCollectionArguments = useMemo(() => ({
|
|
148
151
|
appId,
|
|
149
152
|
apiUrl,
|
|
150
153
|
collectionId: shouldFetchCollection ? collectionId : undefined,
|
|
151
154
|
collectionHandle: shouldFetchCollection ? collectionHandle : undefined,
|
|
152
155
|
language: location === null || location === void 0 ? void 0 : location.language,
|
|
153
156
|
metafields: shouldFetchCollection ? memoizedMetafields : undefined,
|
|
154
|
-
})
|
|
157
|
+
}), [
|
|
158
|
+
appId,
|
|
159
|
+
apiUrl,
|
|
160
|
+
shouldFetchCollection,
|
|
161
|
+
collectionId,
|
|
162
|
+
collectionHandle,
|
|
163
|
+
location === null || location === void 0 ? void 0 : location.language,
|
|
164
|
+
memoizedMetafields,
|
|
165
|
+
]);
|
|
166
|
+
const { specificCollection = {}, error: useCollectionError, loading: isCollectionLoading, } = useCollection(useCollectionArguments);
|
|
155
167
|
const isLoading = isProductsLoading || isCollectionLoading;
|
|
156
168
|
if (useProductsError || useCollectionError) {
|
|
157
169
|
console.error("Unable to load products in conditional block rendering hook: ", useProductsError !== null && useProductsError !== void 0 ? useProductsError : useCollectionError);
|
|
158
170
|
}
|
|
171
|
+
const disabledResult = useMemo(() => ({ shouldShow: true, isLoading: false }), []);
|
|
159
172
|
if (!isConditionalsEnabled) {
|
|
160
|
-
return
|
|
173
|
+
return disabledResult;
|
|
161
174
|
}
|
|
162
175
|
let shouldShow = true;
|
|
163
176
|
const blockHasTags = countNumberOfTagsInState(blockState) > 0;
|
|
@@ -200,5 +213,5 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
|
|
|
200
213
|
console.error("Error evaluating block visibility conditions:", e);
|
|
201
214
|
shouldShow = true; // Fail-safe to show block
|
|
202
215
|
}
|
|
203
|
-
return { shouldShow, isLoading };
|
|
216
|
+
return useMemo(() => ({ shouldShow, isLoading }), [shouldShow, isLoading]);
|
|
204
217
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,
|
|
1
|
+
{"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBAqS3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -49,7 +49,7 @@ initialData, queryVariables: queryVariableProps,
|
|
|
49
49
|
// Common props
|
|
50
50
|
direction = "vertical", productLimit = Infinity, threshold = 0.01, interval = 33, // ~2 frames
|
|
51
51
|
customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
52
|
-
var _a, _b, _c, _d, _e;
|
|
52
|
+
var _a, _b, _c, _d, _e, _f;
|
|
53
53
|
const searchParams = useSearchParams();
|
|
54
54
|
const productCount = useRef(0);
|
|
55
55
|
const isFirstRender = useRef(true);
|
|
@@ -102,7 +102,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
102
102
|
return Object.assign(Object.assign({}, queryVariables), { cursorBlob: previousPageData.pageData.cursorBlob });
|
|
103
103
|
};
|
|
104
104
|
const apiFetcher = (body) => __awaiter(void 0, void 0, void 0, function* () {
|
|
105
|
-
var
|
|
105
|
+
var _g, _h;
|
|
106
106
|
if (!(initialData === null || initialData === void 0 ? void 0 : initialData.apiURL)) {
|
|
107
107
|
throw new Error("initialData.apiURL is required for API mode");
|
|
108
108
|
}
|
|
@@ -111,7 +111,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
111
111
|
body: JSON.stringify(body),
|
|
112
112
|
});
|
|
113
113
|
const data = yield res.json();
|
|
114
|
-
productCount.current += (
|
|
114
|
+
productCount.current += (_h = (_g = data === null || data === void 0 ? void 0 : data.products) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0;
|
|
115
115
|
return data;
|
|
116
116
|
});
|
|
117
117
|
// Return null from getKey to conditionally skip fetching
|
|
@@ -134,20 +134,23 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
134
134
|
};
|
|
135
135
|
const fetcher = usingSearchClient
|
|
136
136
|
? (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
137
|
-
var
|
|
137
|
+
var _j, _k;
|
|
138
138
|
if (!searchClientFetcher)
|
|
139
139
|
return null;
|
|
140
140
|
const result = yield searchClientFetcher(...args);
|
|
141
|
-
productCount.current += (
|
|
141
|
+
productCount.current += (_k = (_j = result === null || result === void 0 ? void 0 : result.products) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : 0;
|
|
142
142
|
return result;
|
|
143
143
|
})
|
|
144
144
|
: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
145
145
|
const effectiveFetcher = customFetcher || apiFetcher;
|
|
146
146
|
return effectiveFetcher(...args);
|
|
147
147
|
});
|
|
148
|
+
// Check if caller provided pre-fetched products to display while SWR fetches
|
|
149
|
+
const hasInitialProducts = Boolean((_a = initialData === null || initialData === void 0 ? void 0 : initialData.products) === null || _a === void 0 ? void 0 : _a.length);
|
|
148
150
|
const { data, error, size, setSize, isLoading, isValidating, mutate, } = useSWRInfinite(getKey, fetcher, {
|
|
149
151
|
revalidateFirstPage: false,
|
|
150
152
|
initialSize: 1,
|
|
153
|
+
keepPreviousData: true,
|
|
151
154
|
});
|
|
152
155
|
// Detect when params change and force cache invalidation
|
|
153
156
|
useEffect(() => {
|
|
@@ -166,15 +169,19 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
166
169
|
// Skip reset on first render, but force it for any subsequent transitions to valid params
|
|
167
170
|
if (!isFirstRender.current && hasValidCurrentParams) {
|
|
168
171
|
productCount.current = 0;
|
|
169
|
-
mutate(
|
|
172
|
+
mutate();
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
isFirstRender.current = false;
|
|
173
176
|
}, [currentCollectionId, currentCollectionHandle, currentSearchQuery, mutate]);
|
|
174
|
-
|
|
177
|
+
// When we have pre-fetched products, don't report as "loading initial data"
|
|
178
|
+
// so the grid renders immediately with those products
|
|
179
|
+
const isLoadingInitialData = !data && !error && !hasInitialProducts;
|
|
175
180
|
const isLoadingMore = isLoadingInitialData ||
|
|
176
181
|
(size > 0 && data && typeof data[size - 1] === "undefined");
|
|
177
|
-
const isEmpty =
|
|
182
|
+
const isEmpty = data
|
|
183
|
+
? ((_d = (_c = (_b = data === null || data === void 0 ? void 0 : data[0]) === null || _b === void 0 ? void 0 : _b.products) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) === 0
|
|
184
|
+
: !hasInitialProducts;
|
|
178
185
|
let isEndPointer;
|
|
179
186
|
if (!data) {
|
|
180
187
|
isEndPointer = true;
|
|
@@ -183,7 +190,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
183
190
|
isEndPointer = !(searchClient === null || searchClient === void 0 ? void 0 : searchClient.getHasMore());
|
|
184
191
|
}
|
|
185
192
|
else {
|
|
186
|
-
isEndPointer = !((
|
|
193
|
+
isEndPointer = !((_f = (_e = data[data.length - 1]) === null || _e === void 0 ? void 0 : _e.pageData) === null || _f === void 0 ? void 0 : _f.cursorBlob);
|
|
187
194
|
}
|
|
188
195
|
const isReachingEnd = isEmpty || isEndPointer;
|
|
189
196
|
const isRefreshing = isValidating && data && data.length === size;
|
|
@@ -214,15 +221,20 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
214
221
|
return;
|
|
215
222
|
const unsubscribeAllChanges = searchClient.onSearchStateChange(() => {
|
|
216
223
|
productCount.current = 0;
|
|
217
|
-
mutate(
|
|
224
|
+
mutate();
|
|
218
225
|
});
|
|
219
226
|
return unsubscribeAllChanges;
|
|
220
227
|
}, [searchClient, mutate, usingSearchClient]);
|
|
221
228
|
const products = useMemo(() => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
229
|
+
if (data) {
|
|
230
|
+
return data.flatMap((page) => page === null || page === void 0 ? void 0 : page.products).slice(0, productLimit);
|
|
231
|
+
}
|
|
232
|
+
// Use pre-fetched products while SWR fetches the real paginated data
|
|
233
|
+
if (hasInitialProducts) {
|
|
234
|
+
return initialData.products;
|
|
235
|
+
}
|
|
236
|
+
return [];
|
|
237
|
+
}, [data, productLimit, hasInitialProducts, initialData === null || initialData === void 0 ? void 0 : initialData.products]);
|
|
226
238
|
// Collect all integration responses per page when using search client
|
|
227
239
|
const integrationResponses = useMemo(() => {
|
|
228
240
|
if (!usingSearchClient)
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { ProductVariant } from "app-studio-types";
|
|
2
2
|
type SelectedOptions = Record<string, string>;
|
|
3
|
-
|
|
3
|
+
type VariantConnection = {
|
|
4
|
+
edges: {
|
|
5
|
+
node: ProductVariant;
|
|
6
|
+
}[];
|
|
7
|
+
};
|
|
8
|
+
type VariantsInput = ProductVariant[] | VariantConnection | undefined | null;
|
|
9
|
+
export declare function useProductOptions(variants: VariantsInput, selectedVariantId?: string | null): {
|
|
4
10
|
selectedOptions: SelectedOptions;
|
|
5
11
|
handleSelect: (optionName: string, optionValue: string) => void;
|
|
6
12
|
selectedVariant: ProductVariant | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-product-options.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-product-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE7C,
|
|
1
|
+
{"version":3,"file":"use-product-options.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-product-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE7C,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,cAAc,CAAA;KAAE,EAAE,CAAA;CAClC,CAAA;AAED,KAAK,aAAa,GAAG,cAAc,EAAE,GAAG,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAA;AAE5E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI;;+BAwEC,MAAM,eAAe,MAAM,KAAG,IAAI;;;;;;EA4CrE"}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useEffect, useMemo, useCallback } from "react";
|
|
3
3
|
export function useProductOptions(variants, selectedVariantId) {
|
|
4
|
-
const safeVariants = useMemo(() =>
|
|
4
|
+
const safeVariants = useMemo(() => {
|
|
5
|
+
var _a;
|
|
6
|
+
if (variants && "edges" in variants) {
|
|
7
|
+
return (((_a = variants.edges) === null || _a === void 0 ? void 0 : _a.map((e) => e === null || e === void 0 ? void 0 : e.node).filter((v) => Boolean(v))) || []);
|
|
8
|
+
}
|
|
9
|
+
return variants || [];
|
|
10
|
+
}, [variants]);
|
|
5
11
|
const getCheapestProductFromVariants = useMemo(() => {
|
|
6
12
|
return () => {
|
|
7
13
|
if (!safeVariants.length)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-recommendations.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAOxE,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAUD,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,GAAG,CAAA;CACX;AAmBD,QAAA,MAAM,kBAAkB,yEASrB,sBAAsB,KAAG,
|
|
1
|
+
{"version":3,"file":"use-recommendations.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAOxE,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAUD,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,GAAG,CAAA;CACX;AAmBD,QAAA,MAAM,kBAAkB,yEASrB,sBAAsB,KAAG,uBA2H3B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -35,7 +35,13 @@ queryVariables, apiURL, }) => {
|
|
|
35
35
|
? query
|
|
36
36
|
: (searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("recommendation")) || "";
|
|
37
37
|
// Check if resolved recommendation meets minimum length requirement
|
|
38
|
-
|
|
38
|
+
// Allow fetching when:
|
|
39
|
+
// - query is empty → show trending searches
|
|
40
|
+
// - query meets minimum length (≥3 chars)
|
|
41
|
+
// - searchClient has a customSearchConfig (e.g., filter-only queries on PDP)
|
|
42
|
+
const shouldFetch = !recommendation.trim().length ||
|
|
43
|
+
recommendation.length >= MIN_QUERY_LENGTH ||
|
|
44
|
+
(usingSearchClient && Boolean(customSearchConfig));
|
|
39
45
|
const [cachedRecommendation, setCachedRecommendations] = React.useState(recommendationsLocalStorage.getCacheItem({
|
|
40
46
|
id: `${recommendation}-${queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.language}`,
|
|
41
47
|
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-recommendations.test.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { useRecommendations } from "./use-recommendations";
|
|
3
|
+
// Mock next/navigation
|
|
4
|
+
jest.mock("next/navigation", () => ({
|
|
5
|
+
useSearchParams: () => new URLSearchParams(),
|
|
6
|
+
}));
|
|
7
|
+
// Mock SWR to give us control over fetching behavior
|
|
8
|
+
jest.mock("swr", () => {
|
|
9
|
+
return {
|
|
10
|
+
__esModule: true,
|
|
11
|
+
default: (key, fetcher, _options) => {
|
|
12
|
+
// If key is null, SWR doesn't fetch
|
|
13
|
+
if (key === null) {
|
|
14
|
+
return { data: undefined, error: undefined, isLoading: false };
|
|
15
|
+
}
|
|
16
|
+
return { data: undefined, error: undefined, isLoading: true };
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
// Mock the local storage cache
|
|
21
|
+
jest.mock("../libs/cache/RecommendationsLocalStorage", () => {
|
|
22
|
+
return {
|
|
23
|
+
__esModule: true,
|
|
24
|
+
default: jest.fn().mockImplementation(() => ({
|
|
25
|
+
getCacheItem: jest.fn().mockReturnValue(null),
|
|
26
|
+
setCacheItem: jest.fn(),
|
|
27
|
+
})),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
describe("useRecommendations", () => {
|
|
31
|
+
const mockSearchClient = {
|
|
32
|
+
getRecommendations: jest.fn().mockResolvedValue({
|
|
33
|
+
products: [{ id: "1", title: "Product 1" }],
|
|
34
|
+
searches: ["suggestion 1"],
|
|
35
|
+
facets: [],
|
|
36
|
+
totalResults: 1,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
const defaultProps = {
|
|
40
|
+
queryVariables: { appId: "test-app" },
|
|
41
|
+
apiURL: "http://test.com/recommendations",
|
|
42
|
+
};
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
});
|
|
46
|
+
it("should fetch trending recommendations when query is empty", () => {
|
|
47
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "" }, defaultProps)));
|
|
48
|
+
// Empty query now triggers a trending recommendations fetch
|
|
49
|
+
expect(result.current.isLoading).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("should not fetch when query is below MIN_QUERY_LENGTH and no customSearchConfig", () => {
|
|
52
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "ab" }, defaultProps)));
|
|
53
|
+
expect(result.current.products).toEqual([]);
|
|
54
|
+
expect(result.current.isLoading).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
it("should fetch trending when no searchClient and empty search params", () => {
|
|
57
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ query: "ab" }, defaultProps)));
|
|
58
|
+
// Without searchClient, recommendation comes from searchParams (empty string)
|
|
59
|
+
// Empty query now triggers a trending fetch
|
|
60
|
+
expect(result.current.isLoading).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
it("should fetch when query meets MIN_QUERY_LENGTH", () => {
|
|
63
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "abc" }, defaultProps)));
|
|
64
|
+
// SWR mock returns isLoading: true when key is not null
|
|
65
|
+
expect(result.current.isLoading).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it("should fetch when customSearchConfig is provided with searchClient even with empty query", () => {
|
|
68
|
+
const customSearchConfig = {
|
|
69
|
+
filter: 'attributes.mfield_bnb__seriesId = 584945 AND NOT id: ANY("9338273530096")',
|
|
70
|
+
pageSize: 6,
|
|
71
|
+
};
|
|
72
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "", customSearchConfig }, defaultProps)));
|
|
73
|
+
// shouldFetch should be true due to customSearchConfig bypass, so SWR gets a key and returns isLoading: true
|
|
74
|
+
expect(result.current.isLoading).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it("should fetch trending when customSearchConfig is provided without searchClient and empty query", () => {
|
|
77
|
+
const customSearchConfig = {
|
|
78
|
+
filter: "attributes.mfield_bnb__seriesId = 584945",
|
|
79
|
+
pageSize: 6,
|
|
80
|
+
};
|
|
81
|
+
const { result } = renderHook(() => useRecommendations(Object.assign({ query: "", customSearchConfig }, defaultProps)));
|
|
82
|
+
// Empty query now triggers a trending fetch regardless of searchClient
|
|
83
|
+
expect(result.current.isLoading).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variablesCart.util.d.ts","sourceRoot":"","sources":["../../lib/variablesCart.util.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,6BAA6B,EAO9B,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,2BAA2B;;cAM5B,MAAM;;0BAEI;YAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;YACzB,KAAK,EAAE,MAAM,CAAA;YACb,cAAc,CAAC,EAAE,MAAM,CAAA;SACxB,EAAE;;;;;;CAyBN,CAAA;
|
|
1
|
+
{"version":3,"file":"variablesCart.util.d.ts","sourceRoot":"","sources":["../../lib/variablesCart.util.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,6BAA6B,EAO9B,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,2BAA2B;;cAM5B,MAAM;;0BAEI;YAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;YACzB,KAAK,EAAE,MAAM,CAAA;YACb,cAAc,CAAC,EAAE,MAAM,CAAA;SACxB,EAAE;;;;;;CAyBN,CAAA;AA8FD,eAAO,MAAM,6BAA6B,SAClC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAED,eAAO,MAAM,2BAA2B,SAChC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAoMD,MAAM,MAAM,uBAAuB,GAAG;IACpC,yBAAyB,EAAE,sBAAsB,EAAE,CAAA;IACnD,gBAAgB,EAAE,6BAA6B,EAAE,CAAA;IACjD,cAAc,EAAE,OAAO,CAAA;IACvB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,eAAO,MAAM,iCAAiC,EAAE,uBAU/C,CAAA;AAED,eAAO,MAAM,8BAA8B,SACnC,SAAS,GAAG,IAAI,KACrB,uBAmCF,CAAA"}
|
|
@@ -42,6 +42,9 @@ const getOrderLevelDiscounts = (cart) => {
|
|
|
42
42
|
if (discount.code) {
|
|
43
43
|
code = ((discount === null || discount === void 0 ? void 0 : discount.code) || "").toUpperCase();
|
|
44
44
|
}
|
|
45
|
+
else if (discount.title) {
|
|
46
|
+
code = ((discount === null || discount === void 0 ? void 0 : discount.title) || "").toUpperCase();
|
|
47
|
+
}
|
|
45
48
|
else if ((_b = (_a = discount.kind) === null || _a === void 0 ? void 0 : _a.manual) === null || _b === void 0 ? void 0 : _b.code) {
|
|
46
49
|
code = (((_d = (_c = discount === null || discount === void 0 ? void 0 : discount.kind) === null || _c === void 0 ? void 0 : _c.manual) === null || _d === void 0 ? void 0 : _d.code) || "").toUpperCase();
|
|
47
50
|
}
|
package/dist/styles.css
CHANGED