@searchspring/snap-controller 0.73.7 → 0.75.0
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 +0 -1
- package/dist/cjs/Autocomplete/AutocompleteController.d.ts.map +1 -1
- package/dist/cjs/Autocomplete/AutocompleteController.js +124 -68
- package/dist/cjs/Finder/FinderController.d.ts.map +1 -1
- package/dist/cjs/Finder/FinderController.js +3 -0
- package/dist/cjs/Recommendation/RecommendationController.d.ts.map +1 -1
- package/dist/cjs/Recommendation/RecommendationController.js +84 -58
- package/dist/cjs/Search/SearchController.d.ts.map +1 -1
- package/dist/cjs/Search/SearchController.js +142 -66
- package/dist/cjs/utils/isClickWithinProductLink.d.ts +1 -1
- package/dist/cjs/utils/isClickWithinProductLink.d.ts.map +1 -1
- package/dist/cjs/utils/isClickWithinProductLink.js +14 -12
- package/dist/esm/Autocomplete/AutocompleteController.d.ts +0 -1
- package/dist/esm/Autocomplete/AutocompleteController.d.ts.map +1 -1
- package/dist/esm/Autocomplete/AutocompleteController.js +101 -28
- package/dist/esm/Finder/FinderController.d.ts.map +1 -1
- package/dist/esm/Finder/FinderController.js +3 -0
- package/dist/esm/Recommendation/RecommendationController.d.ts.map +1 -1
- package/dist/esm/Recommendation/RecommendationController.js +64 -22
- package/dist/esm/Search/SearchController.d.ts.map +1 -1
- package/dist/esm/Search/SearchController.js +106 -22
- package/dist/esm/utils/isClickWithinProductLink.d.ts +1 -1
- package/dist/esm/utils/isClickWithinProductLink.d.ts.map +1 -1
- package/dist/esm/utils/isClickWithinProductLink.js +13 -12
- package/package.json +10 -10
|
@@ -11,6 +11,9 @@ const BACKGROUND_FILTERS_VALUE_FLAGS = [1, 0, '1', '0', 'true', 'false', true, f
|
|
|
11
11
|
const defaultConfig = {
|
|
12
12
|
id: 'search',
|
|
13
13
|
globals: {},
|
|
14
|
+
beacon: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
},
|
|
14
17
|
settings: {
|
|
15
18
|
redirects: {
|
|
16
19
|
merchandising: true,
|
|
@@ -36,7 +39,15 @@ export class SearchController extends AbstractController {
|
|
|
36
39
|
this.track = {
|
|
37
40
|
banner: {
|
|
38
41
|
impression: ({ uid, responseId }) => {
|
|
39
|
-
if (
|
|
42
|
+
if (!uid) {
|
|
43
|
+
this.log.warn('No banner provided to track.banner.impression');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!this.events[responseId]) {
|
|
47
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
else if (this.events[responseId]?.banner[uid]?.impression) {
|
|
40
51
|
return;
|
|
41
52
|
}
|
|
42
53
|
const banner = { uid };
|
|
@@ -46,12 +57,20 @@ export class SearchController extends AbstractController {
|
|
|
46
57
|
results: [],
|
|
47
58
|
};
|
|
48
59
|
this.eventManager.fire('track.banner.impression', { controller: this, product: { uid }, trackEvent: data });
|
|
49
|
-
this.tracker.events[this.page.type].impression({ data, siteId: this.config.globals?.siteId });
|
|
60
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].impression({ data, siteId: this.config.globals?.siteId });
|
|
50
61
|
this.events[responseId].banner[uid] = this.events[responseId].banner[uid] || {};
|
|
51
62
|
this.events[responseId].banner[uid].impression = true;
|
|
52
63
|
},
|
|
53
64
|
click: (e, banner) => {
|
|
65
|
+
if (!banner) {
|
|
66
|
+
this.log.warn('No banner provided to track.banner.click');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
54
69
|
const { responseId, uid } = banner;
|
|
70
|
+
if (!this.events[responseId]) {
|
|
71
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
55
74
|
if (isClickWithinBannerLink(e)) {
|
|
56
75
|
if (this.events?.[responseId]?.banner[uid]?.clickThrough) {
|
|
57
76
|
return;
|
|
@@ -65,13 +84,21 @@ export class SearchController extends AbstractController {
|
|
|
65
84
|
}
|
|
66
85
|
},
|
|
67
86
|
clickThrough: (e, { uid, responseId }) => {
|
|
87
|
+
if (!uid) {
|
|
88
|
+
this.log.warn('No banner provided to track.banner.clickThrough');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!this.events[responseId]) {
|
|
92
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
68
95
|
const banner = { uid };
|
|
69
96
|
const data = {
|
|
70
97
|
responseId,
|
|
71
98
|
banners: [banner],
|
|
72
99
|
};
|
|
73
100
|
this.eventManager.fire('track.banner.clickThrough', { controller: this, event: e, product: { uid }, trackEvent: data });
|
|
74
|
-
this.tracker.events[this.page.type].clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
101
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
75
102
|
this.events[responseId].banner[uid] = this.events[responseId].banner[uid] || {};
|
|
76
103
|
this.events[responseId].banner[uid].clickThrough = true;
|
|
77
104
|
setTimeout(() => {
|
|
@@ -81,7 +108,15 @@ export class SearchController extends AbstractController {
|
|
|
81
108
|
},
|
|
82
109
|
product: {
|
|
83
110
|
clickThrough: (e, result) => {
|
|
111
|
+
if (!result) {
|
|
112
|
+
this.log.warn('No result provided to track.product.clickThrough');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
84
115
|
const responseId = result.responseId;
|
|
116
|
+
if (!this.events[responseId]) {
|
|
117
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
85
120
|
const target = e.target;
|
|
86
121
|
const resultHref = result.display?.mappings.core?.url || result.mappings.core?.url || '';
|
|
87
122
|
const elemHref = target?.getAttribute('href');
|
|
@@ -108,21 +143,34 @@ export class SearchController extends AbstractController {
|
|
|
108
143
|
}
|
|
109
144
|
// store position data or empty object
|
|
110
145
|
this.storage.set('scrollMap', scrollMap);
|
|
146
|
+
const type = (['product', 'banner'].includes(result.type) ? result.type : 'product');
|
|
111
147
|
const item = {
|
|
112
|
-
type
|
|
113
|
-
uid: result.id,
|
|
114
|
-
|
|
115
|
-
|
|
148
|
+
type,
|
|
149
|
+
uid: result.id ? '' + result.id : '',
|
|
150
|
+
...(type === 'product'
|
|
151
|
+
? {
|
|
152
|
+
parentId: result.id ? '' + result.id : '',
|
|
153
|
+
sku: result.mappings.core?.sku ? '' + result.mappings.core?.sku : undefined,
|
|
154
|
+
}
|
|
155
|
+
: {}),
|
|
116
156
|
};
|
|
117
157
|
const data = {
|
|
118
158
|
responseId,
|
|
119
159
|
results: [item],
|
|
120
160
|
};
|
|
121
161
|
this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data });
|
|
122
|
-
this.tracker.events[this.page.type].clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
162
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].clickThrough({ data, siteId: this.config.globals?.siteId });
|
|
123
163
|
},
|
|
124
164
|
click: (e, result) => {
|
|
165
|
+
if (!result) {
|
|
166
|
+
this.log.warn('No result provided to track.product.click');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
125
169
|
const responseId = result.responseId;
|
|
170
|
+
if (!this.events[responseId]) {
|
|
171
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
126
174
|
if (result.type === 'banner' && isClickWithinBannerLink(e)) {
|
|
127
175
|
if (this.events?.[responseId]?.product[result.id]?.inlineBannerClickThrough) {
|
|
128
176
|
return;
|
|
@@ -147,15 +195,28 @@ export class SearchController extends AbstractController {
|
|
|
147
195
|
}
|
|
148
196
|
},
|
|
149
197
|
impression: (result) => {
|
|
198
|
+
if (!result) {
|
|
199
|
+
this.log.warn('No result provided to track.product.impression');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
150
202
|
const responseId = result.responseId;
|
|
151
|
-
if (this.events[responseId]
|
|
203
|
+
if (!this.events[responseId]) {
|
|
204
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
152
205
|
return;
|
|
153
206
|
}
|
|
207
|
+
else if (this.events[responseId]?.product[result.id]?.impression) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const type = (['product', 'banner'].includes(result.type) ? result.type : 'product');
|
|
154
211
|
const item = {
|
|
155
|
-
type
|
|
156
|
-
uid: result.id,
|
|
157
|
-
|
|
158
|
-
|
|
212
|
+
type,
|
|
213
|
+
uid: result.id ? '' + result.id : '',
|
|
214
|
+
...(type === 'product'
|
|
215
|
+
? {
|
|
216
|
+
parentId: result.id ? '' + result.id : '',
|
|
217
|
+
sku: result.mappings.core?.sku ? '' + result.mappings.core?.sku : undefined,
|
|
218
|
+
}
|
|
219
|
+
: {}),
|
|
159
220
|
};
|
|
160
221
|
const data = {
|
|
161
222
|
responseId,
|
|
@@ -163,12 +224,20 @@ export class SearchController extends AbstractController {
|
|
|
163
224
|
banners: [],
|
|
164
225
|
};
|
|
165
226
|
this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data });
|
|
166
|
-
this.tracker.events[this.page.type].impression({ data, siteId: this.config.globals?.siteId });
|
|
227
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].impression({ data, siteId: this.config.globals?.siteId });
|
|
167
228
|
this.events[responseId].product[result.id] = this.events[responseId].product[result.id] || {};
|
|
168
229
|
this.events[responseId].product[result.id].impression = true;
|
|
169
230
|
},
|
|
170
231
|
addToCart: (result) => {
|
|
232
|
+
if (!result) {
|
|
233
|
+
this.log.warn('No result provided to track.product.addToCart');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
171
236
|
const responseId = result.responseId;
|
|
237
|
+
if (!this.events[responseId]) {
|
|
238
|
+
this.log.warn('No responseId found in controller, ensure correct controller is used');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
172
241
|
const product = {
|
|
173
242
|
parentId: result.id,
|
|
174
243
|
uid: result.id,
|
|
@@ -181,16 +250,20 @@ export class SearchController extends AbstractController {
|
|
|
181
250
|
results: [product],
|
|
182
251
|
};
|
|
183
252
|
this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data });
|
|
184
|
-
this.tracker.events[this.page.type].addToCart({ data, siteId: this.config.globals?.siteId });
|
|
253
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].addToCart({ data, siteId: this.config.globals?.siteId });
|
|
185
254
|
},
|
|
186
255
|
},
|
|
187
256
|
redirect: ({ redirectURL, responseId }) => {
|
|
257
|
+
if (!redirectURL) {
|
|
258
|
+
this.log.warn('No redirectURL provided to track.redirect');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
188
261
|
const data = {
|
|
189
262
|
responseId,
|
|
190
263
|
redirect: redirectURL,
|
|
191
264
|
};
|
|
192
265
|
this.eventManager.fire('track.redirect', { controller: this, redirectURL, trackEvent: data });
|
|
193
|
-
this.tracker.events.search.redirect({ data, siteId: this.config.globals?.siteId });
|
|
266
|
+
this.config.beacon?.enabled && this.tracker.events.search.redirect({ data, siteId: this.config.globals?.siteId });
|
|
194
267
|
},
|
|
195
268
|
};
|
|
196
269
|
this.search = async () => {
|
|
@@ -324,6 +397,8 @@ export class SearchController extends AbstractController {
|
|
|
324
397
|
this.previousResults = JSON.parse(JSON.stringify(response.results));
|
|
325
398
|
// update the store
|
|
326
399
|
this.store.update(response);
|
|
400
|
+
const data = { responseId: response.tracking.responseId };
|
|
401
|
+
this.config.beacon?.enabled && this.tracker.events[this.page.type].render({ data, siteId: this.config.globals?.siteId });
|
|
327
402
|
const afterStoreProfile = this.profiler.create({ type: 'event', name: 'afterStore', context: params }).start();
|
|
328
403
|
try {
|
|
329
404
|
await this.eventManager.fire('afterStore', {
|
|
@@ -392,7 +467,11 @@ export class SearchController extends AbstractController {
|
|
|
392
467
|
}
|
|
393
468
|
};
|
|
394
469
|
this.addToCart = async (_products) => {
|
|
395
|
-
const products = typeof _products
|
|
470
|
+
const products = typeof _products?.slice == 'function' ? _products.slice() : [_products];
|
|
471
|
+
if (!_products || products.length === 0) {
|
|
472
|
+
this.log.warn('No products provided to search controller.addToCart');
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
396
475
|
products.forEach((product) => {
|
|
397
476
|
this.track.product.addToCart(product);
|
|
398
477
|
});
|
|
@@ -514,10 +593,7 @@ export class SearchController extends AbstractController {
|
|
|
514
593
|
this.eventManager.on('afterStore', async (search, next) => {
|
|
515
594
|
await next();
|
|
516
595
|
const controller = search.controller;
|
|
517
|
-
const responseId = search.response.tracking.responseId;
|
|
518
596
|
if (controller.store.loaded && !controller.store.error) {
|
|
519
|
-
const data = { responseId };
|
|
520
|
-
this.tracker.events[this.page.type].render({ data, siteId: this.config.globals?.siteId });
|
|
521
597
|
const config = search.controller.config;
|
|
522
598
|
const nonBackgroundFilters = search?.request?.filters?.filter((filter) => !filter.background);
|
|
523
599
|
if (config?.settings?.redirects?.singleResult &&
|
|
@@ -666,8 +742,16 @@ export function generateHrefSelector(element, href, levels = 7) {
|
|
|
666
742
|
let level = 0;
|
|
667
743
|
let elem = element;
|
|
668
744
|
while (elem && level <= levels) {
|
|
669
|
-
|
|
670
|
-
|
|
745
|
+
let innerHrefElem = null;
|
|
746
|
+
try {
|
|
747
|
+
innerHrefElem = elem.querySelector(`[href*="${href}"]`);
|
|
748
|
+
}
|
|
749
|
+
catch (e) {
|
|
750
|
+
try {
|
|
751
|
+
innerHrefElem = elem.querySelector(cssEscape(`[href*="${href}"]`));
|
|
752
|
+
}
|
|
753
|
+
catch { }
|
|
754
|
+
}
|
|
671
755
|
if (innerHrefElem) {
|
|
672
756
|
// innerHrefElem was found! now get selectors up to elem that contained it
|
|
673
757
|
let selector = '';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Product } from '@searchspring/snap-store-mobx';
|
|
2
|
-
export declare const CLICK_DUPLICATION_TIMEOUT =
|
|
2
|
+
export declare const CLICK_DUPLICATION_TIMEOUT = 1000;
|
|
3
3
|
export declare const CLICK_THROUGH_CLOSEST_MAX_LEVELS = 12;
|
|
4
4
|
export declare const isClickWithinProductLink: (e: MouseEvent, result: Product) => boolean;
|
|
5
5
|
//# sourceMappingURL=isClickWithinProductLink.d.ts.map
|
|
@@ -1 +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,
|
|
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,OAAO,CAAC;AAC9C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,MAAO,UAAU,UAAU,OAAO,KAAG,OAoBzE,CAAC"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
export const CLICK_DUPLICATION_TIMEOUT =
|
|
1
|
+
export const CLICK_DUPLICATION_TIMEOUT = 1000;
|
|
2
2
|
export const CLICK_THROUGH_CLOSEST_MAX_LEVELS = 12;
|
|
3
3
|
export const isClickWithinProductLink = (e, result) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
|
|
4
|
+
const resultDisplayUrl = result?.display?.mappings.core?.url || '';
|
|
5
|
+
const resultCoreUrl = result?.mappings.core?.url || '';
|
|
6
|
+
// Get the composed path to handle shadow DOM elements
|
|
7
|
+
const path = e.composedPath ? e.composedPath() : [e.target];
|
|
8
|
+
// Check up to CLICK_THROUGH_CLOSEST_MAX_LEVELS elements in the path
|
|
9
|
+
const elementsToCheck = path.slice(0, CLICK_THROUGH_CLOSEST_MAX_LEVELS);
|
|
10
|
+
for (const element of elementsToCheck) {
|
|
11
|
+
if (element instanceof Element) {
|
|
12
|
+
const href = element.getAttribute('href');
|
|
13
|
+
if (href && ((resultCoreUrl && href.includes(resultCoreUrl)) || (resultDisplayUrl && href.includes(resultDisplayUrl)))) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
|
-
currentElement = currentElement.parentElement;
|
|
15
|
-
level++;
|
|
16
17
|
}
|
|
17
18
|
return false;
|
|
18
19
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@searchspring/snap-controller",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
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.75.0",
|
|
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.75.0",
|
|
29
|
+
"@searchspring/snap-event-manager": "0.75.0",
|
|
30
|
+
"@searchspring/snap-logger": "0.75.0",
|
|
31
|
+
"@searchspring/snap-profiler": "0.75.0",
|
|
32
|
+
"@searchspring/snap-store-mobx": "0.75.0",
|
|
33
|
+
"@searchspring/snap-tracker": "0.75.0",
|
|
34
|
+
"@searchspring/snap-url-manager": "0.75.0"
|
|
35
35
|
},
|
|
36
36
|
"sideEffects": false,
|
|
37
37
|
"files": [
|
|
38
38
|
"dist/**/*"
|
|
39
39
|
],
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "262c02e06e2bc4637e8e4eb9e48990cc3a9d3c18"
|
|
41
41
|
}
|