@dintero/discounts-web-sdk 0.2.2 → 0.2.4

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 (24) hide show
  1. package/dist/declarations/src/dom.d.ts +1 -1
  2. package/dist/dintero-discounts-web-sdk.cjs.dev.js +31 -90
  3. package/dist/dintero-discounts-web-sdk.cjs.prod.js +31 -90
  4. package/dist/dintero-discounts-web-sdk.esm.js +31 -90
  5. package/dist/dintero-discounts-web-sdk.umd.min.js +1 -1
  6. package/dist/dintero-discounts-web-sdk.umd.min.js.map +1 -1
  7. package/package.json +14 -11
  8. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/base.css +0 -224
  9. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/block-navigation.js +0 -87
  10. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/discounts.ts.html +0 -1093
  11. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/dom.ts.html +0 -310
  12. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/error.ts.html +0 -244
  13. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/favicon.png +0 -0
  14. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/fetch.ts.html +0 -385
  15. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/formatters.ts.html +0 -223
  16. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/index.html +0 -236
  17. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/index.ts.html +0 -436
  18. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/loading.ts.html +0 -214
  19. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/normalize.ts.html +0 -136
  20. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/prettify.css +0 -1
  21. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/prettify.js +0 -2
  22. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/sort-arrow-sprite.png +0 -0
  23. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/sorter.js +0 -196
  24. package/coverage/Chrome Headless 109.0.5412.0 (Linux x86_64)/html/translations.ts.html +0 -199
@@ -1,5 +1,5 @@
1
1
  import { Theme } from './types';
2
- declare type CreateElementOptions = {
2
+ type CreateElementOptions = {
3
3
  tag: string;
4
4
  attributes?: {
5
5
  [key: string]: any;
@@ -4,55 +4,43 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const createElement = options => {
6
6
  const elem = document.createElement(options.tag);
7
-
8
7
  if (options.attributes) {
9
8
  addAttributes(elem, options.attributes);
10
9
  }
11
-
12
10
  if (options.handlers) {
13
11
  addEventListeners(elem, options.handlers);
14
12
  }
15
-
16
13
  if (options.styles) {
17
14
  addStyles(elem, options.styles, options.theme);
18
15
  }
19
-
20
16
  if (options.innerHTML) {
21
17
  elem.innerHTML = options.innerHTML;
22
18
  }
23
-
24
19
  return elem;
25
20
  };
26
-
27
21
  const addClass = (elem, className) => {
28
22
  const names = elem.className.split(" ");
29
-
30
23
  if (!names.includes(className)) {
31
24
  elem.className = elem.className + " " + className;
32
25
  }
33
26
  };
34
-
35
27
  const hash = input => {
36
28
  let hash = 0;
37
-
38
29
  for (var i = 0; i < input.length; i++) {
39
30
  var char = input.charCodeAt(i);
40
31
  hash = (hash << 5) - hash + char;
41
32
  hash = hash & hash;
42
33
  }
43
-
44
34
  return hash.toString(36);
45
35
  };
46
-
47
36
  const addStyles = (elem, styles, theme) => {
48
37
  // "styled components" light
49
38
  styles.forEach(cssFn => {
50
39
  // get class name from hash
51
40
  const className = 'dintero-deals-' + hash(cssFn('dintero-deals', theme));
52
- addClass(elem, className); // add style tag to DOM if not exists
53
-
41
+ addClass(elem, className);
42
+ // add style tag to DOM if not exists
54
43
  const hit = document.querySelector(`[data-css-hash=${className}]`);
55
-
56
44
  if (!hit) {
57
45
  const style = document.createElement('style');
58
46
  style.innerHTML = cssFn(className, theme);
@@ -61,13 +49,11 @@ const addStyles = (elem, styles, theme) => {
61
49
  }
62
50
  });
63
51
  };
64
-
65
52
  const addAttributes = (elem, attributes) => {
66
53
  Object.keys(attributes).forEach(key => {
67
54
  elem.setAttribute(key, attributes[key]);
68
55
  });
69
56
  };
70
-
71
57
  const addEventListeners = (elem, handlers) => {
72
58
  Object.keys(handlers).forEach(key => {
73
59
  elem.addEventListener(key, handlers[key]);
@@ -97,13 +83,11 @@ const no = {
97
83
  const translations = {
98
84
  no
99
85
  };
100
- const findValuesRegex = /(\{\{)[^}]*(\}\})/g; // i18n light
101
-
86
+ const findValuesRegex = /(\{\{)[^}]*(\}\})/g;
87
+ // i18n light
102
88
  const t = (translateString, values) => {
103
89
  const matches = translateString.match(findValuesRegex) || [];
104
-
105
90
  const _values = values || {};
106
-
107
91
  return matches.reduce((interpolated, match) => {
108
92
  const key = match.replace('{{', '').replace('}}', '');
109
93
  const value = _values[key] || '';
@@ -118,41 +102,32 @@ const monetaryString = (amount, configuration, options) => {
118
102
  const beforeDot = amountString.slice(0, dotIndex) || '0';
119
103
  const afterDot = amountString.slice(dotIndex);
120
104
  const exponentAmount = afterDot == "00" && !opt.decimal ? beforeDot : beforeDot + '.' + afterDot;
121
-
122
105
  if (opt.variant === 'short' && afterDot === '00') {
123
106
  return beforeDot + ',-';
124
107
  }
125
-
126
108
  if (!opt.currency) {
127
109
  return exponentAmount;
128
110
  }
129
-
130
111
  if (configuration.currency.position === 'prefix') {
131
112
  return configuration.currency.value + ' ' + exponentAmount;
132
113
  }
133
-
134
114
  return exponentAmount + ' ' + configuration.currency.value;
135
115
  };
136
-
137
116
  const padZero = value => {
138
117
  if (value < 10) {
139
118
  return `0${value}`;
140
119
  }
141
-
142
120
  return value.toString();
143
121
  };
144
-
145
122
  const dateString = (isoString, configuration) => {
146
123
  try {
147
124
  const date = new Date(isoString);
148
-
149
125
  if (configuration.language === 'no') {
150
126
  const dd = padZero(date.getDate());
151
127
  const mm = padZero(date.getMonth() + 1);
152
128
  const yyyy = date.getFullYear();
153
129
  return [dd, mm, yyyy].join('.');
154
130
  }
155
-
156
131
  return new Intl.DateTimeFormat().format(date);
157
132
  } catch (e) {
158
133
  return isoString.substr(0, 10);
@@ -178,7 +153,6 @@ const normalize = className => `
178
153
  `;
179
154
 
180
155
  const findWebshopLink = discount => discount.links && discount.links.find(x => x.rel && x.rel === 'webshop');
181
-
182
156
  const discountStyle = (className, theme) => `
183
157
  @keyframes appear {
184
158
  from {
@@ -221,7 +195,6 @@ const discountStyle = (className, theme) => `
221
195
  }
222
196
  }
223
197
  `;
224
-
225
198
  const imageWrapperStyles = className => `
226
199
  .${className} {
227
200
  display: flex;
@@ -230,7 +203,6 @@ const imageWrapperStyles = className => `
230
203
  height: 300px;
231
204
  width: 100%,
232
205
  }`;
233
-
234
206
  const imageStyle = className => `
235
207
  .${className} {
236
208
  max-width: 100%;
@@ -238,7 +210,6 @@ const imageStyle = className => `
238
210
  text-align: center;
239
211
  display: inline-block;
240
212
  }`;
241
-
242
213
  const ribbonTopStyle = (className, theme) => `
243
214
  .${className} {
244
215
  position: absolute;
@@ -266,7 +237,6 @@ const ribbonTopStyle = (className, theme) => `
266
237
  opacity: 0.5;
267
238
  }
268
239
  `;
269
-
270
240
  const ribbonBottomStyle = (className, theme) => `
271
241
  .${className} {
272
242
  position: absolute;
@@ -294,7 +264,6 @@ const ribbonBottomStyle = (className, theme) => `
294
264
  opacity: 0.5;
295
265
  }
296
266
  `;
297
-
298
267
  const titleStyle = className => `
299
268
  .${className} {
300
269
  margin-bottom: 0;
@@ -303,12 +272,10 @@ const titleStyle = className => `
303
272
  font-size: 1.3em;
304
273
  font-weight: 700;
305
274
  }`;
306
-
307
275
  const subtitleStyle = className => `
308
276
  .${className} {
309
277
  margin-top: 0;
310
278
  }`;
311
-
312
279
  const limitationsWrapperStyle = className => `
313
280
  .${className} {
314
281
  font-size: 0.75em;
@@ -317,11 +284,9 @@ const limitationsWrapperStyle = className => `
317
284
  display: block;
318
285
  }
319
286
  `;
320
-
321
287
  const getTopRibbonText = discount => {
322
288
  return discount && discount.metadata && discount.metadata.label || undefined;
323
289
  };
324
-
325
290
  const createTopRibbon = (discount, configuration) => {
326
291
  const text = getTopRibbonText(discount);
327
292
  return text && createElement({
@@ -331,10 +296,8 @@ const createTopRibbon = (discount, configuration) => {
331
296
  styles: [ribbonTopStyle]
332
297
  });
333
298
  };
334
-
335
299
  const getBottomRibbonText = (discount, configuration) => {
336
300
  const tStrings = translations[configuration.language];
337
-
338
301
  if (discount.reward.type === "discount_item_new_price") {
339
302
  return monetaryString(discount.reward.value, configuration, {
340
303
  variant: "short"
@@ -363,10 +326,8 @@ const getBottomRibbonText = (discount, configuration) => {
363
326
  } else if (discount.reward.type === "discount_item_percent") {
364
327
  return discount.reward.value + "%";
365
328
  }
366
-
367
329
  return "";
368
330
  };
369
-
370
331
  const createBottomRibbon = (discount, configuration) => {
371
332
  const text = getBottomRibbonText(discount, configuration);
372
333
  return text && createElement({
@@ -376,7 +337,6 @@ const createBottomRibbon = (discount, configuration) => {
376
337
  styles: [ribbonBottomStyle]
377
338
  });
378
339
  };
379
-
380
340
  const createImage = discount => {
381
341
  const imageWrapper = createElement({
382
342
  tag: "div",
@@ -392,14 +352,11 @@ const createImage = discount => {
392
352
  },
393
353
  styles: [normalize, imageStyle]
394
354
  });
395
-
396
355
  if (image) {
397
356
  imageWrapper.appendChild(image);
398
357
  }
399
-
400
358
  return imageWrapper;
401
359
  };
402
-
403
360
  const createLimitations = (discount, configuration) => {
404
361
  const tStrings = translations[configuration.language];
405
362
  const wrapper = createElement({
@@ -440,7 +397,6 @@ const createLimitations = (discount, configuration) => {
440
397
  });
441
398
  return wrapper;
442
399
  };
443
-
444
400
  const createDiscount = (discount, configuration) => {
445
401
  const discountElem = createElement({
446
402
  tag: findWebshopLink(discount) ? "a" : "div",
@@ -464,8 +420,8 @@ const createDiscount = (discount, configuration) => {
464
420
  tag: "p",
465
421
  innerHTML: discount.description
466
422
  });
467
- const limitations = createLimitations(discount, configuration); // add children to discount wrapper
468
-
423
+ const limitations = createLimitations(discount, configuration);
424
+ // add children to discount wrapper
469
425
  const children = [ribbonTop, ribbonBottom, image, title, subtitle, description, limitations].filter(child => child);
470
426
  children.forEach(child => discountElem.appendChild(child));
471
427
  return discountElem;
@@ -473,7 +429,8 @@ const createDiscount = (discount, configuration) => {
473
429
 
474
430
  const createHeaders = (keyValues, configuration) => {
475
431
  const headers = new Headers();
476
- Object.entries({ ...keyValues,
432
+ Object.entries({
433
+ ...keyValues,
477
434
  "Dintero-System-Name": "deals-web-sdk",
478
435
  "Dintero-System-Version": configuration.version
479
436
  }).forEach(([key, value]) => {
@@ -481,12 +438,10 @@ const createHeaders = (keyValues, configuration) => {
481
438
  });
482
439
  return headers;
483
440
  };
484
-
485
441
  const fetchAccessToken = configuration => {
486
442
  if (!configuration.api) {
487
443
  throw new Error("Authentication configuration missing");
488
444
  }
489
-
490
445
  const basicAuthCredentials = window.btoa(`${configuration.api.key}:${configuration.api.secret}`);
491
446
  const headers = createHeaders({
492
447
  Authorization: `Basic ${basicAuthCredentials}`,
@@ -504,17 +459,14 @@ const fetchAccessToken = configuration => {
504
459
  if (response.status === 200) {
505
460
  return response.json();
506
461
  }
507
-
508
462
  throw new Error("Authentication failed");
509
463
  });
510
464
  };
511
-
512
465
  const fetchDiscounts = configuration => {
513
466
  return fetchAccessToken(configuration).then(tokenResponse => {
514
467
  const headers = createHeaders({
515
468
  Authorization: `${tokenResponse.token_type} ${tokenResponse.access_token}`
516
469
  }, configuration);
517
-
518
470
  if (configuration.api.discountId) {
519
471
  return window.fetch(`${configuration.api.url}/v1/accounts/${configuration.api.account}/discounts/public/rules/${configuration.api.discountId}`, {
520
472
  headers
@@ -522,18 +474,15 @@ const fetchDiscounts = configuration => {
522
474
  if (response.status === 200) {
523
475
  return response.json().then(discount => [discount]);
524
476
  }
525
-
526
477
  throw new Error("Authentication failed");
527
478
  });
528
479
  }
529
-
530
480
  return window.fetch(`${configuration.api.url}/v1/accounts/${configuration.api.account}/discounts/public/rules?limit=${configuration.api.limit}`, {
531
481
  headers
532
482
  }).then(response => {
533
483
  if (response.status === 200) {
534
484
  return response.json();
535
485
  }
536
-
537
486
  throw new Error("Authentication failed");
538
487
  });
539
488
  });
@@ -572,7 +521,6 @@ const errorStyle = (className, theme) => `
572
521
  transform-origin: top center;
573
522
  }
574
523
  `;
575
-
576
524
  const createError = configuration => {
577
525
  const tString = translations[configuration.language];
578
526
  const errorElem = createElement({
@@ -613,7 +561,6 @@ const loadingStyle = (className, theme) => `
613
561
  }
614
562
  }
615
563
  `;
616
-
617
564
  const createLoading = configuration => {
618
565
  const errorElem = createElement({
619
566
  tag: "div",
@@ -625,7 +572,7 @@ const createLoading = configuration => {
625
572
 
626
573
  var pkg = {
627
574
  name: "@dintero/discounts-web-sdk",
628
- version: "0.2.2",
575
+ version: "0.2.4",
629
576
  description: "Dintero Discounts SDK for web frontends",
630
577
  main: "dist/dintero-discounts-web-sdk.cjs.js",
631
578
  module: "dist/dintero-discounts-web-sdk.esm.js",
@@ -636,7 +583,7 @@ var pkg = {
636
583
  umdName: "discounts"
637
584
  },
638
585
  scripts: {
639
- test: "karma start",
586
+ test: "jest --maxWorkers=1 --detectOpenHandles",
640
587
  watch: "preconstruct watch",
641
588
  build: "preconstruct build",
642
589
  prepublishOnly: "npm run build",
@@ -658,23 +605,26 @@ var pkg = {
658
605
  "@babel/preset-typescript": "^7.13.0",
659
606
  "@preconstruct/cli": "^2.1.0",
660
607
  "@semantic-release/git": "^10.0.1",
661
- chai: "^4.2.0",
662
- karma: "^6.3.16",
663
- "karma-chai": "^0.1.0",
664
- "karma-chrome-launcher": "^3.1.0",
665
- "karma-mocha": "^2.0.1",
666
- "karma-typescript": "^5.0.3",
667
- mocha: "^8.1.1",
608
+ "@types/jest": "^29.4.0",
609
+ jest: "^29.4.1",
610
+ "jest-environment-jsdom": "^29.4.1",
668
611
  prettier: "^2.7.0",
669
- puppeteer: "^19.0.0",
670
- "semantic-release": "^19.0.3",
612
+ "semantic-release": "^20.1.0",
613
+ "ts-jest": "^29.0.5",
671
614
  typescript: "^4.2.4"
615
+ },
616
+ jest: {
617
+ preset: "ts-jest",
618
+ testMatch: [
619
+ "**/test/**/*.test.ts"
620
+ ],
621
+ testEnvironment: "jsdom"
672
622
  }
673
623
  };
674
624
 
675
625
  const defaultConfig = {
676
626
  language: 'no',
677
- version: pkg.version,
627
+ version: pkg?.version || "SNAPSHOT",
678
628
  linkTarget: '_self',
679
629
  currency: {
680
630
  value: 'Kr',
@@ -697,22 +647,24 @@ const defaultConfig = {
697
647
  limit: 50
698
648
  }
699
649
  };
700
-
701
650
  const mergeConfig = (a, b) => {
702
- return { ...a,
651
+ return {
652
+ ...a,
703
653
  ...b,
704
- currency: { ...a.currency,
654
+ currency: {
655
+ ...a.currency,
705
656
  ...b.currency
706
657
  },
707
- theme: { ...a.theme,
658
+ theme: {
659
+ ...a.theme,
708
660
  ...b.theme
709
661
  },
710
- api: { ...a.api,
662
+ api: {
663
+ ...a.api,
711
664
  ...b.api
712
665
  }
713
666
  };
714
667
  };
715
-
716
668
  const wrapperStyles = (className, theme) => `
717
669
  .${className} {
718
670
  display: flex;
@@ -725,33 +677,25 @@ const wrapperStyles = (className, theme) => `
725
677
  font-size: ${theme.fontSize};
726
678
  color: ${theme.color};
727
679
  }`;
728
-
729
680
  const embed = async configuration => {
730
681
  const _configuration = mergeConfig(defaultConfig, configuration);
731
-
732
682
  if (!_configuration.container || !_configuration.container.appendChild) {
733
683
  console.error("Invalid configuration");
734
684
  throw new Error("Invalid configuration");
735
685
  }
736
-
737
686
  if (_configuration.discounts) {
738
687
  return renderDeals(_configuration, _configuration.discounts);
739
688
  } else {
740
689
  const loader = createLoading(_configuration);
741
-
742
690
  try {
743
691
  configuration.container.appendChild(loader);
744
692
  const discounts = await fetchDiscounts(_configuration);
745
-
746
693
  _configuration.container.removeChild(loader);
747
-
748
694
  return renderDeals(_configuration, discounts);
749
695
  } catch (error) {
750
696
  configuration.container.removeChild(loader);
751
697
  const errorMessage = createError(_configuration);
752
-
753
698
  _configuration.container.appendChild(errorMessage);
754
-
755
699
  return {
756
700
  destroy: () => {
757
701
  configuration.container.removeChild(errorMessage);
@@ -760,7 +704,6 @@ const embed = async configuration => {
760
704
  }
761
705
  }
762
706
  };
763
-
764
707
  const renderDeals = (configuration, discounts) => {
765
708
  const wrapper = createElement({
766
709
  tag: "div",
@@ -770,12 +713,10 @@ const renderDeals = (configuration, discounts) => {
770
713
  discounts.forEach(discount => {
771
714
  const elem = createDiscount(discount, configuration);
772
715
  const webShopLink = findWebshopLink(discount);
773
-
774
716
  if (webShopLink) {
775
717
  elem.setAttribute('target', configuration?.linkTarget || '_self');
776
718
  elem.setAttribute('href', webShopLink.href);
777
719
  }
778
-
779
720
  wrapper.appendChild(elem);
780
721
  });
781
722
  configuration.container.appendChild(wrapper);