@searchspring/snap-controller 0.65.2 → 0.66.1
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/cjs/Autocomplete/AutocompleteController.d.ts +2 -1
- package/dist/cjs/Autocomplete/AutocompleteController.d.ts.map +1 -1
- package/dist/cjs/Autocomplete/AutocompleteController.js +35 -26
- package/dist/cjs/Recommendation/RecommendationController.d.ts +2 -3
- package/dist/cjs/Recommendation/RecommendationController.d.ts.map +1 -1
- package/dist/cjs/Recommendation/RecommendationController.js +39 -31
- package/dist/cjs/Search/SearchController.d.ts +1 -1
- package/dist/cjs/Search/SearchController.d.ts.map +1 -1
- package/dist/cjs/Search/SearchController.js +106 -74
- package/dist/cjs/utils/isClickWithinProductLink.d.ts +5 -0
- package/dist/cjs/utils/isClickWithinProductLink.d.ts.map +1 -0
- package/dist/cjs/utils/isClickWithinProductLink.js +23 -0
- package/dist/esm/Autocomplete/AutocompleteController.d.ts +2 -1
- package/dist/esm/Autocomplete/AutocompleteController.d.ts.map +1 -1
- package/dist/esm/Autocomplete/AutocompleteController.js +30 -22
- package/dist/esm/Recommendation/RecommendationController.d.ts +2 -3
- package/dist/esm/Recommendation/RecommendationController.d.ts.map +1 -1
- package/dist/esm/Recommendation/RecommendationController.js +30 -26
- package/dist/esm/Search/SearchController.d.ts +1 -1
- package/dist/esm/Search/SearchController.d.ts.map +1 -1
- package/dist/esm/Search/SearchController.js +91 -60
- package/dist/esm/utils/isClickWithinProductLink.d.ts +5 -0
- package/dist/esm/utils/isClickWithinProductLink.d.ts.map +1 -0
- package/dist/esm/utils/isClickWithinProductLink.js +18 -0
- package/package.json +10 -10
|
@@ -2,6 +2,7 @@ import deepmerge from 'deepmerge';
|
|
|
2
2
|
import { ErrorType } from '@searchspring/snap-store-mobx';
|
|
3
3
|
import { AbstractController } from '../Abstract/AbstractController';
|
|
4
4
|
import { ControllerTypes } from '../types';
|
|
5
|
+
import { CLICK_DUPLICATION_TIMEOUT, isClickWithinProductLink } from '../utils/isClickWithinProductLink';
|
|
5
6
|
const defaultConfig = {
|
|
6
7
|
id: 'recommend',
|
|
7
8
|
tag: '',
|
|
@@ -25,17 +26,21 @@ export class RecommendationController extends AbstractController {
|
|
|
25
26
|
this.tracker.events.recommendations.clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
26
27
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
27
28
|
this.events.product[result.id].clickThrough = true;
|
|
28
|
-
this.eventManager.fire('track.product.clickThrough', { controller: this, event: e,
|
|
29
|
+
this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data });
|
|
29
30
|
},
|
|
30
31
|
click: (e, result) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (href) {
|
|
34
|
-
this.track.product.clickThrough(e, result);
|
|
32
|
+
if (this.events.product[result.id]?.click) {
|
|
33
|
+
return;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
if (result.type === 'banner') {
|
|
36
|
+
return;
|
|
38
37
|
}
|
|
38
|
+
isClickWithinProductLink(e, result) && this.track.product.clickThrough(e, result);
|
|
39
|
+
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
40
|
+
this.events.product[result.id].click = true;
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
this.events.product[result.id].click = false;
|
|
43
|
+
}, CLICK_DUPLICATION_TIMEOUT);
|
|
39
44
|
},
|
|
40
45
|
impression: (result) => {
|
|
41
46
|
if (this.events.product[result.id]?.impression)
|
|
@@ -44,7 +49,7 @@ export class RecommendationController extends AbstractController {
|
|
|
44
49
|
this.tracker.events.recommendations.impression({ data, siteId: this.config.globals?.siteId });
|
|
45
50
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
46
51
|
this.events.product[result.id].impression = true;
|
|
47
|
-
this.eventManager.fire('track.product.impression', { controller: this,
|
|
52
|
+
this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data });
|
|
48
53
|
return data;
|
|
49
54
|
},
|
|
50
55
|
render: (result) => {
|
|
@@ -54,23 +59,13 @@ export class RecommendationController extends AbstractController {
|
|
|
54
59
|
this.tracker.events.recommendations.render({ data, siteId: this.config.globals?.siteId });
|
|
55
60
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
56
61
|
this.events.product[result.id].render = true;
|
|
57
|
-
this.eventManager.fire('track.product.render', { controller: this,
|
|
62
|
+
this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
|
|
58
63
|
return data;
|
|
59
64
|
},
|
|
60
65
|
addToCart: (result) => {
|
|
61
66
|
const data = getRecommendationsAddtocartSchemaData({ store: this.store, results: [result] });
|
|
62
67
|
this.tracker.events.recommendations.addToCart({ data, siteId: this.config.globals?.siteId });
|
|
63
|
-
this.eventManager.fire('track.product.addToCart', { controller: this,
|
|
64
|
-
return data;
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
bundle: {
|
|
68
|
-
addToCart: (results) => {
|
|
69
|
-
if (this.store.profile.type != 'bundle')
|
|
70
|
-
return;
|
|
71
|
-
const data = getRecommendationsAddtocartSchemaData({ store: this.store, results });
|
|
72
|
-
this.tracker.events.recommendations.addToCart({ data, siteId: this.config.globals?.siteId });
|
|
73
|
-
this.eventManager.fire('track.bundle.addToCart', { controller: this, products: results, trackEvent: data });
|
|
68
|
+
this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data });
|
|
74
69
|
return data;
|
|
75
70
|
},
|
|
76
71
|
},
|
|
@@ -194,6 +189,15 @@ export class RecommendationController extends AbstractController {
|
|
|
194
189
|
this.store.loading = false;
|
|
195
190
|
}
|
|
196
191
|
};
|
|
192
|
+
this.addToCart = async (_products) => {
|
|
193
|
+
const products = typeof _products.slice == 'function' ? _products.slice() : [_products];
|
|
194
|
+
products.forEach((product) => {
|
|
195
|
+
this.track.product.addToCart(product);
|
|
196
|
+
});
|
|
197
|
+
if (products.length > 0) {
|
|
198
|
+
this.eventManager.fire('addToCart', { controller: this, products });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
197
201
|
if (!config.tag) {
|
|
198
202
|
throw new Error(`Invalid config passed to RecommendationController. The "tag" attribute is required.`);
|
|
199
203
|
}
|
|
@@ -220,8 +224,8 @@ export class RecommendationController extends AbstractController {
|
|
|
220
224
|
products.forEach((result) => {
|
|
221
225
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
222
226
|
this.events.product[result.id].render = true;
|
|
227
|
+
this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
|
|
223
228
|
});
|
|
224
|
-
this.eventManager.fire('track.product.render', { controller: this, products, trackEvent: data });
|
|
225
229
|
}
|
|
226
230
|
});
|
|
227
231
|
// add 'afterStore' middleware
|
|
@@ -269,9 +273,9 @@ function getRecommendationsAddtocartSchemaData({ store, results, }) {
|
|
|
269
273
|
results: results?.map((result) => {
|
|
270
274
|
const core = result.mappings.core;
|
|
271
275
|
return {
|
|
272
|
-
uid: core
|
|
273
|
-
sku: core
|
|
274
|
-
price: Number(core
|
|
276
|
+
uid: core?.uid || '',
|
|
277
|
+
sku: core?.sku,
|
|
278
|
+
price: Number(core?.price),
|
|
275
279
|
qty: result.quantity || 1,
|
|
276
280
|
};
|
|
277
281
|
}) || [],
|
|
@@ -282,11 +286,11 @@ function getRecommendationsSchemaData({ store, results }) {
|
|
|
282
286
|
tag: store.profile.tag,
|
|
283
287
|
results: results?.map((result) => {
|
|
284
288
|
const core = result.mappings.core;
|
|
289
|
+
const position = result.position;
|
|
285
290
|
return {
|
|
291
|
+
position,
|
|
286
292
|
uid: core.uid || '',
|
|
287
|
-
// childUid: core.uid,
|
|
288
293
|
sku: core.sku,
|
|
289
|
-
// childSku: core.sku,
|
|
290
294
|
};
|
|
291
295
|
}) || [],
|
|
292
296
|
};
|
|
@@ -26,7 +26,7 @@ export declare class SearchController extends AbstractController {
|
|
|
26
26
|
track: SearchTrackMethods;
|
|
27
27
|
get params(): SearchRequestModel;
|
|
28
28
|
search: () => Promise<void>;
|
|
29
|
-
addToCart: (
|
|
29
|
+
addToCart: (_products: Product[] | Product) => Promise<void>;
|
|
30
30
|
}
|
|
31
31
|
export declare function getStorableRequestParams(request: SearchRequestModel): SearchRequestModel;
|
|
32
32
|
export declare function generateHrefSelector(element: HTMLElement, href: string, levels?: number): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchController.d.ts","sourceRoot":"","sources":["../../../src/Search/SearchController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAa,MAAM,+BAA+B,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAClF,OAAO,KAAK,EACX,sBAAsB,EAGtB,kBAAkB,EAClB,gBAAgB,EAIhB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EACX,kBAAkB,
|
|
1
|
+
{"version":3,"file":"SearchController.d.ts","sourceRoot":"","sources":["../../../src/Search/SearchController.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAa,MAAM,+BAA+B,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAClF,OAAO,KAAK,EACX,sBAAsB,EAGtB,kBAAkB,EAClB,gBAAgB,EAIhB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EACX,kBAAkB,EAQlB,MAAM,2BAA2B,CAAC;AAiCnC,KAAK,kBAAkB,GAAG;IACzB,OAAO,EAAE;QACR,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACvD,KAAK,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;QACzD,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QAClC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;QACtC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;KACtC,CAAC;IACF,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAGF,qBAAa,gBAAiB,SAAQ,kBAAkB;IAChD,IAAI,kBAA0B;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,sBAAsB,CAAC;IACvC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,MAAM,CAUM;gBAGnB,MAAM,EAAE,sBAAsB,EAC9B,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IAuN3B,KAAK,EAAE,kBAAkB,CA+FvB;IAEF,IAAI,MAAM,IAAI,kBAAkB,CA8C/B;IAED,MAAM,QAAa,QAAQ,IAAI,CAAC,CAyN9B;IAEF,SAAS,cAAqB,OAAO,EAAE,GAAG,OAAO,KAAG,QAAQ,IAAI,CAAC,CAQ/D;CACF;AAuBD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,GAAG,kBAAkB,CAiBxF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,MAAM,GAAG,SAAS,CAgCvG"}
|
|
@@ -4,6 +4,7 @@ import { AbstractController } from '../Abstract/AbstractController';
|
|
|
4
4
|
import { StorageStore, ErrorType } from '@searchspring/snap-store-mobx';
|
|
5
5
|
import { getSearchParams } from '../utils/getParams';
|
|
6
6
|
import { ControllerTypes } from '../types';
|
|
7
|
+
import { CLICK_DUPLICATION_TIMEOUT, isClickWithinProductLink } from '../utils/isClickWithinProductLink';
|
|
7
8
|
const BACKGROUND_FILTER_FIELD_MATCHES = ['collection', 'category', 'categories', 'hierarchy'];
|
|
8
9
|
const BACKGROUND_FILTERS_VALUE_FLAGS = [1, 0, '1', '0', 'true', 'false', true, false];
|
|
9
10
|
const defaultConfig = {
|
|
@@ -22,6 +23,7 @@ const defaultConfig = {
|
|
|
22
23
|
},
|
|
23
24
|
},
|
|
24
25
|
};
|
|
26
|
+
const schemaMap = {};
|
|
25
27
|
export class SearchController extends AbstractController {
|
|
26
28
|
constructor(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context) {
|
|
27
29
|
super(config, { client, store, urlManager, eventManager, profiler, logger, tracker }, context);
|
|
@@ -61,52 +63,53 @@ export class SearchController extends AbstractController {
|
|
|
61
63
|
}
|
|
62
64
|
// store position data or empty object
|
|
63
65
|
this.storage.set('scrollMap', scrollMap);
|
|
64
|
-
const data =
|
|
66
|
+
const data = schemaMap[result.id];
|
|
65
67
|
this.tracker.events[this.pageType].clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
66
68
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
67
69
|
this.events.product[result.id].clickThrough = true;
|
|
68
|
-
this.eventManager.fire('track.product.clickThrough', { controller: this, event: e,
|
|
70
|
+
this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data });
|
|
69
71
|
},
|
|
70
72
|
click: (e, result) => {
|
|
71
|
-
if (result.
|
|
73
|
+
if (this.events.product[result.id]?.click) {
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (href) {
|
|
77
|
-
this.track.product.clickThrough(e, result);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
// TODO: in future, send as an interaction event
|
|
76
|
+
if (result.type === 'banner') {
|
|
77
|
+
return;
|
|
81
78
|
}
|
|
79
|
+
isClickWithinProductLink(e, result) && this.track.product.clickThrough(e, result);
|
|
80
|
+
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
81
|
+
this.events.product[result.id].click = true;
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
this.events.product[result.id].click = false;
|
|
84
|
+
}, CLICK_DUPLICATION_TIMEOUT);
|
|
82
85
|
},
|
|
83
86
|
render: (result) => {
|
|
84
87
|
if (this.events.product[result.id]?.render) {
|
|
85
88
|
return;
|
|
86
89
|
}
|
|
87
|
-
const data =
|
|
90
|
+
const data = schemaMap[result.id];
|
|
88
91
|
this.tracker.events[this.pageType].render({ data, siteId: this.config.globals?.siteId });
|
|
89
92
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
90
93
|
this.events.product[result.id].render = true;
|
|
91
|
-
this.eventManager.fire('track.product.render', { controller: this,
|
|
94
|
+
this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
|
|
92
95
|
},
|
|
93
96
|
impression: (result) => {
|
|
94
97
|
if (this.events.product[result.id]?.impression) {
|
|
95
98
|
return;
|
|
96
99
|
}
|
|
97
|
-
const data =
|
|
100
|
+
const data = schemaMap[result.id];
|
|
98
101
|
this.tracker.events[this.pageType].impression({ data, siteId: this.config.globals?.siteId });
|
|
99
102
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
100
103
|
this.events.product[result.id].impression = true;
|
|
101
|
-
this.eventManager.fire('track.product.impression', { controller: this,
|
|
104
|
+
this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data });
|
|
102
105
|
},
|
|
103
106
|
addToCart: (result) => {
|
|
104
|
-
const data = getSearchAddtocartSchemaData({
|
|
107
|
+
const data = getSearchAddtocartSchemaData({ searchSchemaData: schemaMap[result.id], results: [result] });
|
|
105
108
|
this.tracker.events[this.pageType].addToCart({
|
|
106
109
|
data,
|
|
107
110
|
siteId: this.config.globals?.siteId,
|
|
108
111
|
});
|
|
109
|
-
this.eventManager.fire('track.product.addToCart', { controller: this,
|
|
112
|
+
this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data });
|
|
110
113
|
},
|
|
111
114
|
},
|
|
112
115
|
redirect: (redirectURL) => {
|
|
@@ -142,7 +145,7 @@ export class SearchController extends AbstractController {
|
|
|
142
145
|
throw err;
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
|
-
const stringyParams = JSON.stringify(params);
|
|
148
|
+
const stringyParams = JSON.stringify(getStorableRequestParams(params));
|
|
146
149
|
const prevStringyParams = this.storage.get('lastStringyParams');
|
|
147
150
|
if (stringyParams == prevStringyParams) {
|
|
148
151
|
// no param change - not searching
|
|
@@ -164,6 +167,7 @@ export class SearchController extends AbstractController {
|
|
|
164
167
|
// infinite backfill is enabled AND we have not yet fetched any results
|
|
165
168
|
if (this.config.settings?.infinite.backfill && !this.store.loaded) {
|
|
166
169
|
// create requests for all missing pages (using Arrray(page).fill() to populate an array to map)
|
|
170
|
+
const backfillRequestsParams = [];
|
|
167
171
|
const backfillRequests = Array(params.pagination.page)
|
|
168
172
|
.fill('backfill')
|
|
169
173
|
.map((v, i) => {
|
|
@@ -177,6 +181,7 @@ export class SearchController extends AbstractController {
|
|
|
177
181
|
delete backfillParams?.search?.redirectResponse;
|
|
178
182
|
}
|
|
179
183
|
}
|
|
184
|
+
backfillRequestsParams.push(backfillParams);
|
|
180
185
|
return this.client.search(backfillParams);
|
|
181
186
|
});
|
|
182
187
|
const backfillResponses = await Promise.all(backfillRequests);
|
|
@@ -185,8 +190,8 @@ export class SearchController extends AbstractController {
|
|
|
185
190
|
meta = backfillResponses[0][0];
|
|
186
191
|
response = backfillResponses[0][1];
|
|
187
192
|
// accumulate results from all backfill responses
|
|
188
|
-
const backfillResults = backfillResponses.reduce((results, response) => {
|
|
189
|
-
|
|
193
|
+
const backfillResults = backfillResponses.reduce((results, response, index) => {
|
|
194
|
+
createResultSchemaMapping({ request: backfillRequestsParams[index], response: response });
|
|
190
195
|
return results.concat(...response[1].results);
|
|
191
196
|
}, []);
|
|
192
197
|
// overwrite pagination params to expected state
|
|
@@ -198,6 +203,7 @@ export class SearchController extends AbstractController {
|
|
|
198
203
|
else {
|
|
199
204
|
// infinite with no backfills.
|
|
200
205
|
[meta, response] = await this.client.search(params);
|
|
206
|
+
createResultSchemaMapping({ request: params, response: [meta, response] });
|
|
201
207
|
// append new results to previous results
|
|
202
208
|
response.results = [...this.previousResults, ...(response.results || [])];
|
|
203
209
|
}
|
|
@@ -209,6 +215,7 @@ export class SearchController extends AbstractController {
|
|
|
209
215
|
// clear previousResults to prevent infinite scroll from using them
|
|
210
216
|
this.previousResults = [];
|
|
211
217
|
[meta, response] = await this.client.search(params);
|
|
218
|
+
createResultSchemaMapping({ request: params, response: [meta, response] });
|
|
212
219
|
}
|
|
213
220
|
// MockClient will overwrite the client search() method and use SearchData to return mock data which already contains meta data
|
|
214
221
|
if (!response.meta) {
|
|
@@ -308,9 +315,14 @@ export class SearchController extends AbstractController {
|
|
|
308
315
|
this.store.loading = false;
|
|
309
316
|
}
|
|
310
317
|
};
|
|
311
|
-
this.addToCart = async (
|
|
312
|
-
|
|
313
|
-
|
|
318
|
+
this.addToCart = async (_products) => {
|
|
319
|
+
const products = typeof _products.slice == 'function' ? _products.slice() : [_products];
|
|
320
|
+
products.forEach((product) => {
|
|
321
|
+
this.track.product.addToCart(product);
|
|
322
|
+
});
|
|
323
|
+
if (products.length > 0) {
|
|
324
|
+
this.eventManager.fire('addToCart', { controller: this, products });
|
|
325
|
+
}
|
|
314
326
|
};
|
|
315
327
|
// deep merge config with defaults
|
|
316
328
|
this.config = deepmerge(defaultConfig, this.config);
|
|
@@ -372,25 +384,14 @@ export class SearchController extends AbstractController {
|
|
|
372
384
|
window.location.replace(redirectURL);
|
|
373
385
|
return false;
|
|
374
386
|
}
|
|
375
|
-
const nonBackgroundFilters = search?.request?.filters?.filter((filter) => !filter.background);
|
|
376
|
-
if (config?.settings?.redirects?.singleResult &&
|
|
377
|
-
search?.response?.search?.query &&
|
|
378
|
-
search?.response?.pagination?.totalResults === 1 &&
|
|
379
|
-
!nonBackgroundFilters?.length) {
|
|
380
|
-
//set loaded to true to prevent infinite search/reloading from happening
|
|
381
|
-
searchStore.loaded = true;
|
|
382
|
-
window.location.replace(search?.response.results[0].mappings.core.url);
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
387
|
await next();
|
|
386
388
|
});
|
|
387
389
|
this.eventManager.on('afterStore', async (search, next) => {
|
|
388
390
|
await next();
|
|
389
|
-
// save last params
|
|
390
|
-
this.storage.set('lastStringyParams', JSON.stringify(search.request));
|
|
391
391
|
// get scrollTo positioning and send it to 'restorePosition' event
|
|
392
392
|
const storableRequestParams = getStorableRequestParams(search.request);
|
|
393
393
|
const stringyParams = JSON.stringify(storableRequestParams);
|
|
394
|
+
this.storage.set('lastStringyParams', stringyParams);
|
|
394
395
|
const scrollMap = this.storage.get('scrollMap') || {};
|
|
395
396
|
const elementPosition = scrollMap[stringyParams];
|
|
396
397
|
if (!elementPosition) {
|
|
@@ -405,14 +406,27 @@ export class SearchController extends AbstractController {
|
|
|
405
406
|
const controller = search.controller;
|
|
406
407
|
if (controller.store.loaded && !controller.store.error) {
|
|
407
408
|
const products = controller.store.results.filter((result) => result.type === 'product' && !this.events.product[result.id]?.render);
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
409
|
+
if (products.length === 0) {
|
|
410
|
+
// handle no results
|
|
411
|
+
const data = getSearchSchemaData({ params: search.request, response: search.response });
|
|
412
|
+
this.tracker.events[this.pageType].render({ data, siteId: this.config.globals?.siteId });
|
|
413
|
+
}
|
|
411
414
|
products.forEach((result) => {
|
|
415
|
+
const data = schemaMap[result.id];
|
|
416
|
+
this.tracker.events[this.pageType].render({ data, siteId: this.config.globals?.siteId });
|
|
412
417
|
this.events.product[result.id] = this.events.product[result.id] || {};
|
|
413
418
|
this.events.product[result.id].render = true;
|
|
419
|
+
this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
|
|
414
420
|
});
|
|
415
|
-
|
|
421
|
+
const config = search.controller.config;
|
|
422
|
+
const nonBackgroundFilters = search?.request?.filters?.filter((filter) => !filter.background);
|
|
423
|
+
if (config?.settings?.redirects?.singleResult &&
|
|
424
|
+
search?.response?.search?.query &&
|
|
425
|
+
search?.response?.pagination?.totalResults === 1 &&
|
|
426
|
+
!nonBackgroundFilters?.length) {
|
|
427
|
+
window.location.replace(search?.response.results[0].mappings.core.url);
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
416
430
|
}
|
|
417
431
|
});
|
|
418
432
|
// restore position
|
|
@@ -528,6 +542,25 @@ export class SearchController extends AbstractController {
|
|
|
528
542
|
return params;
|
|
529
543
|
}
|
|
530
544
|
}
|
|
545
|
+
function createResultSchemaMapping({ request, response }) {
|
|
546
|
+
const [_, searchResponse] = response;
|
|
547
|
+
const schema = getSearchSchemaData({
|
|
548
|
+
params: request,
|
|
549
|
+
response: searchResponse,
|
|
550
|
+
});
|
|
551
|
+
searchResponse.results?.forEach((result) => {
|
|
552
|
+
schemaMap[result.id] = {
|
|
553
|
+
...schema,
|
|
554
|
+
results: [
|
|
555
|
+
{
|
|
556
|
+
position: result.position,
|
|
557
|
+
uid: result.mappings?.core?.uid || '',
|
|
558
|
+
sku: result.mappings?.core?.sku,
|
|
559
|
+
},
|
|
560
|
+
],
|
|
561
|
+
};
|
|
562
|
+
});
|
|
563
|
+
}
|
|
531
564
|
export function getStorableRequestParams(request) {
|
|
532
565
|
return {
|
|
533
566
|
siteId: request.siteId,
|
|
@@ -575,10 +608,9 @@ function getSearchRedirectSchemaData({ redirectURL }) {
|
|
|
575
608
|
redirect: redirectURL,
|
|
576
609
|
};
|
|
577
610
|
}
|
|
578
|
-
function getSearchAddtocartSchemaData({
|
|
579
|
-
const base = getSearchSchemaData({ params, store, results });
|
|
611
|
+
function getSearchAddtocartSchemaData({ searchSchemaData, results, }) {
|
|
580
612
|
return {
|
|
581
|
-
...
|
|
613
|
+
...searchSchemaData,
|
|
582
614
|
results: results?.map((result) => {
|
|
583
615
|
const core = result.mappings.core;
|
|
584
616
|
return {
|
|
@@ -590,7 +622,7 @@ function getSearchAddtocartSchemaData({ params, store, results, }) {
|
|
|
590
622
|
}) || [],
|
|
591
623
|
};
|
|
592
624
|
}
|
|
593
|
-
function getSearchSchemaData({ params,
|
|
625
|
+
function getSearchSchemaData({ params, response }) {
|
|
594
626
|
const filters = params.filters?.reduce((acc, filter) => {
|
|
595
627
|
const key = filter.background ? 'bgfilter' : 'filter';
|
|
596
628
|
acc[key] = acc[key] || [];
|
|
@@ -611,9 +643,16 @@ function getSearchSchemaData({ params, store, results }) {
|
|
|
611
643
|
}
|
|
612
644
|
return acc;
|
|
613
645
|
}, {});
|
|
646
|
+
let correctedQuery;
|
|
647
|
+
if (response?.search?.originalQuery && response?.search?.query) {
|
|
648
|
+
correctedQuery = response?.search?.query;
|
|
649
|
+
}
|
|
650
|
+
const campaigns = response?.merchandising?.campaigns || [];
|
|
651
|
+
const experiments = response?.merchandising?.experiments || [];
|
|
614
652
|
return {
|
|
615
653
|
q: params.search?.query?.string || '',
|
|
616
|
-
correctedQuery
|
|
654
|
+
correctedQuery,
|
|
655
|
+
matchType: response?.search?.matchType,
|
|
617
656
|
...filters,
|
|
618
657
|
sort: params.sorts?.map((sort) => {
|
|
619
658
|
return {
|
|
@@ -622,16 +661,16 @@ function getSearchSchemaData({ params, store, results }) {
|
|
|
622
661
|
};
|
|
623
662
|
}),
|
|
624
663
|
pagination: {
|
|
625
|
-
totalResults:
|
|
626
|
-
page:
|
|
627
|
-
resultsPerPage:
|
|
664
|
+
totalResults: response?.pagination?.totalResults,
|
|
665
|
+
page: response?.pagination?.page,
|
|
666
|
+
resultsPerPage: response?.pagination?.pageSize,
|
|
628
667
|
},
|
|
629
668
|
merchandising: {
|
|
630
|
-
personalized:
|
|
631
|
-
redirect:
|
|
632
|
-
triggeredCampaigns: (
|
|
633
|
-
|
|
634
|
-
const experiement =
|
|
669
|
+
personalized: response?.merchandising?.personalized,
|
|
670
|
+
redirect: response?.merchandising?.redirect,
|
|
671
|
+
triggeredCampaigns: (campaigns.length &&
|
|
672
|
+
campaigns.map((campaign) => {
|
|
673
|
+
const experiement = experiments.find((experiment) => experiment.campaignId === campaign.id);
|
|
635
674
|
return {
|
|
636
675
|
id: campaign.id,
|
|
637
676
|
experimentId: experiement?.experimentId,
|
|
@@ -640,14 +679,6 @@ function getSearchSchemaData({ params, store, results }) {
|
|
|
640
679
|
})) ||
|
|
641
680
|
undefined,
|
|
642
681
|
},
|
|
643
|
-
results:
|
|
644
|
-
const core = result.mappings.core;
|
|
645
|
-
return {
|
|
646
|
-
uid: core.uid || '',
|
|
647
|
-
// childUid: core.uid,
|
|
648
|
-
sku: core.sku,
|
|
649
|
-
// childSku: core.sku,
|
|
650
|
-
};
|
|
651
|
-
}) || [],
|
|
682
|
+
results: [],
|
|
652
683
|
};
|
|
653
684
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Product } from '@searchspring/snap-store-mobx';
|
|
2
|
+
export declare const CLICK_DUPLICATION_TIMEOUT = 300;
|
|
3
|
+
export declare const CLICK_THROUGH_CLOSEST_MAX_LEVELS = 12;
|
|
4
|
+
export declare const isClickWithinProductLink: (e: MouseEvent, result: Product) => boolean;
|
|
5
|
+
//# sourceMappingURL=isClickWithinProductLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isClickWithinProductLink.d.ts","sourceRoot":"","sources":["../../../src/utils/isClickWithinProductLink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAE7D,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,MAAO,UAAU,UAAU,OAAO,KAAG,OAgBzE,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const CLICK_DUPLICATION_TIMEOUT = 300;
|
|
2
|
+
export const CLICK_THROUGH_CLOSEST_MAX_LEVELS = 12;
|
|
3
|
+
export const isClickWithinProductLink = (e, result) => {
|
|
4
|
+
let currentElement = e.target;
|
|
5
|
+
let href = null;
|
|
6
|
+
let level = 0;
|
|
7
|
+
const resultCoreUrl = result?.display?.mappings.core?.url || '';
|
|
8
|
+
const resultDisplayUrl = result?.mappings.core?.url || '';
|
|
9
|
+
while (currentElement && level < CLICK_THROUGH_CLOSEST_MAX_LEVELS) {
|
|
10
|
+
href = currentElement.getAttribute('href');
|
|
11
|
+
if (href && ((resultCoreUrl && href.includes(resultCoreUrl)) || (resultDisplayUrl && href.includes(resultDisplayUrl)))) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
currentElement = currentElement.parentElement;
|
|
15
|
+
level++;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@searchspring/snap-controller",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.1",
|
|
4
4
|
"description": "Snap Controllers",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -20,22 +20,22 @@
|
|
|
20
20
|
"test:watch": "jest --watch"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@searchspring/snap-toolbox": "^0.
|
|
23
|
+
"@searchspring/snap-toolbox": "^0.66.1",
|
|
24
24
|
"css.escape": "1.5.1",
|
|
25
25
|
"deepmerge": "4.3.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@searchspring/snap-client": "^0.
|
|
29
|
-
"@searchspring/snap-event-manager": "^0.
|
|
30
|
-
"@searchspring/snap-logger": "^0.
|
|
31
|
-
"@searchspring/snap-profiler": "^0.
|
|
32
|
-
"@searchspring/snap-store-mobx": "^0.
|
|
33
|
-
"@searchspring/snap-tracker": "^0.
|
|
34
|
-
"@searchspring/snap-url-manager": "^0.
|
|
28
|
+
"@searchspring/snap-client": "^0.66.1",
|
|
29
|
+
"@searchspring/snap-event-manager": "^0.66.1",
|
|
30
|
+
"@searchspring/snap-logger": "^0.66.1",
|
|
31
|
+
"@searchspring/snap-profiler": "^0.66.1",
|
|
32
|
+
"@searchspring/snap-store-mobx": "^0.66.1",
|
|
33
|
+
"@searchspring/snap-tracker": "^0.66.1",
|
|
34
|
+
"@searchspring/snap-url-manager": "^0.66.1"
|
|
35
35
|
},
|
|
36
36
|
"sideEffects": false,
|
|
37
37
|
"files": [
|
|
38
38
|
"dist/**/*"
|
|
39
39
|
],
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "a9d6de5e5e144ca964e8a19d9dba5c63d9d94db6"
|
|
41
41
|
}
|