@tapcart/mobile-components 0.14.0 → 0.14.5
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-infinite-scroll.d.ts.map +1 -1
- package/dist/components/hooks/use-infinite-scroll.js +41 -20
- package/dist/components/hooks/use-infinite-scroll.test.d.ts +2 -0
- package/dist/components/hooks/use-infinite-scroll.test.d.ts.map +1 -0
- package/dist/components/hooks/use-infinite-scroll.test.js +212 -0
- package/dist/components/ui/animate-container.d.ts.map +1 -1
- package/dist/components/ui/chip.d.ts.map +1 -1
- package/dist/components/ui/quantity-picker.d.ts.map +1 -1
- package/dist/components/ui/quantity-picker.js +1 -1
- package/dist/components/ui/quantity-pickerNEW.d.ts +0 -1
- package/dist/components/ui/quantity-pickerNEW.d.ts.map +1 -1
- package/dist/components/ui/quantity-pickerNEW.js +1 -1
- package/dist/components/ui/use-toast.d.ts.map +1 -1
- package/dist/components/ui/wishlist-select.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/lib/cart.util.d.ts.map +1 -1
- package/dist/lib/cart.util.js +3 -2
- package/dist/lib/cart.util.test.js +89 -0
- package/dist/lib/discountAllocation.util.d.ts +24 -0
- package/dist/lib/discountAllocation.util.d.ts.map +1 -0
- package/dist/lib/discountAllocation.util.js +150 -0
- package/dist/lib/discountAllocation.util.test.d.ts +2 -0
- package/dist/lib/discountAllocation.util.test.d.ts.map +1 -0
- package/dist/lib/discountAllocation.util.test.js +200 -0
- package/dist/lib/variablesCart.util.d.ts.map +1 -1
- package/dist/lib/variablesCart.util.js +3 -4
- package/dist/lib/variablesCart.util.test.js +85 -0
- package/dist/styles.css +3 -0
- package/package.json +1 -1
|
@@ -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;AAG5D,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;AAWD,eAAO,MAAM,wBAAwB,UAAa,CAAA;AAIlD,eAAO,MAAM,qBAAqB,WACxB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B,OAAO,MAAM,EAAE,OAAO,CAMxB,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;AAG5D,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;AAWD,eAAO,MAAM,wBAAwB,UAAa,CAAA;AAIlD,eAAO,MAAM,qBAAqB,WACxB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B,OAAO,MAAM,EAAE,OAAO,CAMxB,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBA8V3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -71,7 +71,6 @@ direction = "vertical", productLimit = Infinity, threshold = 0.01, interval = 33
|
|
|
71
71
|
customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
72
72
|
var _a, _b, _c, _d, _e, _f;
|
|
73
73
|
const searchParams = useSearchParams();
|
|
74
|
-
const productCount = useRef(0);
|
|
75
74
|
const isFirstRender = useRef(true);
|
|
76
75
|
const prevParamsRef = useRef({
|
|
77
76
|
collectionId: null,
|
|
@@ -122,7 +121,6 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
122
121
|
return Object.assign(Object.assign({}, queryVariables), { cursorBlob: previousPageData.pageData.cursorBlob });
|
|
123
122
|
};
|
|
124
123
|
const apiFetcher = (body) => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
-
var _g, _h;
|
|
126
124
|
if (!(initialData === null || initialData === void 0 ? void 0 : initialData.apiURL)) {
|
|
127
125
|
throw new Error("initialData.apiURL is required for API mode");
|
|
128
126
|
}
|
|
@@ -131,7 +129,6 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
131
129
|
body: JSON.stringify(body),
|
|
132
130
|
});
|
|
133
131
|
const data = yield res.json();
|
|
134
|
-
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;
|
|
135
132
|
return data;
|
|
136
133
|
});
|
|
137
134
|
// Return null from getKey to conditionally skip fetching
|
|
@@ -154,18 +151,14 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
154
151
|
};
|
|
155
152
|
const fetcher = usingSearchClient
|
|
156
153
|
? (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
157
|
-
var _j, _k;
|
|
158
154
|
if (!searchClientFetcher)
|
|
159
155
|
return null;
|
|
160
156
|
const result = yield searchClientFetcher(...args);
|
|
161
|
-
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;
|
|
162
157
|
return result;
|
|
163
158
|
})
|
|
164
159
|
: (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
165
|
-
var _l, _m;
|
|
166
160
|
if (customFetcher) {
|
|
167
161
|
const result = yield customFetcher(...args);
|
|
168
|
-
productCount.current += (_m = (_l = result === null || result === void 0 ? void 0 : result.products) === null || _l === void 0 ? void 0 : _l.length) !== null && _m !== void 0 ? _m : 0;
|
|
169
162
|
return result;
|
|
170
163
|
}
|
|
171
164
|
return apiFetcher(args[0]);
|
|
@@ -203,7 +196,6 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
203
196
|
};
|
|
204
197
|
// Skip reset on first render, but force it for any subsequent transitions to valid params
|
|
205
198
|
if (!isFirstRender.current && hasValidCurrentParams) {
|
|
206
|
-
productCount.current = 0;
|
|
207
199
|
mutate();
|
|
208
200
|
}
|
|
209
201
|
}
|
|
@@ -241,10 +233,49 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
241
233
|
}
|
|
242
234
|
const isReachingEnd = isEmpty || isEndPointer;
|
|
243
235
|
const isRefreshing = isValidating && data && data.length === size;
|
|
236
|
+
// Flatten the SWR Infinite pages into the rendered list, deduped by product id
|
|
237
|
+
// (handle fallback). Cursor/page pagination over a volatile, mutable-sort
|
|
238
|
+
// result set — Shopify relevance/best-selling search, reorderable collections —
|
|
239
|
+
// re-serves boundary products on the following page, so consecutive pages
|
|
240
|
+
// overlap; without this dedupe the same product renders twice (seen on Edikted
|
|
241
|
+
// search). Keep the FIRST occurrence so the original ranked position wins, and
|
|
242
|
+
// dedupe BEFORE the productLimit slice so a finite limit (carousels) yields N
|
|
243
|
+
// UNIQUE products. Two rows sharing a product id always resolve to the same
|
|
244
|
+
// product-level card (the search-client pipeline resolves every row via
|
|
245
|
+
// getProduct(id|handle) and discards per-hit variant context), so this can
|
|
246
|
+
// never collapse a legitimately-distinct variant card.
|
|
247
|
+
const products = useMemo(() => {
|
|
248
|
+
var _a;
|
|
249
|
+
const flat = data
|
|
250
|
+
? data.flatMap((page) => { var _a; return (_a = page === null || page === void 0 ? void 0 : page.products) !== null && _a !== void 0 ? _a : []; })
|
|
251
|
+
: hasInitialProducts
|
|
252
|
+
? initialData.products
|
|
253
|
+
: [];
|
|
254
|
+
const seen = new Set();
|
|
255
|
+
const deduped = [];
|
|
256
|
+
for (const product of flat) {
|
|
257
|
+
if (!product)
|
|
258
|
+
continue;
|
|
259
|
+
const key = (_a = product.id) !== null && _a !== void 0 ? _a : product.handle;
|
|
260
|
+
if (key == null) {
|
|
261
|
+
deduped.push(product);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (seen.has(key))
|
|
265
|
+
continue;
|
|
266
|
+
seen.add(key);
|
|
267
|
+
deduped.push(product);
|
|
268
|
+
}
|
|
269
|
+
return deduped.slice(0, productLimit);
|
|
270
|
+
}, [data, productLimit, hasInitialProducts, initialData === null || initialData === void 0 ? void 0 : initialData.products]);
|
|
244
271
|
const loadMore = useCallback(() => {
|
|
245
272
|
if (isLoading || isLoadingMore || isReachingEnd || isRefreshing)
|
|
246
273
|
return;
|
|
247
|
-
|
|
274
|
+
// Gate on the deduped, limit-sliced `products` count. Counting raw fetched
|
|
275
|
+
// products would include the page-seam duplicates the memo above removes, so
|
|
276
|
+
// a finite productLimit could look "reached" while fewer than productLimit
|
|
277
|
+
// unique products have actually loaded — under-filling the carousel.
|
|
278
|
+
if (products.length >= productLimit)
|
|
248
279
|
return;
|
|
249
280
|
setSize(size + 1);
|
|
250
281
|
}, [
|
|
@@ -252,6 +283,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
252
283
|
isLoadingMore,
|
|
253
284
|
isReachingEnd,
|
|
254
285
|
isRefreshing,
|
|
286
|
+
products.length,
|
|
255
287
|
productLimit,
|
|
256
288
|
setSize,
|
|
257
289
|
size,
|
|
@@ -267,21 +299,10 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
|
|
|
267
299
|
if (!usingSearchClient || !searchClient)
|
|
268
300
|
return;
|
|
269
301
|
const unsubscribeAllChanges = searchClient.onSearchStateChange(() => {
|
|
270
|
-
productCount.current = 0;
|
|
271
302
|
mutate();
|
|
272
303
|
});
|
|
273
304
|
return unsubscribeAllChanges;
|
|
274
305
|
}, [searchClient, mutate, usingSearchClient]);
|
|
275
|
-
const products = useMemo(() => {
|
|
276
|
-
if (data) {
|
|
277
|
-
return data.flatMap((page) => page === null || page === void 0 ? void 0 : page.products).slice(0, productLimit);
|
|
278
|
-
}
|
|
279
|
-
// Use pre-fetched products while SWR fetches the real paginated data
|
|
280
|
-
if (hasInitialProducts) {
|
|
281
|
-
return initialData.products;
|
|
282
|
-
}
|
|
283
|
-
return [];
|
|
284
|
-
}, [data, productLimit, hasInitialProducts, initialData === null || initialData === void 0 ? void 0 : initialData.products]);
|
|
285
306
|
// Collect all integration responses per page when using search client
|
|
286
307
|
const integrationResponses = useMemo(() => {
|
|
287
308
|
if (!usingSearchClient)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-infinite-scroll.test.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
// ─── Module mocks ────────────────────────────────────────────────────────────
|
|
3
|
+
// useSWRInfinite is the source of `data` (the array of fetched pages). We mock
|
|
4
|
+
// it so each test can hand the hook an exact set of (possibly overlapping)
|
|
5
|
+
// pages and assert how they are flattened/deduped into `products`.
|
|
6
|
+
jest.mock("swr/infinite", () => ({
|
|
7
|
+
__esModule: true,
|
|
8
|
+
default: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
jest.mock("react-intersection-observer", () => ({
|
|
11
|
+
useInView: jest.fn(() => ({ ref: jest.fn(), inView: false })),
|
|
12
|
+
}));
|
|
13
|
+
jest.mock("next/navigation", () => ({
|
|
14
|
+
useSearchParams: () => new URLSearchParams(),
|
|
15
|
+
}));
|
|
16
|
+
jest.mock("./swr-retry", () => ({
|
|
17
|
+
useSwrRetryConfig: () => ({
|
|
18
|
+
errorRetryCount: 0,
|
|
19
|
+
shouldRetryOnError: false,
|
|
20
|
+
onErrorRetry: jest.fn(),
|
|
21
|
+
onSuccess: jest.fn(),
|
|
22
|
+
isRetrying: false,
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
// ─── Imports after mocks ─────────────────────────────────────────────────────
|
|
26
|
+
import useSWRInfinite from "swr/infinite";
|
|
27
|
+
import { useInView } from "react-intersection-observer";
|
|
28
|
+
import { useInfiniteScroll } from "./use-infinite-scroll";
|
|
29
|
+
const mockUseSWRInfinite = useSWRInfinite;
|
|
30
|
+
const mockUseInView = useInView;
|
|
31
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
32
|
+
const setPages = (pages) => {
|
|
33
|
+
var _a;
|
|
34
|
+
mockUseSWRInfinite.mockReturnValue({
|
|
35
|
+
data: pages,
|
|
36
|
+
error: undefined,
|
|
37
|
+
size: (_a = pages === null || pages === void 0 ? void 0 : pages.length) !== null && _a !== void 0 ? _a : 0,
|
|
38
|
+
setSize: jest.fn(),
|
|
39
|
+
isLoading: false,
|
|
40
|
+
isValidating: false,
|
|
41
|
+
mutate: jest.fn(),
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const page = (products, hasMore = true) => ({
|
|
45
|
+
products,
|
|
46
|
+
hasMore,
|
|
47
|
+
filtersData: null,
|
|
48
|
+
apiURL: "https://example.com/api",
|
|
49
|
+
});
|
|
50
|
+
const renderProducts = (props = {}) => renderHook(() => useInfiniteScroll(Object.assign({
|
|
51
|
+
// Force fetching on so `products` is returned (not the skip-override).
|
|
52
|
+
shouldSkipFetch: false, initialData: {
|
|
53
|
+
apiURL: "https://example.com/api",
|
|
54
|
+
products: [],
|
|
55
|
+
filtersData: null,
|
|
56
|
+
}, queryVariables: { collectionId: "c1" } }, props))).result.current.products;
|
|
57
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
58
|
+
describe("useInfiniteScroll – page flatten & dedupe", () => {
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
mockUseSWRInfinite.mockReset();
|
|
61
|
+
});
|
|
62
|
+
it("removes products re-served at page seams, keeping first occurrence", () => {
|
|
63
|
+
// page 1 ends with id 3; page 2 re-serves 3 at its head (the cursor race);
|
|
64
|
+
// page 3 re-serves 5. Expected: 1..6 once each, in first-seen order.
|
|
65
|
+
setPages([
|
|
66
|
+
page([{ id: "1" }, { id: "2" }, { id: "3" }]),
|
|
67
|
+
page([{ id: "3" }, { id: "4" }, { id: "5" }]),
|
|
68
|
+
page([{ id: "5" }, { id: "6" }], false),
|
|
69
|
+
]);
|
|
70
|
+
expect(renderProducts().map((p) => p.id)).toEqual([
|
|
71
|
+
"1",
|
|
72
|
+
"2",
|
|
73
|
+
"3",
|
|
74
|
+
"4",
|
|
75
|
+
"5",
|
|
76
|
+
"6",
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
it("dedupes BEFORE applying productLimit (limit counts unique products)", () => {
|
|
80
|
+
setPages([
|
|
81
|
+
page([{ id: "1" }, { id: "2" }, { id: "3" }]),
|
|
82
|
+
page([{ id: "3" }, { id: "4" }, { id: "5" }]),
|
|
83
|
+
]);
|
|
84
|
+
// If the slice ran before dedupe we'd get ["1","2","3","3"]; dedupe-first
|
|
85
|
+
// yields four UNIQUE products.
|
|
86
|
+
expect(renderProducts({ productLimit: 4 }).map((p) => p.id)).toEqual([
|
|
87
|
+
"1",
|
|
88
|
+
"2",
|
|
89
|
+
"3",
|
|
90
|
+
"4",
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
it("dedupes and slices the initialData fallback before SWR data resolves", () => {
|
|
94
|
+
// Before the first fetch resolves, `data` is undefined and `products` falls
|
|
95
|
+
// back to `initialData.products` (the warm-IDB seed from
|
|
96
|
+
// useInfiniteScrollCached, which is NOT pre-bounded to productLimit). That
|
|
97
|
+
// fallback now runs through the same dedupe + productLimit slice as fetched
|
|
98
|
+
// pages, so a seed with duplicates and more entries than the limit is capped
|
|
99
|
+
// to N unique products on first paint instead of rendering the raw seed.
|
|
100
|
+
setPages(undefined);
|
|
101
|
+
const products = renderProducts({
|
|
102
|
+
initialData: {
|
|
103
|
+
apiURL: "https://example.com/api",
|
|
104
|
+
products: [{ id: "1" }, { id: "1" }, { id: "2" }, { id: "3" }],
|
|
105
|
+
filtersData: null,
|
|
106
|
+
},
|
|
107
|
+
productLimit: 2,
|
|
108
|
+
});
|
|
109
|
+
expect(products.map((p) => p.id)).toEqual(["1", "2"]);
|
|
110
|
+
});
|
|
111
|
+
it("falls back to handle when id is absent", () => {
|
|
112
|
+
setPages([
|
|
113
|
+
page([{ handle: "a" }, { handle: "b" }]),
|
|
114
|
+
page([{ handle: "b" }, { handle: "c" }], false),
|
|
115
|
+
]);
|
|
116
|
+
expect(renderProducts().map((p) => p.handle)).toEqual(["a", "b", "c"]);
|
|
117
|
+
});
|
|
118
|
+
it("does not collapse same-name colorways that are distinct product ids", () => {
|
|
119
|
+
// The Edikted "Holly Glitter" case: same title, different ids → all kept.
|
|
120
|
+
setPages([
|
|
121
|
+
page([
|
|
122
|
+
{ id: "gid://shopify/Product/1", handle: "holly_silver" },
|
|
123
|
+
{ id: "gid://shopify/Product/2", handle: "holly_gold" },
|
|
124
|
+
]),
|
|
125
|
+
page([
|
|
126
|
+
{ id: "gid://shopify/Product/2", handle: "holly_gold" },
|
|
127
|
+
{ id: "gid://shopify/Product/3", handle: "holly_black" },
|
|
128
|
+
], false),
|
|
129
|
+
]);
|
|
130
|
+
expect(renderProducts().map((p) => p.handle)).toEqual([
|
|
131
|
+
"holly_silver",
|
|
132
|
+
"holly_gold",
|
|
133
|
+
"holly_black",
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
it("keeps unkeyable items (no id and no handle) rather than dropping them", () => {
|
|
137
|
+
setPages([page([{ title: "x" }, { title: "y" }], false)]);
|
|
138
|
+
expect(renderProducts()).toHaveLength(2);
|
|
139
|
+
});
|
|
140
|
+
it("skips nullish page payloads while still in flight", () => {
|
|
141
|
+
setPages([page([{ id: "1" }, { id: "2" }]), undefined]);
|
|
142
|
+
expect(renderProducts().map((p) => p.id)).toEqual(["1", "2"]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// ─── loadMore gate: deduped count, not raw fetch count ──────────────────────
|
|
146
|
+
//
|
|
147
|
+
// `loadMore` must gate on the deduped, limit-sliced `products` count, not the
|
|
148
|
+
// raw `productCount` ref (which counts every fetched product including
|
|
149
|
+
// page-seam duplicates the `products` memo removes). These tests drive the
|
|
150
|
+
// `inView` intersection-observer effect (the real trigger path for `loadMore`)
|
|
151
|
+
// against pages whose provider size is smaller than `productLimit`, with
|
|
152
|
+
// boundary duplicates between pages. `pageData.cursorBlob` is set on every
|
|
153
|
+
// page (non-search-client / API mode) so `isReachingEnd` stays false and only
|
|
154
|
+
// the productLimit gate can be responsible for stopping pagination.
|
|
155
|
+
describe("useInfiniteScroll – loadMore gate uses deduped count, not raw fetch count", () => {
|
|
156
|
+
const apiPage = (products, cursorBlob = "cursor") => ({
|
|
157
|
+
products,
|
|
158
|
+
pageData: { cursorBlob },
|
|
159
|
+
filtersData: null,
|
|
160
|
+
apiURL: "https://example.com/api",
|
|
161
|
+
});
|
|
162
|
+
const renderWithPages = (pages, setSizeSpy, productLimit) => {
|
|
163
|
+
mockUseSWRInfinite.mockReturnValue({
|
|
164
|
+
data: pages,
|
|
165
|
+
error: undefined,
|
|
166
|
+
size: pages.length,
|
|
167
|
+
setSize: setSizeSpy,
|
|
168
|
+
isLoading: false,
|
|
169
|
+
isValidating: false,
|
|
170
|
+
mutate: jest.fn(),
|
|
171
|
+
});
|
|
172
|
+
return renderHook(() => useInfiniteScroll({
|
|
173
|
+
shouldSkipFetch: false,
|
|
174
|
+
initialData: {
|
|
175
|
+
apiURL: "https://example.com/api",
|
|
176
|
+
products: [],
|
|
177
|
+
filtersData: null,
|
|
178
|
+
},
|
|
179
|
+
queryVariables: { collectionId: "c1" },
|
|
180
|
+
// interval: 0 makes the lodash throttle wrapping loadMore invoke
|
|
181
|
+
// synchronously on the leading edge, so the inView effect's call is
|
|
182
|
+
// observable immediately after render.
|
|
183
|
+
interval: 0,
|
|
184
|
+
productLimit,
|
|
185
|
+
}));
|
|
186
|
+
};
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
mockUseSWRInfinite.mockReset();
|
|
189
|
+
mockUseInView.mockReturnValue({ ref: jest.fn(), inView: true });
|
|
190
|
+
});
|
|
191
|
+
it("keeps paginating while deduped products are still below a finite limit", () => {
|
|
192
|
+
const setSizeSpy = jest.fn();
|
|
193
|
+
// Provider page size (2) is well below productLimit (6); only 2 unique
|
|
194
|
+
// products so far — loadMore must advance setSize rather than gate off.
|
|
195
|
+
renderWithPages([apiPage([{ id: "1" }, { id: "2" }])], setSizeSpy, 6);
|
|
196
|
+
expect(setSizeSpy).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
it("stops paginating once deduped products reach the limit, even though raw fetched count differs due to seam duplicates", () => {
|
|
199
|
+
const setSizeSpy = jest.fn();
|
|
200
|
+
// 3 pages x 2 raw products = 6 raw fetched entries, but seam duplicates
|
|
201
|
+
// ("2" and "3") collapse them to 4 UNIQUE products — exactly productLimit.
|
|
202
|
+
// The last page still has a cursorBlob (more pages are technically
|
|
203
|
+
// available), so only the deduped-count gate — not isReachingEnd — can be
|
|
204
|
+
// responsible for stopping pagination here.
|
|
205
|
+
renderWithPages([
|
|
206
|
+
apiPage([{ id: "1" }, { id: "2" }]),
|
|
207
|
+
apiPage([{ id: "2" }, { id: "3" }]),
|
|
208
|
+
apiPage([{ id: "3" }, { id: "4" }]),
|
|
209
|
+
], setSizeSpy, 4);
|
|
210
|
+
expect(setSizeSpy).not.toHaveBeenCalled();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate-container.d.ts","sourceRoot":"","sources":["../../../components/ui/animate-container.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"animate-container.d.ts","sourceRoot":"","sources":["../../../components/ui/animate-container.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAGhD,KAAK,aAAa,GAAG,OAAO,GAAG,MAAM,CAAA;AAErC,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,eAAe,CAW7D,CAAA;AAED,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,UAAU,EACV,QAAc,EACd,SAAS,EACT,SAAS,EACT,OAAO,GACR,EAAE,qBAAqB,2CAmCvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../components/ui/chip.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,
|
|
1
|
+
{"version":3,"file":"chip.d.ts","sourceRoot":"","sources":["../../../components/ui/chip.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAA8B,MAAM,OAAO,CAAA;AAIlD,QAAA,MAAM,YAAY;;;;mFAwBjB,CAAA;AAED,KAAK,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GACpD,YAAY,CAAC,OAAO,YAAY,CAAC,GAAG;IAClC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAEH,QAAA,MAAM,IAAI;;;;;;;;;;yBALc,IAAI;yBACJ,IAAI;;wCAgF3B,CAAA;AAGD,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAA;IACzC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;CAC9C,CAAA;AAED,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAqD/C,CAAA;AAED,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,kBAAkB,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quantity-picker.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"quantity-picker.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-picker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,WAAW,mBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5C,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAChC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAmCD,QAAA,MAAM,cAAc,4FA2GnB,CAAA;AAID,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
};
|
|
13
13
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
14
|
import * as React from "react";
|
|
15
|
-
import { cn
|
|
15
|
+
import { cn } from "../../lib/utils";
|
|
16
16
|
import { Icon } from "./icon";
|
|
17
17
|
const IconButton = ({ iconUrl, iconColor, handler, className, style, disabled }) => {
|
|
18
18
|
const visualButtonStyle = Object.assign({}, style);
|
|
@@ -17,7 +17,6 @@ export interface QuantityPickerNEWProps extends React.HTMLAttributes<HTMLDivElem
|
|
|
17
17
|
inputStyle?: React.CSSProperties;
|
|
18
18
|
buttonStyle?: React.CSSProperties;
|
|
19
19
|
buttonCornerRadius?: number;
|
|
20
|
-
debounceTime?: number;
|
|
21
20
|
max?: number;
|
|
22
21
|
parentRef?: React.RefObject<HTMLDivElement>;
|
|
23
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quantity-pickerNEW.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-pickerNEW.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"quantity-pickerNEW.d.ts","sourceRoot":"","sources":["../../../components/ui/quantity-pickerNEW.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,WAAW,sBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5C,eAAe,EAAE,MAAM,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAA;IACxC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAChC,WAAW,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAA;CAC5C;AAgED,QAAA,MAAM,iBAAiB,+FA2KtB,CAAA;AAID,OAAO,EAAE,iBAAiB,EAAE,CAAA"}
|
|
@@ -37,7 +37,7 @@ const IconButton = ({ iconUrl, iconColor, handler, className, style, disabled })
|
|
|
37
37
|
}, disabled: disabled, "aria-label": "Quantity button" })] })));
|
|
38
38
|
};
|
|
39
39
|
const QuantityPickerNEW = React.forwardRef((_a, ref) => {
|
|
40
|
-
var { className, decreaseIconUrl, increaseIconUrl, deleteIconUrl, isDeleteOnly = false, iconColor, onDecreaseClick, onIncreaseClick, isDecreaseDisabled, isIncreaseDisabled, value, onValueSet, onAdjustQuantity, inputStyle, buttonStyle, buttonCornerRadius = 4, max = 99, loading = false,
|
|
40
|
+
var { className, decreaseIconUrl, increaseIconUrl, deleteIconUrl, isDeleteOnly = false, iconColor, onDecreaseClick, onIncreaseClick, isDecreaseDisabled, isIncreaseDisabled, value, onValueSet, onAdjustQuantity, inputStyle, buttonStyle, buttonCornerRadius = 4, max = 99, loading = false, parentRef } = _a, props = __rest(_a, ["className", "decreaseIconUrl", "increaseIconUrl", "deleteIconUrl", "isDeleteOnly", "iconColor", "onDecreaseClick", "onIncreaseClick", "isDecreaseDisabled", "isIncreaseDisabled", "value", "onValueSet", "onAdjustQuantity", "inputStyle", "buttonStyle", "buttonCornerRadius", "max", "loading", "parentRef"]);
|
|
41
41
|
const safeValue = typeof value === "number" && Number.isFinite(value) ? value : 1;
|
|
42
42
|
const inputRef = React.useRef(null);
|
|
43
43
|
const [isFocused, setIsFocused] = React.useState(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-toast.d.ts","sourceRoot":"","sources":["../../../components/ui/use-toast.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-toast.d.ts","sourceRoot":"","sources":["../../../components/ui/use-toast.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC,KAAK,YAAY,GAAG,UAAU,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;CACnD,CAAA;AAED,QAAA,MAAM,WAAW;;;;;CAKP,CAAA;AASV,KAAK,UAAU,GAAG,OAAO,WAAW,CAAA;AAEpC,KAAK,MAAM,GACP;IACE,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAA;IAC7B,KAAK,EAAE,YAAY,CAAA;CACpB,GACD;IACE,IAAI,EAAE,UAAU,CAAC,cAAc,CAAC,CAAA;IAChC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;CAC7B,GACD;IACE,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,CAAA;IACjC,OAAO,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAA;CAC7B,GACD;IACE,IAAI,EAAE,UAAU,CAAC,cAAc,CAAC,CAAA;IAChC,OAAO,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAA;CAC7B,CAAA;AAEL,UAAU,KAAK;IACb,MAAM,EAAE,YAAY,EAAE,CAAA;CACvB;AAoBD,eAAO,MAAM,OAAO,UAAW,KAAK,UAAU,MAAM,KAAG,KAqDtD,CAAA;AAaD,KAAK,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;AAErC,iBAAS,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK;;;oBAGT,YAAY;EAwBpC;AAED,iBAAS,QAAQ;;wBAgBO,MAAM;;EAE7B;AAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wishlist-select.d.ts","sourceRoot":"","sources":["../../../components/ui/wishlist-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAgB,KAAK,
|
|
1
|
+
{"version":3,"file":"wishlist-select.d.ts","sourceRoot":"","sources":["../../../components/ui/wishlist-select.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { addItemToWishlist, cn, createCollectionImageMap, cva, formatRelativeTime, getBackgroundAndPaddingStyle, getBackgroundAndSpacingStyle, getBadgesForProductFn, getBorderSidesStyle, getBorderStyle, getColor, getDestinationHandler, getEnabledWishlistIntegration, getGridSpacing, getIdFromGid, getInputPlaceholderTextProps, getLoyaltyButtonProps, getMarginStyle, getOverlayStyle, getPaddingStyle, getProductGidsFromIds, getSpaceBetween, getTextStyle, getVariantGidsFromIds, getVerticalAlignment, getVerticalAlignmentStyle, getWishlistAddItemNavigation, isFavoriteIntegrationEnabled, isVideo, mapFlexToAlignment, parsePhoneNumber, pluralize, productGidFromId, removeItemFromWishlists, stringRatioToInt, supportedVideoExtensions, supportsMultipleWishlists, throttleFunction, variantGidFromId, formatFrequency, formatPrice, shareContent, } from "./lib/utils";
|
|
2
2
|
export type { WishlistEntryMatch } from "./lib/utils";
|
|
3
3
|
export * from "./lib/cart.util";
|
|
4
|
+
export * from "./lib/discountAllocation.util";
|
|
4
5
|
export * from "./lib/variablesCart.util";
|
|
5
6
|
export * from "./lib/isTapcartVersion20.util";
|
|
6
7
|
export * from "./components/contexts/translation-context";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// component exports
|
|
2
2
|
export { addItemToWishlist, cn, createCollectionImageMap, cva, formatRelativeTime, getBackgroundAndPaddingStyle, getBackgroundAndSpacingStyle, getBadgesForProductFn, getBorderSidesStyle, getBorderStyle, getColor, getDestinationHandler, getEnabledWishlistIntegration, getGridSpacing, getIdFromGid, getInputPlaceholderTextProps, getLoyaltyButtonProps, getMarginStyle, getOverlayStyle, getPaddingStyle, getProductGidsFromIds, getSpaceBetween, getTextStyle, getVariantGidsFromIds, getVerticalAlignment, getVerticalAlignmentStyle, getWishlistAddItemNavigation, isFavoriteIntegrationEnabled, isVideo, mapFlexToAlignment, parsePhoneNumber, pluralize, productGidFromId, removeItemFromWishlists, stringRatioToInt, supportedVideoExtensions, supportsMultipleWishlists, throttleFunction, variantGidFromId, formatFrequency, formatPrice, shareContent, } from "./lib/utils";
|
|
3
3
|
export * from "./lib/cart.util";
|
|
4
|
+
export * from "./lib/discountAllocation.util";
|
|
4
5
|
export * from "./lib/variablesCart.util";
|
|
5
6
|
export * from "./lib/isTapcartVersion20.util";
|
|
6
7
|
export * from "./components/contexts/translation-context";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart.util.d.ts","sourceRoot":"","sources":["../../lib/cart.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,6BAA6B,
|
|
1
|
+
{"version":3,"file":"cart.util.d.ts","sourceRoot":"","sources":["../../lib/cart.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,6BAA6B,EAC7B,cAAc,EACf,MAAM,kBAAkB,CAAA;AAOzB,eAAO,MAAM,eAAe;cAKhB,MAAM;aACP,cAAc;;0BAEH;YAChB,gBAAgB,CAAC,EAAE;gBACjB,MAAM,EAAE,MAAM,CAAA;gBACd,YAAY,EAAE,MAAM,CAAA;aACrB,CAAA;YACD,KAAK,EAAE;gBACL,MAAM,EAAE,MAAM,CAAA;gBACd,YAAY,EAAE,MAAM,CAAA;aACrB,CAAA;YACD,cAAc,CAAC,EAAE;gBACf,MAAM,EAAE,MAAM,CAAA;gBACd,YAAY,EAAE,MAAM,CAAA;aACrB,CAAA;SACF,EAAE;;;;;;;CAgCN,CAAA;AAuCD,eAAO,MAAM,oBAAoB,SAAU,MAAM,QAAQ,YAAY,YAMpE,CAAA;AAED,eAAO,MAAM,kBAAkB,SAAU,MAAM,QAAQ,YAAY,YAMlE,CAAA;AAoHD,MAAM,MAAM,cAAc,GAAG;IAC3B,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,uBAAuB,EAAE,cAUrC,CAAA;AAED,eAAO,MAAM,qBAAqB,SAAU,YAAY,KAAG,cA8B1D,CAAA;AAGD,eAAO,MAAM,mBAAmB;eACnB,MAAM;;;;;aAEG,MAAM;eAAS,MAAM;;;;oBAIN,MAAM;0BAAgB,MAAM;;;eAElD,MAAM;;;;;gBAML,MAAM;cACR,MAAM;;cAEN,MAAM;;;0BAIM;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE;oBAC9C,MAAM;;;;YAsCzB,CAAA"}
|
package/dist/lib/cart.util.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isMerchandiseDiscountAllocation, isShippingDiscountAllocation, } from "./discountAllocation.util";
|
|
1
2
|
import { getIdFromGid } from "./utils";
|
|
2
3
|
export const getVariantPrice = ({ quantity, variant, sellingPlanAllocation = { priceAdjustments: [] }, }) => {
|
|
3
4
|
var _a, _b, _c, _d;
|
|
@@ -27,7 +28,7 @@ export const getVariantPrice = ({ quantity, variant, sellingPlanAllocation = { p
|
|
|
27
28
|
};
|
|
28
29
|
const getOrderLevelDiscounts = (cart) => {
|
|
29
30
|
var _a;
|
|
30
|
-
const discountMap = (_a = cart.discountAllocations) === null || _a === void 0 ? void 0 : _a.filter((discount) => discount
|
|
31
|
+
const discountMap = (_a = cart.discountAllocations) === null || _a === void 0 ? void 0 : _a.filter((discount) => isMerchandiseDiscountAllocation(discount, cart)).reduce((acc, discount) => {
|
|
31
32
|
var _a, _b;
|
|
32
33
|
const code = ((_a = discount.code) !== null && _a !== void 0 ? _a : "unknown").toUpperCase();
|
|
33
34
|
if (!acc[code]) {
|
|
@@ -51,7 +52,7 @@ const getShippingDiscounts = (cart) => {
|
|
|
51
52
|
var _a;
|
|
52
53
|
if (!(cart === null || cart === void 0 ? void 0 : cart.discountAllocations))
|
|
53
54
|
return [];
|
|
54
|
-
return (_a = cart.discountAllocations) === null || _a === void 0 ? void 0 : _a.filter((discount) => discount
|
|
55
|
+
return (_a = cart.discountAllocations) === null || _a === void 0 ? void 0 : _a.filter((discount) => isShippingDiscountAllocation(discount, cart)).map((discount) => {
|
|
55
56
|
var _a;
|
|
56
57
|
return ({
|
|
57
58
|
amount: (_a = discount === null || discount === void 0 ? void 0 : discount.discountedAmount) === null || _a === void 0 ? void 0 : _a.amount,
|
|
@@ -88,6 +88,95 @@ describe("cart-provider.util", () => {
|
|
|
88
88
|
const result = getCalculatedCartData(mockCart);
|
|
89
89
|
expect(result.isFreeShipping).toBe(true);
|
|
90
90
|
});
|
|
91
|
+
it("should exclude shipping discount without targetType from merchandise totals (TICKET-4028)", () => {
|
|
92
|
+
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["CEXFS9P882N"], cost: {
|
|
93
|
+
subtotalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
94
|
+
totalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
95
|
+
totalTaxAmount: { amount: "0.00", currencyCode: "USD" },
|
|
96
|
+
}, items: [
|
|
97
|
+
{
|
|
98
|
+
id: "line1",
|
|
99
|
+
quantity: 1,
|
|
100
|
+
productId: "prod1",
|
|
101
|
+
variantId: "var1",
|
|
102
|
+
productDetails: {},
|
|
103
|
+
variantDetails: {
|
|
104
|
+
compareAtPrice: { amount: "14.00", currencyCode: "USD" },
|
|
105
|
+
price: { amount: "14.00", currencyCode: "USD" },
|
|
106
|
+
},
|
|
107
|
+
discounts: [],
|
|
108
|
+
cost: { totalAmount: { amount: "14.00", currencyCode: "USD" } },
|
|
109
|
+
},
|
|
110
|
+
], discountAllocations: [
|
|
111
|
+
{
|
|
112
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
113
|
+
code: "CEXFS9P882N",
|
|
114
|
+
},
|
|
115
|
+
], appliedGiftCards: [], deliveryGroups: [
|
|
116
|
+
{
|
|
117
|
+
deliveryOptions: [
|
|
118
|
+
{
|
|
119
|
+
title: "Ground Shipping",
|
|
120
|
+
estimatedCost: { amount: "7.95", currencyCode: "USD" },
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
title: "Express Shipping",
|
|
124
|
+
estimatedCost: { amount: "18.95", currencyCode: "USD" },
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
] });
|
|
129
|
+
const result = getCalculatedCartData(mockCart);
|
|
130
|
+
expect(result.orderAndLineItemDiscounts).toHaveLength(0);
|
|
131
|
+
expect(result.discountsTotalAmount).toBe(0);
|
|
132
|
+
expect(result.isFreeShipping).toBe(true);
|
|
133
|
+
expect(result.totalDiscountedPrice).toBe(14);
|
|
134
|
+
});
|
|
135
|
+
it("should exclude shipping from merchandise totals when merch + free ship stack (TICKET-4028)", () => {
|
|
136
|
+
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["SAVE10", "FREESHIP"], cost: {
|
|
137
|
+
subtotalAmount: { amount: "100.00", currencyCode: "USD" },
|
|
138
|
+
totalAmount: { amount: "90.00", currencyCode: "USD" },
|
|
139
|
+
totalTaxAmount: { amount: "0.00", currencyCode: "USD" },
|
|
140
|
+
}, items: [
|
|
141
|
+
{
|
|
142
|
+
id: "line1",
|
|
143
|
+
quantity: 1,
|
|
144
|
+
productId: "prod1",
|
|
145
|
+
variantId: "var1",
|
|
146
|
+
productDetails: {},
|
|
147
|
+
variantDetails: {
|
|
148
|
+
compareAtPrice: { amount: "100.00", currencyCode: "USD" },
|
|
149
|
+
price: { amount: "100.00", currencyCode: "USD" },
|
|
150
|
+
},
|
|
151
|
+
discounts: [],
|
|
152
|
+
cost: { totalAmount: { amount: "100.00", currencyCode: "USD" } },
|
|
153
|
+
},
|
|
154
|
+
], discountAllocations: [
|
|
155
|
+
{
|
|
156
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
157
|
+
code: "SAVE10",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
161
|
+
code: "FREESHIP",
|
|
162
|
+
},
|
|
163
|
+
], deliveryGroups: [
|
|
164
|
+
{
|
|
165
|
+
deliveryOptions: [
|
|
166
|
+
{
|
|
167
|
+
title: "Standard",
|
|
168
|
+
estimatedCost: { amount: "10.00", currencyCode: "USD" },
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
] });
|
|
173
|
+
const result = getCalculatedCartData(mockCart);
|
|
174
|
+
expect(result.orderAndLineItemDiscounts).toHaveLength(1);
|
|
175
|
+
expect(result.orderAndLineItemDiscounts[0].amount).toBe(10);
|
|
176
|
+
expect(result.discountsTotalAmount).toBe(10);
|
|
177
|
+
expect(result.isFreeShipping).toBe(true);
|
|
178
|
+
expect(result.totalDiscountedPrice).toBe(90);
|
|
179
|
+
});
|
|
91
180
|
it("should calculate total discounted price correctly", () => {
|
|
92
181
|
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["Discount10"], items: [
|
|
93
182
|
{
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CartDiscountAllocation } from "app-studio-types";
|
|
2
|
+
type DeliveryOptionCost = {
|
|
3
|
+
estimatedCost?: {
|
|
4
|
+
amount?: string | number | null;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
type CartWithDeliveryGroups = {
|
|
8
|
+
discountAllocations?: CartDiscountAllocation[];
|
|
9
|
+
deliveryGroups?: Array<{
|
|
10
|
+
deliveryOptions?: DeliveryOptionCost[];
|
|
11
|
+
}>;
|
|
12
|
+
cost?: {
|
|
13
|
+
subtotalAmount?: {
|
|
14
|
+
amount?: string | number | null;
|
|
15
|
+
};
|
|
16
|
+
totalAmount?: {
|
|
17
|
+
amount?: string | number | null;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare const isShippingDiscountAllocation: (allocation: CartDiscountAllocation, cart?: CartWithDeliveryGroups | null) => boolean;
|
|
22
|
+
export declare const isMerchandiseDiscountAllocation: (allocation: CartDiscountAllocation, cart?: CartWithDeliveryGroups | null) => boolean;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=discountAllocation.util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discountAllocation.util.d.ts","sourceRoot":"","sources":["../../lib/discountAllocation.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAEvB,MAAM,kBAAkB,CAAA;AAEzB,KAAK,kBAAkB,GAAG;IACxB,aAAa,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CACpD,CAAA;AAED,KAAK,sBAAsB,GAAG;IAC5B,mBAAmB,CAAC,EAAE,sBAAsB,EAAE,CAAA;IAC9C,cAAc,CAAC,EAAE,KAAK,CAAC;QACrB,eAAe,CAAC,EAAE,kBAAkB,EAAE,CAAA;KACvC,CAAC,CAAA;IACF,IAAI,CAAC,EAAE;QACL,cAAc,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;QACpD,WAAW,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KAClD,CAAA;CACF,CAAA;AAkMD,eAAO,MAAM,4BAA4B,eAC3B,sBAAsB,SAC3B,sBAAsB,GAAG,IAAI,KACnC,OAAiE,CAAA;AAEpE,eAAO,MAAM,+BAA+B,eAC9B,sBAAsB,SAC3B,sBAAsB,GAAG,IAAI,KACnC,OAAoE,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { DiscountApplicationTargetType, } from "app-studio-types";
|
|
2
|
+
const MONEY_EPSILON = 0.01;
|
|
3
|
+
const parseAmount = (amount) => {
|
|
4
|
+
if (amount === undefined || amount === null || amount === "")
|
|
5
|
+
return 0;
|
|
6
|
+
const parsed = parseFloat(String(amount));
|
|
7
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
8
|
+
};
|
|
9
|
+
const approxEqual = (a, b, epsilon = MONEY_EPSILON) => Math.abs(a - b) <= epsilon;
|
|
10
|
+
const getDeliveryOptionAmounts = (cart) => {
|
|
11
|
+
var _a;
|
|
12
|
+
if (!((_a = cart === null || cart === void 0 ? void 0 : cart.deliveryGroups) === null || _a === void 0 ? void 0 : _a.length))
|
|
13
|
+
return [];
|
|
14
|
+
return cart.deliveryGroups.flatMap((group) => {
|
|
15
|
+
var _a;
|
|
16
|
+
return ((_a = group.deliveryOptions) !== null && _a !== void 0 ? _a : []).map((option) => { var _a; return parseAmount((_a = option.estimatedCost) === null || _a === void 0 ? void 0 : _a.amount); });
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const allocationMatchesDeliveryOptionCost = (allocation, cart) => {
|
|
20
|
+
var _a;
|
|
21
|
+
const allocationAmount = parseAmount((_a = allocation === null || allocation === void 0 ? void 0 : allocation.discountedAmount) === null || _a === void 0 ? void 0 : _a.amount);
|
|
22
|
+
if (!allocationAmount)
|
|
23
|
+
return false;
|
|
24
|
+
return getDeliveryOptionAmounts(cart).some((deliveryCost) => approxEqual(deliveryCost, allocationAmount));
|
|
25
|
+
};
|
|
26
|
+
const inferShippingWhenTargetTypeMissing = (allocation, cart) => {
|
|
27
|
+
var _a, _b, _c, _d, _e, _f;
|
|
28
|
+
if (!((_a = cart === null || cart === void 0 ? void 0 : cart.deliveryGroups) === null || _a === void 0 ? void 0 : _a.length))
|
|
29
|
+
return false;
|
|
30
|
+
const allocationAmount = parseAmount((_b = allocation === null || allocation === void 0 ? void 0 : allocation.discountedAmount) === null || _b === void 0 ? void 0 : _b.amount);
|
|
31
|
+
if (!allocationAmount)
|
|
32
|
+
return false;
|
|
33
|
+
const deliveryAmounts = getDeliveryOptionAmounts(cart);
|
|
34
|
+
if (!deliveryAmounts.length)
|
|
35
|
+
return false;
|
|
36
|
+
const subtotal = parseAmount((_d = (_c = cart === null || cart === void 0 ? void 0 : cart.cost) === null || _c === void 0 ? void 0 : _c.subtotalAmount) === null || _d === void 0 ? void 0 : _d.amount);
|
|
37
|
+
const total = parseAmount((_f = (_e = cart === null || cart === void 0 ? void 0 : cart.cost) === null || _e === void 0 ? void 0 : _e.totalAmount) === null || _f === void 0 ? void 0 : _f.amount);
|
|
38
|
+
if (subtotal > 0 && total > 0) {
|
|
39
|
+
if (approxEqual(total, subtotal - allocationAmount))
|
|
40
|
+
return false;
|
|
41
|
+
if (approxEqual(total, subtotal) &&
|
|
42
|
+
deliveryAmounts.some((cost) => approxEqual(cost, allocationAmount))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (total > subtotal + MONEY_EPSILON &&
|
|
46
|
+
deliveryAmounts.some((cost) => approxEqual(cost, allocationAmount))) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Merchandise discounts reduce total below subtotal; unchanged total implies
|
|
50
|
+
// shipping-side allocation (e.g. FREESHIPPING with zeroed delivery rates).
|
|
51
|
+
if (approxEqual(total, subtotal) && allocationAmount > 0) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return allocationMatchesDeliveryOptionCost(allocation, cart);
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* When multiple allocations omit targetType (stacked merch + free ship), per-allocation
|
|
60
|
+
* inference misclassifies shipping as merchandise. Partition using merchBudget =
|
|
61
|
+
* subtotal − total (Shopify subtotalAmount is merchandise-only).
|
|
62
|
+
*/
|
|
63
|
+
const partitionAmbiguousAllocations = (ambiguous, cart) => {
|
|
64
|
+
var _a, _b, _c, _d;
|
|
65
|
+
const result = new Map();
|
|
66
|
+
const subtotal = parseAmount((_b = (_a = cart === null || cart === void 0 ? void 0 : cart.cost) === null || _a === void 0 ? void 0 : _a.subtotalAmount) === null || _b === void 0 ? void 0 : _b.amount);
|
|
67
|
+
const total = parseAmount((_d = (_c = cart === null || cart === void 0 ? void 0 : cart.cost) === null || _c === void 0 ? void 0 : _c.totalAmount) === null || _d === void 0 ? void 0 : _d.amount);
|
|
68
|
+
const merchBudget = subtotal > 0 && total > 0 ? Math.max(0, subtotal - total) : 0;
|
|
69
|
+
const items = ambiguous.map((allocation) => {
|
|
70
|
+
var _a;
|
|
71
|
+
return ({
|
|
72
|
+
allocation,
|
|
73
|
+
amount: parseAmount((_a = allocation === null || allocation === void 0 ? void 0 : allocation.discountedAmount) === null || _a === void 0 ? void 0 : _a.amount),
|
|
74
|
+
matchesDelivery: allocationMatchesDeliveryOptionCost(allocation, cart),
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
let assignedMerch = 0;
|
|
78
|
+
for (const item of items) {
|
|
79
|
+
if (!item.matchesDelivery) {
|
|
80
|
+
result.set(item.allocation, "merchandise");
|
|
81
|
+
assignedMerch += item.amount;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const item of items.filter((i) => i.matchesDelivery)) {
|
|
85
|
+
if (assignedMerch + item.amount <= merchBudget + MONEY_EPSILON) {
|
|
86
|
+
result.set(item.allocation, "merchandise");
|
|
87
|
+
assignedMerch += item.amount;
|
|
88
|
+
}
|
|
89
|
+
else if (assignedMerch >= merchBudget - MONEY_EPSILON ||
|
|
90
|
+
assignedMerch + item.amount > merchBudget + MONEY_EPSILON) {
|
|
91
|
+
result.set(item.allocation, "shipping");
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
result.set(item.allocation, "merchandise");
|
|
95
|
+
assignedMerch += item.amount;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
100
|
+
const kindMapCache = new WeakMap();
|
|
101
|
+
const buildAllocationKindMap = (cart) => {
|
|
102
|
+
const allocations = cart === null || cart === void 0 ? void 0 : cart.discountAllocations;
|
|
103
|
+
if (!Array.isArray(allocations) || !allocations.length)
|
|
104
|
+
return null;
|
|
105
|
+
const cached = kindMapCache.get(cart);
|
|
106
|
+
if (cached)
|
|
107
|
+
return cached;
|
|
108
|
+
const map = new Map();
|
|
109
|
+
const ambiguous = [];
|
|
110
|
+
for (const allocation of allocations) {
|
|
111
|
+
if ((allocation === null || allocation === void 0 ? void 0 : allocation.targetType) === DiscountApplicationTargetType.ShippingLine) {
|
|
112
|
+
map.set(allocation, "shipping");
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if ((allocation === null || allocation === void 0 ? void 0 : allocation.targetType) != null) {
|
|
116
|
+
map.set(allocation, "merchandise");
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
ambiguous.push(allocation);
|
|
120
|
+
}
|
|
121
|
+
if (ambiguous.length === 1) {
|
|
122
|
+
const allocation = ambiguous[0];
|
|
123
|
+
map.set(allocation, inferShippingWhenTargetTypeMissing(allocation, cart)
|
|
124
|
+
? "shipping"
|
|
125
|
+
: "merchandise");
|
|
126
|
+
}
|
|
127
|
+
else if (ambiguous.length > 1) {
|
|
128
|
+
const partitioned = partitionAmbiguousAllocations(ambiguous, cart);
|
|
129
|
+
partitioned.forEach((kind, allocation) => map.set(allocation, kind));
|
|
130
|
+
}
|
|
131
|
+
kindMapCache.set(cart, map);
|
|
132
|
+
return map;
|
|
133
|
+
};
|
|
134
|
+
const resolveAllocationKind = (allocation, cart) => {
|
|
135
|
+
if ((allocation === null || allocation === void 0 ? void 0 : allocation.targetType) === DiscountApplicationTargetType.ShippingLine) {
|
|
136
|
+
return "shipping";
|
|
137
|
+
}
|
|
138
|
+
if ((allocation === null || allocation === void 0 ? void 0 : allocation.targetType) != null) {
|
|
139
|
+
return "merchandise";
|
|
140
|
+
}
|
|
141
|
+
const kindMap = buildAllocationKindMap(cart);
|
|
142
|
+
const kind = kindMap === null || kindMap === void 0 ? void 0 : kindMap.get(allocation);
|
|
143
|
+
if (kind)
|
|
144
|
+
return kind;
|
|
145
|
+
return inferShippingWhenTargetTypeMissing(allocation, cart)
|
|
146
|
+
? "shipping"
|
|
147
|
+
: "merchandise";
|
|
148
|
+
};
|
|
149
|
+
export const isShippingDiscountAllocation = (allocation, cart) => resolveAllocationKind(allocation, cart) === "shipping";
|
|
150
|
+
export const isMerchandiseDiscountAllocation = (allocation, cart) => resolveAllocationKind(allocation, cart) === "merchandise";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discountAllocation.util.test.d.ts","sourceRoot":"","sources":["../../lib/discountAllocation.util.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { DiscountApplicationTargetType, } from "app-studio-types";
|
|
2
|
+
import { isMerchandiseDiscountAllocation, isShippingDiscountAllocation, } from "./discountAllocation.util";
|
|
3
|
+
const naturalLifeDeliveryGroups = [
|
|
4
|
+
{
|
|
5
|
+
deliveryOptions: [
|
|
6
|
+
{
|
|
7
|
+
title: "Ground Shipping",
|
|
8
|
+
estimatedCost: { amount: "7.95", currencyCode: "USD" },
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
title: "Express Shipping",
|
|
12
|
+
estimatedCost: { amount: "18.95", currencyCode: "USD" },
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
const naturalLifeFreeShippingCost = {
|
|
18
|
+
subtotalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
19
|
+
totalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
20
|
+
};
|
|
21
|
+
describe("discountAllocation.util", () => {
|
|
22
|
+
describe("isShippingDiscountAllocation", () => {
|
|
23
|
+
it("returns true when targetType is SHIPPING_LINE", () => {
|
|
24
|
+
const allocation = {
|
|
25
|
+
targetType: DiscountApplicationTargetType.ShippingLine,
|
|
26
|
+
discountedAmount: { amount: "5.00", currencyCode: "USD" },
|
|
27
|
+
};
|
|
28
|
+
expect(isShippingDiscountAllocation(allocation)).toBe(true);
|
|
29
|
+
expect(isMerchandiseDiscountAllocation(allocation)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it("returns false when targetType is LINE_ITEM", () => {
|
|
32
|
+
const allocation = {
|
|
33
|
+
targetType: DiscountApplicationTargetType.LineItem,
|
|
34
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
35
|
+
};
|
|
36
|
+
expect(isShippingDiscountAllocation(allocation)).toBe(false);
|
|
37
|
+
expect(isMerchandiseDiscountAllocation(allocation)).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
it("infers shipping when targetType is missing and amount matches a delivery option (TICKET-4028)", () => {
|
|
40
|
+
const allocation = {
|
|
41
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
42
|
+
code: "CEXFS9P882N",
|
|
43
|
+
};
|
|
44
|
+
const cart = {
|
|
45
|
+
deliveryGroups: naturalLifeDeliveryGroups,
|
|
46
|
+
cost: naturalLifeFreeShippingCost,
|
|
47
|
+
};
|
|
48
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(true);
|
|
49
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it("infers shipping without cart.cost when amount matches delivery (legacy fallback)", () => {
|
|
52
|
+
const allocation = {
|
|
53
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
54
|
+
code: "CEXFS9P882N",
|
|
55
|
+
};
|
|
56
|
+
const cart = { deliveryGroups: naturalLifeDeliveryGroups };
|
|
57
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(true);
|
|
58
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
it("treats missing targetType as merchandise when amount equals shipping but total dropped vs subtotal", () => {
|
|
61
|
+
const allocation = {
|
|
62
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
63
|
+
code: "SAVE795",
|
|
64
|
+
};
|
|
65
|
+
const cart = {
|
|
66
|
+
deliveryGroups: naturalLifeDeliveryGroups,
|
|
67
|
+
cost: {
|
|
68
|
+
subtotalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
69
|
+
totalAmount: { amount: "6.05", currencyCode: "USD" },
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(false);
|
|
73
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
it("treats missing targetType as merchandise when amount is less than shipping and total exceeds subtotal", () => {
|
|
76
|
+
const allocation = {
|
|
77
|
+
discountedAmount: { amount: "5.00", currencyCode: "USD" },
|
|
78
|
+
code: "SAVE5",
|
|
79
|
+
};
|
|
80
|
+
const cart = {
|
|
81
|
+
deliveryGroups: naturalLifeDeliveryGroups,
|
|
82
|
+
cost: {
|
|
83
|
+
subtotalAmount: { amount: "50.00", currencyCode: "USD" },
|
|
84
|
+
totalAmount: { amount: "52.95", currencyCode: "USD" },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(false);
|
|
88
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
it("infers shipping when total exceeds subtotal and amount exactly matches delivery cost", () => {
|
|
91
|
+
const allocation = {
|
|
92
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
93
|
+
code: "FREESHIP",
|
|
94
|
+
};
|
|
95
|
+
const cart = {
|
|
96
|
+
deliveryGroups: naturalLifeDeliveryGroups,
|
|
97
|
+
cost: {
|
|
98
|
+
subtotalAmount: { amount: "50.00", currencyCode: "USD" },
|
|
99
|
+
totalAmount: { amount: "57.95", currencyCode: "USD" },
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(true);
|
|
103
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
it("infers shipping when total equals subtotal despite delivery cost mismatch (FREESHIPPING zeroed rates)", () => {
|
|
106
|
+
const allocation = {
|
|
107
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
108
|
+
code: "FREESHIPPING",
|
|
109
|
+
};
|
|
110
|
+
const cart = {
|
|
111
|
+
deliveryGroups: [
|
|
112
|
+
{
|
|
113
|
+
deliveryOptions: [
|
|
114
|
+
{
|
|
115
|
+
title: "Standard",
|
|
116
|
+
estimatedCost: { amount: "0.0", currencyCode: "USD" },
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
title: "Express Shipping",
|
|
120
|
+
estimatedCost: { amount: "0.0", currencyCode: "USD" },
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
cost: {
|
|
126
|
+
subtotalAmount: { amount: "50.0", currencyCode: "USD" },
|
|
127
|
+
totalAmount: { amount: "50.0", currencyCode: "USD" },
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(true);
|
|
131
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
it("infers shipping for single allocation when total equals subtotal (unchanged merchandise total)", () => {
|
|
134
|
+
const allocation = {
|
|
135
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
136
|
+
code: "SAVE10",
|
|
137
|
+
};
|
|
138
|
+
const cart = {
|
|
139
|
+
deliveryGroups: naturalLifeDeliveryGroups,
|
|
140
|
+
cost: naturalLifeFreeShippingCost,
|
|
141
|
+
};
|
|
142
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(true);
|
|
143
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
it("treats missing targetType as merchandise when cart has no deliveryGroups", () => {
|
|
146
|
+
const allocation = {
|
|
147
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
148
|
+
code: "CEXFS9P882N",
|
|
149
|
+
};
|
|
150
|
+
expect(isShippingDiscountAllocation(allocation, {})).toBe(false);
|
|
151
|
+
expect(isMerchandiseDiscountAllocation(allocation, {})).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
it("treats missing targetType as merchandise when deliveryGroups is empty (pre-checkout)", () => {
|
|
154
|
+
const allocation = {
|
|
155
|
+
discountedAmount: { amount: "7.95", currencyCode: "GBP" },
|
|
156
|
+
code: "PROMO",
|
|
157
|
+
};
|
|
158
|
+
const cart = {
|
|
159
|
+
deliveryGroups: [],
|
|
160
|
+
cost: {
|
|
161
|
+
subtotalAmount: { amount: "5.7", currencyCode: "GBP" },
|
|
162
|
+
totalAmount: { amount: "5.7", currencyCode: "GBP" },
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
expect(isShippingDiscountAllocation(allocation, cart)).toBe(false);
|
|
166
|
+
expect(isMerchandiseDiscountAllocation(allocation, cart)).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
it("partitions stacked $10 off + $10 free ship when targetType is missing (TICKET-4028)", () => {
|
|
169
|
+
const merchAllocation = {
|
|
170
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
171
|
+
code: "SAVE10",
|
|
172
|
+
};
|
|
173
|
+
const shipAllocation = {
|
|
174
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
175
|
+
code: "FREESHIP",
|
|
176
|
+
};
|
|
177
|
+
const cart = {
|
|
178
|
+
discountAllocations: [merchAllocation, shipAllocation],
|
|
179
|
+
deliveryGroups: [
|
|
180
|
+
{
|
|
181
|
+
deliveryOptions: [
|
|
182
|
+
{
|
|
183
|
+
title: "Standard",
|
|
184
|
+
estimatedCost: { amount: "10.00", currencyCode: "USD" },
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
cost: {
|
|
190
|
+
subtotalAmount: { amount: "100.00", currencyCode: "USD" },
|
|
191
|
+
totalAmount: { amount: "90.00", currencyCode: "USD" },
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
expect(isMerchandiseDiscountAllocation(merchAllocation, cart)).toBe(true);
|
|
195
|
+
expect(isShippingDiscountAllocation(merchAllocation, cart)).toBe(false);
|
|
196
|
+
expect(isShippingDiscountAllocation(shipAllocation, cart)).toBe(true);
|
|
197
|
+
expect(isMerchandiseDiscountAllocation(shipAllocation, cart)).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"variablesCart.util.d.ts","sourceRoot":"","sources":["../../lib/variablesCart.util.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,6BAA6B,EAE9B,MAAM,kBAAkB,CAAA;AAMzB,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;AA4FD,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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { DiscountApplicationTargetType, } from "app-studio-types";
|
|
1
|
+
import { isMerchandiseDiscountAllocation, isShippingDiscountAllocation, } from "./discountAllocation.util";
|
|
3
2
|
export const getSubscriptionPriceDetails = ({ totalAmount, quantity, sellingPlanAllocation = { priceAdjustments: [] }, }) => {
|
|
4
3
|
var _a;
|
|
5
4
|
const isSubscription = ((_a = sellingPlanAllocation === null || sellingPlanAllocation === void 0 ? void 0 : sellingPlanAllocation.priceAdjustments) === null || _a === void 0 ? void 0 : _a.length) > 0;
|
|
@@ -34,7 +33,7 @@ const getOrderLevelDiscounts = (cart) => {
|
|
|
34
33
|
return [];
|
|
35
34
|
// We'll build a map of code -> { amount, isAutomatic }
|
|
36
35
|
const discountMap = discountAllocations
|
|
37
|
-
.filter((discount) => discount
|
|
36
|
+
.filter((discount) => isMerchandiseDiscountAllocation(discount, cart))
|
|
38
37
|
.reduce((acc, discount) => {
|
|
39
38
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
40
39
|
// Figure out the code for this discount
|
|
@@ -87,7 +86,7 @@ const getShippingDiscounts = (cart) => {
|
|
|
87
86
|
if (!discountAllocations)
|
|
88
87
|
return [];
|
|
89
88
|
return discountAllocations
|
|
90
|
-
.filter((discount) => discount
|
|
89
|
+
.filter((discount) => isShippingDiscountAllocation(discount, cart))
|
|
91
90
|
.map((discount) => {
|
|
92
91
|
var _a;
|
|
93
92
|
return ({
|
|
@@ -103,6 +103,91 @@ describe("cart-provider.util", () => {
|
|
|
103
103
|
const result = getVariablesCalculatedCartData(mockCart);
|
|
104
104
|
expect(result.isFreeShipping).toBe(true);
|
|
105
105
|
});
|
|
106
|
+
it("should exclude shipping discount without targetType from merchandise totals (TICKET-4028)", () => {
|
|
107
|
+
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["CEXFS9P882N"], cost: {
|
|
108
|
+
subtotalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
109
|
+
totalAmount: { amount: "14.00", currencyCode: "USD" },
|
|
110
|
+
totalTaxAmount: { amount: "0.00", currencyCode: "USD" },
|
|
111
|
+
}, items: [
|
|
112
|
+
{
|
|
113
|
+
id: "line1",
|
|
114
|
+
quantity: 1,
|
|
115
|
+
productId: "prod1",
|
|
116
|
+
variantId: "var1",
|
|
117
|
+
price: 14.0,
|
|
118
|
+
compareAtPrice: 14.0,
|
|
119
|
+
currencyCode: "USD",
|
|
120
|
+
discounts: [],
|
|
121
|
+
cost: { totalAmount: { amount: "14.00", currencyCode: "USD" } },
|
|
122
|
+
},
|
|
123
|
+
], discountAllocations: [
|
|
124
|
+
{
|
|
125
|
+
discountedAmount: { amount: "7.95", currencyCode: "USD" },
|
|
126
|
+
code: "CEXFS9P882N",
|
|
127
|
+
},
|
|
128
|
+
], deliveryGroups: [
|
|
129
|
+
{
|
|
130
|
+
deliveryOptions: [
|
|
131
|
+
{
|
|
132
|
+
title: "Ground Shipping",
|
|
133
|
+
estimatedCost: { amount: "7.95", currencyCode: "USD" },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: "Express Shipping",
|
|
137
|
+
estimatedCost: { amount: "18.95", currencyCode: "USD" },
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
] });
|
|
142
|
+
const result = getVariablesCalculatedCartData(mockCart);
|
|
143
|
+
expect(result.orderAndLineItemDiscounts).toHaveLength(0);
|
|
144
|
+
expect(result.discountsTotalAmount).toBe(0);
|
|
145
|
+
expect(result.isFreeShipping).toBe(true);
|
|
146
|
+
expect(result.totalDiscountedPrice).toBe(14);
|
|
147
|
+
});
|
|
148
|
+
it("should exclude shipping from merchandise totals when merch + free ship stack (TICKET-4028)", () => {
|
|
149
|
+
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["SAVE10", "FREESHIP"], cost: {
|
|
150
|
+
subtotalAmount: { amount: "100.00", currencyCode: "USD" },
|
|
151
|
+
totalAmount: { amount: "90.00", currencyCode: "USD" },
|
|
152
|
+
totalTaxAmount: { amount: "0.00", currencyCode: "USD" },
|
|
153
|
+
}, items: [
|
|
154
|
+
{
|
|
155
|
+
id: "line1",
|
|
156
|
+
quantity: 1,
|
|
157
|
+
productId: "prod1",
|
|
158
|
+
variantId: "var1",
|
|
159
|
+
price: 100.0,
|
|
160
|
+
compareAtPrice: 100.0,
|
|
161
|
+
currencyCode: "USD",
|
|
162
|
+
discounts: [],
|
|
163
|
+
cost: { totalAmount: { amount: "100.00", currencyCode: "USD" } },
|
|
164
|
+
},
|
|
165
|
+
], discountAllocations: [
|
|
166
|
+
{
|
|
167
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
168
|
+
code: "SAVE10",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
discountedAmount: { amount: "10.00", currencyCode: "USD" },
|
|
172
|
+
code: "FREESHIP",
|
|
173
|
+
},
|
|
174
|
+
], deliveryGroups: [
|
|
175
|
+
{
|
|
176
|
+
deliveryOptions: [
|
|
177
|
+
{
|
|
178
|
+
title: "Standard",
|
|
179
|
+
estimatedCost: { amount: "10.00", currencyCode: "USD" },
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
] });
|
|
184
|
+
const result = getVariablesCalculatedCartData(mockCart);
|
|
185
|
+
expect(result.orderAndLineItemDiscounts).toHaveLength(1);
|
|
186
|
+
expect(result.orderAndLineItemDiscounts[0].amount).toBe(10);
|
|
187
|
+
expect(result.discountsTotalAmount).toBe(10);
|
|
188
|
+
expect(result.isFreeShipping).toBe(true);
|
|
189
|
+
expect(result.totalDiscountedPrice).toBe(90);
|
|
190
|
+
});
|
|
106
191
|
it("should calculate total discounted price correctly", () => {
|
|
107
192
|
const mockCart = Object.assign(Object.assign({}, baseCartData), { discounts: ["Discount10"], items: [
|
|
108
193
|
{
|
package/dist/styles.css
CHANGED