@searchspring/snap-controller 0.65.1 → 0.66.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.
Files changed (25) hide show
  1. package/dist/cjs/Autocomplete/AutocompleteController.d.ts +2 -1
  2. package/dist/cjs/Autocomplete/AutocompleteController.d.ts.map +1 -1
  3. package/dist/cjs/Autocomplete/AutocompleteController.js +79 -32
  4. package/dist/cjs/Recommendation/RecommendationController.d.ts +2 -3
  5. package/dist/cjs/Recommendation/RecommendationController.d.ts.map +1 -1
  6. package/dist/cjs/Recommendation/RecommendationController.js +73 -25
  7. package/dist/cjs/Search/SearchController.d.ts +1 -1
  8. package/dist/cjs/Search/SearchController.d.ts.map +1 -1
  9. package/dist/cjs/Search/SearchController.js +141 -61
  10. package/dist/cjs/utils/isClickWithinProductLink.d.ts +5 -0
  11. package/dist/cjs/utils/isClickWithinProductLink.d.ts.map +1 -0
  12. package/dist/cjs/utils/isClickWithinProductLink.js +22 -0
  13. package/dist/esm/Autocomplete/AutocompleteController.d.ts +2 -1
  14. package/dist/esm/Autocomplete/AutocompleteController.d.ts.map +1 -1
  15. package/dist/esm/Autocomplete/AutocompleteController.js +66 -28
  16. package/dist/esm/Recommendation/RecommendationController.d.ts +2 -3
  17. package/dist/esm/Recommendation/RecommendationController.d.ts.map +1 -1
  18. package/dist/esm/Recommendation/RecommendationController.js +56 -23
  19. package/dist/esm/Search/SearchController.d.ts +1 -1
  20. package/dist/esm/Search/SearchController.d.ts.map +1 -1
  21. package/dist/esm/Search/SearchController.js +123 -53
  22. package/dist/esm/utils/isClickWithinProductLink.d.ts +5 -0
  23. package/dist/esm/utils/isClickWithinProductLink.d.ts.map +1 -0
  24. package/dist/esm/utils/isClickWithinProductLink.js +17 -0
  25. package/package.json +10 -10
@@ -81,6 +81,7 @@ var AbstractController_1 = require("../Abstract/AbstractController");
81
81
  var snap_store_mobx_1 = require("@searchspring/snap-store-mobx");
82
82
  var getParams_1 = require("../utils/getParams");
83
83
  var types_1 = require("../types");
84
+ var isClickWithinProductLink_1 = require("../utils/isClickWithinProductLink");
84
85
  var BACKGROUND_FILTER_FIELD_MATCHES = ['collection', 'category', 'categories', 'hierarchy'];
85
86
  var BACKGROUND_FILTERS_VALUE_FLAGS = [1, 0, '1', '0', 'true', 'false', true, false];
86
87
  var defaultConfig = {
@@ -99,6 +100,7 @@ var defaultConfig = {
99
100
  },
100
101
  },
101
102
  };
103
+ var schemaMap = {};
102
104
  var SearchController = /** @class */ (function (_super) {
103
105
  __extends(SearchController, _super);
104
106
  function SearchController(config, _a, context) {
@@ -142,56 +144,57 @@ var SearchController = /** @class */ (function (_super) {
142
144
  }
143
145
  // store position data or empty object
144
146
  _this.storage.set('scrollMap', scrollMap);
145
- var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
147
+ var data = schemaMap[result.id];
146
148
  _this.tracker.events[_this.pageType].clickThrough({ data: data, siteId: (_f = _this.config.globals) === null || _f === void 0 ? void 0 : _f.siteId });
147
149
  _this.events.product[result.id] = _this.events.product[result.id] || {};
148
150
  _this.events.product[result.id].clickThrough = true;
149
- _this.eventManager.fire('track.product.clickThrough', { controller: _this, event: e, products: [result], trackEvent: data });
151
+ _this.eventManager.fire('track.product.clickThrough', { controller: _this, event: e, product: result, trackEvent: data });
150
152
  },
151
153
  click: function (e, result) {
152
- var _a, _b, _c;
153
- if (result.type === 'banner') {
154
+ var _a;
155
+ if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.click) {
154
156
  return;
155
157
  }
156
- // TODO: closest might be going too far - write own function to only go n levels up - additionally check that href includes result.url
157
- var href = ((_a = e.target) === null || _a === void 0 ? void 0 : _a.getAttribute('href')) || ((_c = (_b = e.target) === null || _b === void 0 ? void 0 : _b.closest('a')) === null || _c === void 0 ? void 0 : _c.getAttribute('href'));
158
- if (href) {
159
- _this.track.product.clickThrough(e, result);
160
- }
161
- else {
162
- // TODO: in future, send as an interaction event
158
+ if (result.type === 'banner') {
159
+ return;
163
160
  }
161
+ (0, isClickWithinProductLink_1.isClickWithinProductLink)(e, result) && _this.track.product.clickThrough(e, result);
162
+ _this.events.product[result.id] = _this.events.product[result.id] || {};
163
+ _this.events.product[result.id].click = true;
164
+ setTimeout(function () {
165
+ _this.events.product[result.id].click = false;
166
+ }, isClickWithinProductLink_1.CLICK_DUPLICATION_TIMEOUT);
164
167
  },
165
168
  render: function (result) {
166
169
  var _a, _b;
167
170
  if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.render) {
168
171
  return;
169
172
  }
170
- var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
173
+ var data = schemaMap[result.id];
171
174
  _this.tracker.events[_this.pageType].render({ data: data, siteId: (_b = _this.config.globals) === null || _b === void 0 ? void 0 : _b.siteId });
172
175
  _this.events.product[result.id] = _this.events.product[result.id] || {};
173
176
  _this.events.product[result.id].render = true;
174
- _this.eventManager.fire('track.product.render', { controller: _this, products: [result], trackEvent: data });
177
+ _this.eventManager.fire('track.product.render', { controller: _this, product: result, trackEvent: data });
175
178
  },
176
179
  impression: function (result) {
177
180
  var _a, _b;
178
181
  if ((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.impression) {
179
182
  return;
180
183
  }
181
- var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
184
+ var data = schemaMap[result.id];
182
185
  _this.tracker.events[_this.pageType].impression({ data: data, siteId: (_b = _this.config.globals) === null || _b === void 0 ? void 0 : _b.siteId });
183
186
  _this.events.product[result.id] = _this.events.product[result.id] || {};
184
187
  _this.events.product[result.id].impression = true;
185
- _this.eventManager.fire('track.product.impression', { controller: _this, products: [result], trackEvent: data });
188
+ _this.eventManager.fire('track.product.impression', { controller: _this, product: result, trackEvent: data });
186
189
  },
187
190
  addToCart: function (result) {
188
191
  var _a;
189
- var data = getSearchSchemaData({ params: _this.params, store: _this.store, results: [result] });
192
+ var data = getSearchAddtocartSchemaData({ searchSchemaData: schemaMap[result.id], results: [result] });
190
193
  _this.tracker.events[_this.pageType].addToCart({
191
194
  data: data,
192
195
  siteId: (_a = _this.config.globals) === null || _a === void 0 ? void 0 : _a.siteId,
193
196
  });
194
- _this.eventManager.fire('track.product.addToCart', { controller: _this, products: [result], trackEvent: data });
197
+ _this.eventManager.fire('track.product.addToCart', { controller: _this, product: result, trackEvent: data });
195
198
  },
196
199
  },
197
200
  redirect: function (redirectURL) {
@@ -202,7 +205,7 @@ var SearchController = /** @class */ (function (_super) {
202
205
  },
203
206
  };
204
207
  _this.search = function () { return __awaiter(_this, void 0, void 0, function () {
205
- var params_1, err_1, stringyParams, prevStringyParams, searchProfile, meta, response, preventBackfill, dontBackfill, backfillRequests, backfillResponses, backfillResults, afterSearchProfile, err_2, afterStoreProfile, err_3, err_4;
208
+ var params_1, err_1, stringyParams, prevStringyParams, searchProfile, meta, response, preventBackfill, dontBackfill, backfillRequestsParams_1, backfillRequests, backfillResponses, backfillResults, afterSearchProfile, err_2, afterStoreProfile, err_3, err_4;
206
209
  var _a, _b;
207
210
  var _this = this;
208
211
  var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
@@ -244,7 +247,7 @@ var SearchController = /** @class */ (function (_super) {
244
247
  }
245
248
  return [3 /*break*/, 6];
246
249
  case 6:
247
- stringyParams = JSON.stringify(params_1);
250
+ stringyParams = JSON.stringify(getStorableRequestParams(params_1));
248
251
  prevStringyParams = this.storage.get('lastStringyParams');
249
252
  if (stringyParams == prevStringyParams) {
250
253
  // no param change - not searching
@@ -263,6 +266,7 @@ var SearchController = /** @class */ (function (_super) {
263
266
  return [2 /*return*/];
264
267
  }
265
268
  if (!(((_l = this.config.settings) === null || _l === void 0 ? void 0 : _l.infinite.backfill) && !this.store.loaded)) return [3 /*break*/, 8];
269
+ backfillRequestsParams_1 = [];
266
270
  backfillRequests = Array(params_1.pagination.page)
267
271
  .fill('backfill')
268
272
  .map(function (v, i) {
@@ -277,6 +281,7 @@ var SearchController = /** @class */ (function (_super) {
277
281
  (_d = backfillParams === null || backfillParams === void 0 ? void 0 : backfillParams.search) === null || _d === void 0 ? true : delete _d.redirectResponse;
278
282
  }
279
283
  }
284
+ backfillRequestsParams_1.push(backfillParams);
280
285
  return _this.client.search(backfillParams);
281
286
  });
282
287
  return [4 /*yield*/, Promise.all(backfillRequests)];
@@ -286,8 +291,8 @@ var SearchController = /** @class */ (function (_super) {
286
291
  // set the meta and response to the first page of backfillResponses
287
292
  meta = backfillResponses[0][0];
288
293
  response = backfillResponses[0][1];
289
- backfillResults = backfillResponses.reduce(function (results, response) {
290
- // response is [meta, searchResponse]
294
+ backfillResults = backfillResponses.reduce(function (results, response, index) {
295
+ createResultSchemaMapping({ request: backfillRequestsParams_1[index], response: response });
291
296
  return results.concat.apply(results, response[1].results);
292
297
  }, []);
293
298
  // overwrite pagination params to expected state
@@ -300,6 +305,7 @@ var SearchController = /** @class */ (function (_super) {
300
305
  case 9:
301
306
  // infinite with no backfills.
302
307
  _a = _o.sent(), meta = _a[0], response = _a[1];
308
+ createResultSchemaMapping({ request: params_1, response: [meta, response] });
303
309
  // append new results to previous results
304
310
  response.results = __spreadArray(__spreadArray([], this.previousResults, true), (response.results || []), true);
305
311
  _o.label = 10;
@@ -313,6 +319,7 @@ var SearchController = /** @class */ (function (_super) {
313
319
  return [4 /*yield*/, this.client.search(params_1)];
314
320
  case 12:
315
321
  _b = _o.sent(), meta = _b[0], response = _b[1];
322
+ createResultSchemaMapping({ request: params_1, response: [meta, response] });
316
323
  _o.label = 13;
317
324
  case 13:
318
325
  // MockClient will overwrite the client search() method and use SearchData to return mock data which already contains meta data
@@ -429,10 +436,17 @@ var SearchController = /** @class */ (function (_super) {
429
436
  }
430
437
  });
431
438
  }); };
432
- _this.addToCart = function (product) { return __awaiter(_this, void 0, void 0, function () {
439
+ _this.addToCart = function (_products) { return __awaiter(_this, void 0, void 0, function () {
440
+ var products;
441
+ var _this = this;
433
442
  return __generator(this, function (_a) {
434
- this.track.product.addToCart(product);
435
- this.eventManager.fire('addToCart', { controller: this, products: [product] });
443
+ products = typeof _products.slice == 'function' ? _products.slice() : [_products];
444
+ products.forEach(function (product) {
445
+ _this.track.product.addToCart(product);
446
+ });
447
+ if (products.length > 0) {
448
+ this.eventManager.fire('addToCart', { controller: this, products: products });
449
+ }
436
450
  return [2 /*return*/];
437
451
  });
438
452
  }); };
@@ -498,10 +512,10 @@ var SearchController = /** @class */ (function (_super) {
498
512
  });
499
513
  // add 'afterSearch' middleware
500
514
  _this.eventManager.on('afterSearch', function (search, next) { return __awaiter(_this, void 0, void 0, function () {
501
- var config, redirectURL, searchStore, nonBackgroundFilters;
502
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
503
- return __generator(this, function (_q) {
504
- switch (_q.label) {
515
+ var config, redirectURL, searchStore;
516
+ var _a, _b, _c, _d, _e, _f;
517
+ return __generator(this, function (_g) {
518
+ switch (_g.label) {
505
519
  case 0:
506
520
  config = search.controller.config;
507
521
  redirectURL = (_b = (_a = search.response) === null || _a === void 0 ? void 0 : _a.merchandising) === null || _b === void 0 ? void 0 : _b.redirect;
@@ -513,19 +527,9 @@ var SearchController = /** @class */ (function (_super) {
513
527
  window.location.replace(redirectURL);
514
528
  return [2 /*return*/, false];
515
529
  }
516
- nonBackgroundFilters = (_h = (_g = search === null || search === void 0 ? void 0 : search.request) === null || _g === void 0 ? void 0 : _g.filters) === null || _h === void 0 ? void 0 : _h.filter(function (filter) { return !filter.background; });
517
- if (((_k = (_j = config === null || config === void 0 ? void 0 : config.settings) === null || _j === void 0 ? void 0 : _j.redirects) === null || _k === void 0 ? void 0 : _k.singleResult) &&
518
- ((_m = (_l = search === null || search === void 0 ? void 0 : search.response) === null || _l === void 0 ? void 0 : _l.search) === null || _m === void 0 ? void 0 : _m.query) &&
519
- ((_p = (_o = search === null || search === void 0 ? void 0 : search.response) === null || _o === void 0 ? void 0 : _o.pagination) === null || _p === void 0 ? void 0 : _p.totalResults) === 1 &&
520
- !(nonBackgroundFilters === null || nonBackgroundFilters === void 0 ? void 0 : nonBackgroundFilters.length)) {
521
- //set loaded to true to prevent infinite search/reloading from happening
522
- searchStore.loaded = true;
523
- window.location.replace(search === null || search === void 0 ? void 0 : search.response.results[0].mappings.core.url);
524
- return [2 /*return*/, false];
525
- }
526
530
  return [4 /*yield*/, next()];
527
531
  case 1:
528
- _q.sent();
532
+ _g.sent();
529
533
  return [2 /*return*/];
530
534
  }
531
535
  });
@@ -537,10 +541,9 @@ var SearchController = /** @class */ (function (_super) {
537
541
  case 0: return [4 /*yield*/, next()];
538
542
  case 1:
539
543
  _a.sent();
540
- // save last params
541
- this.storage.set('lastStringyParams', JSON.stringify(search.request));
542
544
  storableRequestParams = getStorableRequestParams(search.request);
543
545
  stringyParams = JSON.stringify(storableRequestParams);
546
+ this.storage.set('lastStringyParams', stringyParams);
544
547
  scrollMap = this.storage.get('scrollMap') || {};
545
548
  elementPosition = scrollMap[stringyParams];
546
549
  if (!elementPosition) {
@@ -553,6 +556,44 @@ var SearchController = /** @class */ (function (_super) {
553
556
  }
554
557
  });
555
558
  }); });
559
+ _this.eventManager.on('afterStore', function (search, next) { return __awaiter(_this, void 0, void 0, function () {
560
+ var controller, products, data, config_1, nonBackgroundFilters;
561
+ var _this = this;
562
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
563
+ return __generator(this, function (_k) {
564
+ switch (_k.label) {
565
+ case 0: return [4 /*yield*/, next()];
566
+ case 1:
567
+ _k.sent();
568
+ controller = search.controller;
569
+ if (controller.store.loaded && !controller.store.error) {
570
+ products = controller.store.results.filter(function (result) { var _a; return result.type === 'product' && !((_a = _this.events.product[result.id]) === null || _a === void 0 ? void 0 : _a.render); });
571
+ if (products.length === 0) {
572
+ data = getSearchSchemaData({ params: search.request, results: [] });
573
+ this.tracker.events[this.pageType].render({ data: data, siteId: (_a = this.config.globals) === null || _a === void 0 ? void 0 : _a.siteId });
574
+ }
575
+ products.forEach(function (result) {
576
+ var _a;
577
+ var data = schemaMap[result.id];
578
+ _this.tracker.events[_this.pageType].render({ data: data, siteId: (_a = _this.config.globals) === null || _a === void 0 ? void 0 : _a.siteId });
579
+ _this.events.product[result.id] = _this.events.product[result.id] || {};
580
+ _this.events.product[result.id].render = true;
581
+ _this.eventManager.fire('track.product.render', { controller: _this, product: result, trackEvent: data });
582
+ });
583
+ config_1 = search.controller.config;
584
+ nonBackgroundFilters = (_c = (_b = search === null || search === void 0 ? void 0 : search.request) === null || _b === void 0 ? void 0 : _b.filters) === null || _c === void 0 ? void 0 : _c.filter(function (filter) { return !filter.background; });
585
+ if (((_e = (_d = config_1 === null || config_1 === void 0 ? void 0 : config_1.settings) === null || _d === void 0 ? void 0 : _d.redirects) === null || _e === void 0 ? void 0 : _e.singleResult) &&
586
+ ((_g = (_f = search === null || search === void 0 ? void 0 : search.response) === null || _f === void 0 ? void 0 : _f.search) === null || _g === void 0 ? void 0 : _g.query) &&
587
+ ((_j = (_h = search === null || search === void 0 ? void 0 : search.response) === null || _h === void 0 ? void 0 : _h.pagination) === null || _j === void 0 ? void 0 : _j.totalResults) === 1 &&
588
+ !(nonBackgroundFilters === null || nonBackgroundFilters === void 0 ? void 0 : nonBackgroundFilters.length)) {
589
+ window.location.replace(search === null || search === void 0 ? void 0 : search.response.results[0].mappings.core.url);
590
+ return [2 /*return*/, false];
591
+ }
592
+ }
593
+ return [2 /*return*/];
594
+ }
595
+ });
596
+ }); });
556
597
  // restore position
557
598
  if ((_d = (_c = _this.config.settings) === null || _c === void 0 ? void 0 : _c.restorePosition) === null || _d === void 0 ? void 0 : _d.enabled) {
558
599
  _this.eventManager.on('restorePosition', function (_a, next) {
@@ -703,6 +744,26 @@ var SearchController = /** @class */ (function (_super) {
703
744
  return SearchController;
704
745
  }(AbstractController_1.AbstractController));
705
746
  exports.SearchController = SearchController;
747
+ function createResultSchemaMapping(_a) {
748
+ var _b;
749
+ var request = _a.request, response = _a.response;
750
+ var _ = response[0], searchResponse = response[1];
751
+ var schema = getSearchSchemaData({
752
+ params: request,
753
+ results: [], // results added below because this would contain all results
754
+ response: searchResponse,
755
+ });
756
+ (_b = searchResponse.results) === null || _b === void 0 ? void 0 : _b.forEach(function (result) {
757
+ var _a, _b, _c, _d;
758
+ schemaMap[result.id] = __assign(__assign({}, schema), { results: [
759
+ {
760
+ position: result.position,
761
+ uid: ((_b = (_a = result.mappings) === null || _a === void 0 ? void 0 : _a.core) === null || _b === void 0 ? void 0 : _b.uid) || '',
762
+ sku: (_d = (_c = result.mappings) === null || _c === void 0 ? void 0 : _c.core) === null || _d === void 0 ? void 0 : _d.sku,
763
+ },
764
+ ] });
765
+ });
766
+ }
706
767
  function getStorableRequestParams(request) {
707
768
  var _a, _b, _c, _d;
708
769
  return {
@@ -755,9 +816,21 @@ function getSearchRedirectSchemaData(_a) {
755
816
  redirect: redirectURL,
756
817
  };
757
818
  }
819
+ function getSearchAddtocartSchemaData(_a) {
820
+ var searchSchemaData = _a.searchSchemaData, results = _a.results;
821
+ return __assign(__assign({}, searchSchemaData), { results: (results === null || results === void 0 ? void 0 : results.map(function (result) {
822
+ var core = result.mappings.core;
823
+ return {
824
+ uid: core.uid || '',
825
+ sku: core.sku,
826
+ price: Number(core.price),
827
+ qty: result.quantity || 1,
828
+ };
829
+ })) || [] });
830
+ }
758
831
  function getSearchSchemaData(_a) {
759
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
760
- var params = _a.params, store = _a.store, results = _a.results;
832
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
833
+ var params = _a.params, results = _a.results, response = _a.response;
761
834
  var filters = (_b = params.filters) === null || _b === void 0 ? void 0 : _b.reduce(function (acc, filter) {
762
835
  var _a;
763
836
  var _b, _c, _d, _e;
@@ -780,35 +853,42 @@ function getSearchSchemaData(_a) {
780
853
  }
781
854
  return acc;
782
855
  }, {});
783
- return __assign(__assign({ q: ((_d = (_c = params.search) === null || _c === void 0 ? void 0 : _c.query) === null || _d === void 0 ? void 0 : _d.string) || '', correctedQuery: ((_f = (_e = store.search) === null || _e === void 0 ? void 0 : _e.originalQuery) === null || _f === void 0 ? void 0 : _f.string) ? (_h = (_g = store.search) === null || _g === void 0 ? void 0 : _g.query) === null || _h === void 0 ? void 0 : _h.string : undefined }, filters), { sort: [
784
- {
785
- field: (_j = store.sorting.current) === null || _j === void 0 ? void 0 : _j.field,
786
- dir: (_k = store.sorting.current) === null || _k === void 0 ? void 0 : _k.direction,
787
- },
788
- ], pagination: {
789
- totalResults: store.pagination.totalResults,
790
- page: store.pagination.page,
791
- resultsPerPage: store.pagination.pageSize,
856
+ var correctedQuery;
857
+ if (((_c = response === null || response === void 0 ? void 0 : response.search) === null || _c === void 0 ? void 0 : _c.originalQuery) && ((_d = response === null || response === void 0 ? void 0 : response.search) === null || _d === void 0 ? void 0 : _d.query)) {
858
+ correctedQuery = (_e = response === null || response === void 0 ? void 0 : response.search) === null || _e === void 0 ? void 0 : _e.query;
859
+ }
860
+ var campaigns = ((_f = response === null || response === void 0 ? void 0 : response.merchandising) === null || _f === void 0 ? void 0 : _f.campaigns) || [];
861
+ var experiments = ((_g = response === null || response === void 0 ? void 0 : response.merchandising) === null || _g === void 0 ? void 0 : _g.experiments) || [];
862
+ return __assign(__assign({ q: ((_j = (_h = params.search) === null || _h === void 0 ? void 0 : _h.query) === null || _j === void 0 ? void 0 : _j.string) || '', correctedQuery: correctedQuery, matchType: (_k = response === null || response === void 0 ? void 0 : response.search) === null || _k === void 0 ? void 0 : _k.matchType }, filters), { sort: (_l = params.sorts) === null || _l === void 0 ? void 0 : _l.map(function (sort) {
863
+ return {
864
+ field: sort.field,
865
+ dir: sort.direction,
866
+ };
867
+ }), pagination: {
868
+ totalResults: (_m = response === null || response === void 0 ? void 0 : response.pagination) === null || _m === void 0 ? void 0 : _m.totalResults,
869
+ page: (_o = response === null || response === void 0 ? void 0 : response.pagination) === null || _o === void 0 ? void 0 : _o.page,
870
+ resultsPerPage: (_p = response === null || response === void 0 ? void 0 : response.pagination) === null || _p === void 0 ? void 0 : _p.pageSize,
792
871
  }, merchandising: {
793
- personalized: store.merchandising.personalized,
794
- redirect: store.merchandising.redirect,
795
- triggeredCampaigns: (((_l = store.merchandising.campaigns) === null || _l === void 0 ? void 0 : _l.length) &&
796
- ((_m = store.merchandising.campaigns) === null || _m === void 0 ? void 0 : _m.map(function (campaign) {
797
- var experiement = store.merchandising.experiments.find(function (experiment) { return experiment.campaignId === campaign.id; });
872
+ personalized: (_q = response === null || response === void 0 ? void 0 : response.merchandising) === null || _q === void 0 ? void 0 : _q.personalized,
873
+ redirect: (_r = response === null || response === void 0 ? void 0 : response.merchandising) === null || _r === void 0 ? void 0 : _r.redirect,
874
+ triggeredCampaigns: (campaigns.length &&
875
+ campaigns.map(function (campaign) {
876
+ var experiement = experiments.find(function (experiment) { return experiment.campaignId === campaign.id; });
798
877
  return {
799
878
  id: campaign.id,
800
879
  experimentId: experiement === null || experiement === void 0 ? void 0 : experiement.experimentId,
801
880
  variationId: experiement === null || experiement === void 0 ? void 0 : experiement.variationId,
802
881
  };
803
- }))) ||
882
+ })) ||
804
883
  undefined,
805
884
  }, results: (results === null || results === void 0 ? void 0 : results.map(function (result) {
806
- var core = result.mappings.core;
885
+ var _a;
886
+ var core = (_a = result.mappings) === null || _a === void 0 ? void 0 : _a.core;
887
+ var position = result.position;
807
888
  return {
889
+ position: position,
808
890
  uid: core.uid || '',
809
- // childUid: core.uid,
810
891
  sku: core.sku,
811
- // childSku: core.sku,
812
892
  };
813
893
  })) || [] });
814
894
  }
@@ -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,OAezE,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isClickWithinProductLink = exports.CLICK_THROUGH_CLOSEST_MAX_LEVELS = exports.CLICK_DUPLICATION_TIMEOUT = void 0;
4
+ exports.CLICK_DUPLICATION_TIMEOUT = 300;
5
+ exports.CLICK_THROUGH_CLOSEST_MAX_LEVELS = 12;
6
+ var isClickWithinProductLink = function (e, result) {
7
+ var _a, _b, _c;
8
+ var currentElement = e.target;
9
+ var href = null;
10
+ var level = 0;
11
+ var resultUrl = ((_b = (_a = result === null || result === void 0 ? void 0 : result.display) === null || _a === void 0 ? void 0 : _a.mappings.core) === null || _b === void 0 ? void 0 : _b.url) || ((_c = result === null || result === void 0 ? void 0 : result.mappings.core) === null || _c === void 0 ? void 0 : _c.url) || '';
12
+ while (currentElement && level < exports.CLICK_THROUGH_CLOSEST_MAX_LEVELS) {
13
+ href = currentElement.getAttribute('href');
14
+ if (href && resultUrl && href.includes(resultUrl)) {
15
+ return true;
16
+ }
17
+ currentElement = currentElement.parentElement;
18
+ level++;
19
+ }
20
+ return false;
21
+ };
22
+ exports.isClickWithinProductLink = isClickWithinProductLink;
@@ -23,6 +23,7 @@ export declare class AutocompleteController extends AbstractController {
23
23
  storage: StorageStore;
24
24
  events: {
25
25
  product: Record<string, {
26
+ click?: boolean;
26
27
  clickThrough?: boolean;
27
28
  impression?: boolean;
28
29
  render?: boolean;
@@ -51,7 +52,7 @@ export declare class AutocompleteController extends AbstractController {
51
52
  bind(): Promise<void>;
52
53
  searchTrending: () => Promise<void>;
53
54
  search: () => Promise<void>;
54
- addToCart: (product: Product) => Promise<void>;
55
+ addToCart: (_products: Product[] | Product) => Promise<void>;
55
56
  }
56
57
  export {};
57
58
  //# sourceMappingURL=AutocompleteController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/Autocomplete/AutocompleteController.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAa,OAAO,EAAqB,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5G,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,4BAA4B,EAAiC,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElI,OAAO,KAAK,EAAE,wBAAwB,EAAgE,MAAM,2BAA2B,CAAC;AAWxI,eAAO,MAAM,WAAW,MAAM,CAAC;AA+B/B,KAAK,wBAAwB,GAAG;IAC/B,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;AAEF,qBAAa,sBAAuB,SAAQ,kBAAkB;IACtD,IAAI,kBAAgC;IACnC,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACtC,OAAO,EAAE,YAAY,CAAC;IAE7B,MAAM,EAAE;QACP,OAAO,EAAE,MAAM,CACd,MAAM,EACN;YACC,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,CAAC;SACjB,CACD,CAAC;KACF,CAEC;gBAGD,MAAM,EAAE,4BAA4B,EACpC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA0D3B,KAAK,EAAE,wBAAwB,CAqD7B;IAEF,IAAI,MAAM,IAAI,wBAAwB,CAwCrC;IAEK,UAAU,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhE,KAAK,IAAI,IAAI;IASb,QAAQ;;0BAEc,aAAa,KAAG,QAAQ,OAAO,GAAG,SAAS,CAAC;wBAkDpD,aAAa,KAAG,IAAI;uBAMrB,UAAU,KAAG,IAAI;4BAQN,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAG,QAAQ,IAAI,CAAC;mCAgDhD,MAAM,WAAW,CAAC,gBAAgB,CAAC,KAAG,IAAI;uBAqBtD,KAAK;;;;uBAyDL,UAAU,KAAG,IAAI;;MAS5B;IAEF,MAAM,IAAI,IAAI;IAgBR,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgF3B,cAAc,QAAa,QAAQ,IAAI,CAAC,CAwBtC;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CA6I9B;IAEF,SAAS,YAAmB,OAAO,KAAG,QAAQ,IAAI,CAAC,CAGjD;CACF"}
1
+ {"version":3,"file":"AutocompleteController.d.ts","sourceRoot":"","sources":["../../../src/Autocomplete/AutocompleteController.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAa,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,4BAA4B,EAAiC,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAElI,OAAO,KAAK,EAAE,wBAAwB,EAAgE,MAAM,2BAA2B,CAAC;AAcxI,eAAO,MAAM,WAAW,MAAM,CAAC;AA+B/B,KAAK,wBAAwB,GAAG;IAC/B,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;AAEF,qBAAa,sBAAuB,SAAQ,kBAAkB;IACtD,IAAI,kBAAgC;IACnC,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,4BAA4B,CAAC;IACtC,OAAO,EAAE,YAAY,CAAC;IAE7B,MAAM,EAAE;QACP,OAAO,EAAE,MAAM,CACd,MAAM,EACN;YACC,KAAK,CAAC,EAAE,OAAO,CAAC;YAChB,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,UAAU,CAAC,EAAE,OAAO,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,CAAC;SACjB,CACD,CAAC;KACF,CAEC;gBAGD,MAAM,EAAE,4BAA4B,EACpC,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAC1F,OAAO,CAAC,EAAE,gBAAgB;IA4E3B,KAAK,EAAE,wBAAwB,CA0D7B;IAEF,IAAI,MAAM,IAAI,wBAAwB,CAwCrC;IAEK,UAAU,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhE,KAAK,IAAI,IAAI;IASb,QAAQ;;0BAEc,aAAa,KAAG,QAAQ,OAAO,GAAG,SAAS,CAAC;wBAkDpD,aAAa,KAAG,IAAI;uBAMrB,UAAU,KAAG,IAAI;4BAQN,MAAM,SAAS,CAAC,gBAAgB,CAAC,KAAG,QAAQ,IAAI,CAAC;mCAgDhD,MAAM,WAAW,CAAC,gBAAgB,CAAC,KAAG,IAAI;uBAqBtD,KAAK;;;;uBAyDL,UAAU,KAAG,IAAI;;MAS5B;IAEF,MAAM,IAAI,IAAI;IAgBR,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgF3B,cAAc,QAAa,QAAQ,IAAI,CAAC,CAwBtC;IAEF,MAAM,QAAa,QAAQ,IAAI,CAAC,CA0I9B;IAEF,SAAS,cAAqB,OAAO,EAAE,GAAG,OAAO,KAAG,QAAQ,IAAI,CAAC,CAQ/D;CACF"}
@@ -3,6 +3,7 @@ import { StorageStore, ErrorType } from '@searchspring/snap-store-mobx';
3
3
  import { AbstractController } from '../Abstract/AbstractController';
4
4
  import { getSearchParams } from '../utils/getParams';
5
5
  import { ControllerTypes } from '../types';
6
+ import { CLICK_DUPLICATION_TIMEOUT, isClickWithinProductLink } from '../utils/isClickWithinProductLink';
6
7
  const INPUT_ATTRIBUTE = 'ss-autocomplete-input';
7
8
  export const INPUT_DELAY = 200;
8
9
  const KEY_ENTER = 13;
@@ -50,29 +51,30 @@ export class AutocompleteController extends AbstractController {
50
51
  this.tracker.events.autocomplete.clickThrough({ data, siteId: this.config.globals?.siteId });
51
52
  this.events.product[result.id] = this.events.product[result.id] || {};
52
53
  this.events.product[result.id].clickThrough = true;
53
- this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, products: [result], trackEvent: data });
54
+ this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data });
54
55
  },
55
56
  click: (e, result) => {
56
- if (result.type === 'banner') {
57
+ if (this.events.product[result.id]?.click) {
57
58
  return;
58
59
  }
59
- // TODO: closest might be going too far - write own function to only go n levels up - additionally check that href includes result.url
60
- const href = e.target?.getAttribute('href') || e.target?.closest('a')?.getAttribute('href');
61
- if (href) {
62
- this.track.product.clickThrough(e, result);
63
- }
64
- else {
65
- // TODO: in future, send as an interaction event
60
+ if (result.type === 'banner') {
61
+ return;
66
62
  }
63
+ isClickWithinProductLink(e, result) && this.track.product.clickThrough(e, result);
64
+ this.events.product[result.id] = this.events.product[result.id] || {};
65
+ this.events.product[result.id].click = true;
66
+ setTimeout(() => {
67
+ this.events.product[result.id].click = false;
68
+ }, CLICK_DUPLICATION_TIMEOUT);
67
69
  },
68
70
  render: (result) => {
69
71
  if (this.events.product[result.id]?.render)
70
72
  return;
71
- const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
73
+ const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: result ? [result] : [] });
72
74
  this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId });
73
75
  this.events.product[result.id] = this.events.product[result.id] || {};
74
76
  this.events.product[result.id].render = true;
75
- this.eventManager.fire('track.product.render', { controller: this, products: [result], trackEvent: data });
77
+ this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
76
78
  },
77
79
  impression: (result) => {
78
80
  if (this.events.product[result.id]?.impression)
@@ -81,18 +83,18 @@ export class AutocompleteController extends AbstractController {
81
83
  this.tracker.events.autocomplete.impression({ data, siteId: this.config.globals?.siteId });
82
84
  this.events.product[result.id] = this.events.product[result.id] || {};
83
85
  this.events.product[result.id].impression = true;
84
- this.eventManager.fire('track.product.impression', { controller: this, products: [result], trackEvent: data });
86
+ this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data });
85
87
  },
86
88
  addToCart: (result) => {
87
- const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] });
89
+ const data = getAutocompleteAddtocartSchemaData({ params: this.params, store: this.store, results: [result] });
88
90
  this.tracker.events.autocomplete.addToCart({ data, siteId: this.config.globals?.siteId });
89
- this.eventManager.fire('track.product.addToCart', { controller: this, products: [result], trackEvent: data });
91
+ this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data });
90
92
  },
91
93
  },
92
94
  redirect: (redirectURL) => {
93
95
  const data = getAutocompleteRedirectSchemaData({ redirectURL });
94
96
  this.tracker.events.autocomplete.redirect({ data, siteId: this.config.globals?.siteId });
95
- this.eventManager.fire('track.product.redirect', { controller: this, redirectURL, trackEvent: data });
97
+ this.eventManager.fire('track.redirect', { controller: this, redirectURL, trackEvent: data });
96
98
  },
97
99
  };
98
100
  this.handlers = {
@@ -307,8 +309,6 @@ export class AutocompleteController extends AbstractController {
307
309
  if (!this.initialized) {
308
310
  await this.init();
309
311
  }
310
- // reset events for new search
311
- this.events = { product: {} };
312
312
  // if urlManager has no query, there will be no need to get params and no query
313
313
  if (!this.urlManager.state.query) {
314
314
  return;
@@ -434,9 +434,14 @@ export class AutocompleteController extends AbstractController {
434
434
  this.store.loading = false;
435
435
  }
436
436
  };
437
- this.addToCart = async (product) => {
438
- this.track.product.addToCart(product);
439
- this.eventManager.fire('addToCart', { controller: this, products: [product] });
437
+ this.addToCart = async (_products) => {
438
+ const products = typeof _products.slice == 'function' ? _products.slice() : [_products];
439
+ products.forEach((product) => {
440
+ this.track.product.addToCart(product);
441
+ });
442
+ if (products.length > 0) {
443
+ this.eventManager.fire('addToCart', { controller: this, products });
444
+ }
440
445
  };
441
446
  // deep merge config with defaults
442
447
  this.config = deepmerge(defaultConfig, this.config);
@@ -453,6 +458,23 @@ export class AutocompleteController extends AbstractController {
453
458
  type: 'session',
454
459
  key: `ss-controller-${this.config.id}`,
455
460
  });
461
+ this.eventManager.on('afterStore', async (search, next) => {
462
+ await next();
463
+ const controller = search.controller;
464
+ if (controller.store.loaded && !controller.store.error) {
465
+ const products = controller.store.results.filter((result) => result.type === 'product');
466
+ const results = products.length === 0 ? [] : products;
467
+ const data = getAutocompleteSchemaData({ params: search.request, store: this.store, results });
468
+ this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId });
469
+ products.forEach((result) => {
470
+ this.events.product[result.id] = this.events.product[result.id] || {};
471
+ this.events.product[result.id].render = true;
472
+ this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data });
473
+ });
474
+ // reset events for new search. It's here for a reason. Ensures render happens before impressions on new input value?
475
+ this.events = { product: {} };
476
+ }
477
+ });
456
478
  // add 'afterSearch' middleware
457
479
  this.eventManager.on('afterSearch', async (ac, next) => {
458
480
  await next();
@@ -692,6 +714,21 @@ function getAutocompleteRedirectSchemaData({ redirectURL }) {
692
714
  redirect: redirectURL,
693
715
  };
694
716
  }
717
+ function getAutocompleteAddtocartSchemaData({ params, store, results, }) {
718
+ const base = getAutocompleteSchemaData({ params, store, results });
719
+ return {
720
+ ...base,
721
+ results: results?.map((result) => {
722
+ const core = result.mappings.core;
723
+ return {
724
+ uid: core.uid || '',
725
+ sku: core.sku,
726
+ price: Number(core.price),
727
+ qty: result.quantity || 1,
728
+ };
729
+ }) || [],
730
+ };
731
+ }
695
732
  function getAutocompleteSchemaData({ params, store, results, }) {
696
733
  const filters = params.filters?.reduce((acc, filter) => {
697
734
  const key = filter.background ? 'bgfilter' : 'filter';
@@ -716,13 +753,14 @@ function getAutocompleteSchemaData({ params, store, results, }) {
716
753
  return {
717
754
  q: store.search?.originalQuery?.string || store.search?.query?.string || '',
718
755
  correctedQuery: store.search?.originalQuery?.string ? store.search?.query?.string : undefined,
756
+ matchType: store.search.matchType,
719
757
  ...filters,
720
- sort: [
721
- {
722
- field: store.sorting.current?.field,
723
- dir: store.sorting.current?.direction,
724
- },
725
- ],
758
+ sort: params.sorts?.map((sort) => {
759
+ return {
760
+ field: sort.field,
761
+ dir: sort.direction,
762
+ };
763
+ }),
726
764
  pagination: {
727
765
  totalResults: store.pagination.totalResults,
728
766
  page: store.pagination.page,
@@ -744,11 +782,11 @@ function getAutocompleteSchemaData({ params, store, results, }) {
744
782
  },
745
783
  results: results?.map((result) => {
746
784
  const core = result.mappings.core;
785
+ const position = result.position;
747
786
  return {
787
+ position,
748
788
  uid: core.uid || '',
749
- // childUid: core.uid,
750
789
  sku: core.sku,
751
- // childSku: core.sku,
752
790
  };
753
791
  }) || [],
754
792
  };