@rebilly/instruments 1.0.2-beta.8 → 2.1.1-beta.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 (211) hide show
  1. package/.babelrc +13 -4
  2. package/.eslintrc.js +3 -0
  3. package/.prettierrc.js +11 -0
  4. package/README.md +15 -314
  5. package/dist/events/base-event.js +6 -9
  6. package/dist/events/events.spec.js +4 -4
  7. package/dist/events/index.js +2 -1
  8. package/dist/functions/destroy.js +12 -14
  9. package/dist/functions/destroy.spec.js +3 -3
  10. package/dist/functions/mount/fetch-data.js +183 -0
  11. package/dist/functions/mount/fetch-data.spec.js +189 -0
  12. package/dist/functions/mount/index.js +158 -251
  13. package/dist/functions/mount/mount.spec.js +24 -121
  14. package/dist/functions/mount/setup-element.js +40 -0
  15. package/dist/functions/mount/setup-framepay.js +46 -0
  16. package/dist/functions/mount/setup-i18n.js +33 -0
  17. package/dist/functions/mount/setup-options.js +96 -0
  18. package/dist/functions/mount/setup-options.spec.js +66 -0
  19. package/dist/functions/mount/setup-storefront.js +34 -0
  20. package/dist/functions/mount/setup-styles.js +43 -0
  21. package/dist/functions/on.js +13 -4
  22. package/dist/functions/on.spec.js +19 -5
  23. package/dist/functions/purchase.js +139 -22
  24. package/dist/functions/purchase.spec.js +23 -19
  25. package/dist/functions/setup.js +85 -0
  26. package/dist/functions/setup.spec.js +87 -0
  27. package/dist/functions/show.js +31 -14
  28. package/dist/functions/show.spec.js +47 -18
  29. package/dist/functions/update.js +53 -27
  30. package/dist/functions/update.spec.js +40 -21
  31. package/dist/i18n/en.json +4 -1
  32. package/dist/i18n/es.json +4 -1
  33. package/dist/index.js +67 -56
  34. package/dist/index.spec.js +7 -27
  35. package/dist/loader/index.js +4 -3
  36. package/dist/storefront/index.js +33 -0
  37. package/dist/storefront/invoices.js +27 -0
  38. package/dist/storefront/models/base-model.js +18 -0
  39. package/dist/storefront/models/invoice-model.js +14 -0
  40. package/dist/storefront/models/plan-model.js +4 -35
  41. package/dist/storefront/models/product-model.js +4 -23
  42. package/dist/storefront/models/summary-model.js +12 -25
  43. package/dist/storefront/models/transaction-model.js +31 -0
  44. package/dist/storefront/payment-instruments.js +47 -0
  45. package/dist/storefront/payment-instruments.spec.js +55 -0
  46. package/dist/storefront/plans.js +15 -24
  47. package/dist/storefront/plans.spec.js +17 -44
  48. package/dist/storefront/products.js +16 -20
  49. package/dist/storefront/products.spec.js +25 -49
  50. package/dist/storefront/purchase.js +28 -16
  51. package/dist/storefront/purchase.spec.js +4 -22
  52. package/dist/storefront/ready-to-pay.js +26 -22
  53. package/dist/storefront/ready-to-pay.spec.js +25 -54
  54. package/dist/storefront/storefront.spec.js +1 -1
  55. package/dist/storefront/summary.js +27 -24
  56. package/dist/storefront/summary.spec.js +44 -86
  57. package/dist/storefront/transactions.js +27 -0
  58. package/dist/style/base/theme.js +3 -3
  59. package/dist/style/components/methods.js +43 -42
  60. package/dist/style/utils/color-values.js +1 -3
  61. package/dist/style/views/confirmation.js +0 -4
  62. package/dist/style/views/method-selector.js +1 -1
  63. package/dist/style/views/modal.js +3 -1
  64. package/dist/style/views/summary.js +5 -1
  65. package/dist/utils/format-currency.js +4 -2
  66. package/dist/utils/has-valid-css-selector.js +1 -1
  67. package/dist/utils/process-property-as-dom-element.js +0 -2
  68. package/dist/views/__snapshots__/summary.spec.js.snap +103 -113
  69. package/dist/views/common/iframe/base-iframe.js +10 -2
  70. package/dist/views/common/iframe/modal-iframe.js +44 -3
  71. package/dist/views/confirmation.js +44 -20
  72. package/dist/views/method-selector/express-methods/apple-pay.js +92 -0
  73. package/dist/views/method-selector/express-methods/google-pay.js +31 -0
  74. package/dist/views/method-selector/express-methods/paypal.js +19 -0
  75. package/dist/views/method-selector/generate-digital-wallet.js +68 -0
  76. package/dist/views/method-selector/generate-digital-wallet.spec.js +135 -0
  77. package/dist/views/method-selector/get-payment-methods.js +28 -8
  78. package/dist/views/method-selector/get-payment-methods.spec.js +25 -26
  79. package/dist/views/method-selector/index.js +55 -86
  80. package/dist/views/method-selector/method-selector.spec.js +80 -69
  81. package/dist/views/method-selector/mount-express-methods.js +38 -62
  82. package/dist/views/method-selector/mount-methods.js +18 -18
  83. package/dist/views/modal.js +21 -15
  84. package/dist/views/result.js +13 -16
  85. package/dist/views/summary.js +170 -114
  86. package/dist/views/summary.spec.js +72 -76
  87. package/package.json +5 -4
  88. package/src/events/base-event.js +15 -17
  89. package/src/events/events.spec.js +6 -4
  90. package/src/events/index.js +6 -3
  91. package/src/functions/destroy.js +12 -13
  92. package/src/functions/destroy.spec.js +30 -31
  93. package/src/functions/mount/fetch-data.js +148 -0
  94. package/src/functions/mount/fetch-data.spec.js +238 -0
  95. package/src/functions/mount/index.js +129 -244
  96. package/src/functions/mount/mount.spec.js +35 -139
  97. package/src/functions/mount/setup-element.js +26 -0
  98. package/src/functions/mount/setup-framepay.js +41 -0
  99. package/src/functions/mount/setup-i18n.js +19 -0
  100. package/src/functions/mount/setup-options.js +100 -0
  101. package/src/functions/mount/setup-options.spec.js +60 -0
  102. package/src/functions/mount/setup-storefront.js +24 -0
  103. package/src/functions/mount/setup-styles.js +30 -0
  104. package/src/functions/on.js +13 -8
  105. package/src/functions/on.spec.js +30 -17
  106. package/src/functions/purchase.js +101 -19
  107. package/src/functions/purchase.spec.js +18 -18
  108. package/src/functions/setup.js +48 -0
  109. package/src/functions/setup.spec.js +98 -0
  110. package/src/functions/show.js +20 -10
  111. package/src/functions/show.spec.js +43 -22
  112. package/src/functions/update.js +50 -27
  113. package/src/functions/update.spec.js +57 -22
  114. package/src/i18n/en.json +4 -1
  115. package/src/i18n/es.json +4 -1
  116. package/src/i18n/i18n.spec.js +6 -4
  117. package/src/i18n/index.js +14 -11
  118. package/src/index.js +41 -52
  119. package/src/index.spec.js +8 -37
  120. package/src/loader/index.js +51 -47
  121. package/src/loader/loader.spec.js +26 -19
  122. package/src/storefront/index.js +37 -7
  123. package/src/storefront/invoices.js +11 -0
  124. package/src/storefront/models/base-model.js +10 -0
  125. package/src/storefront/models/invoice-model.js +3 -0
  126. package/src/storefront/models/plan-model.js +3 -35
  127. package/src/storefront/models/product-model.js +3 -23
  128. package/src/storefront/models/ready-to-pay-model.js +3 -3
  129. package/src/storefront/models/summary-model.js +15 -29
  130. package/src/storefront/models/transaction-model.js +19 -0
  131. package/src/storefront/payment-instruments.js +30 -0
  132. package/src/storefront/payment-instruments.spec.js +69 -0
  133. package/src/storefront/plans.js +16 -23
  134. package/src/storefront/plans.spec.js +25 -54
  135. package/src/storefront/products.js +18 -22
  136. package/src/storefront/products.spec.js +23 -54
  137. package/src/storefront/purchase.js +14 -14
  138. package/src/storefront/purchase.spec.js +17 -29
  139. package/src/storefront/ready-to-pay.js +26 -23
  140. package/src/storefront/ready-to-pay.spec.js +41 -71
  141. package/src/storefront/storefront.spec.js +1 -1
  142. package/src/storefront/summary.js +26 -22
  143. package/src/storefront/summary.spec.js +60 -109
  144. package/src/storefront/transactions.js +11 -0
  145. package/src/style/base/theme.js +10 -8
  146. package/src/style/base/theme.spec.js +4 -2
  147. package/src/style/browserslist.js +1 -3
  148. package/src/style/components/button.js +3 -1
  149. package/src/style/components/forms/checkbox.js +3 -1
  150. package/src/style/components/index.js +1 -1
  151. package/src/style/components/loader.js +3 -1
  152. package/src/style/components/methods.js +43 -42
  153. package/src/style/helpers/index.js +1 -1
  154. package/src/style/index.js +2 -1
  155. package/src/style/utils/color-values.js +4 -4
  156. package/src/style/vendor/framepay.js +1 -1
  157. package/src/style/vendor/postmate.js +1 -1
  158. package/src/style/views/confirmation.js +0 -4
  159. package/src/style/views/index.js +1 -1
  160. package/src/style/views/method-selector.js +1 -1
  161. package/src/style/views/modal.js +4 -2
  162. package/src/style/views/summary.js +5 -1
  163. package/src/utils/add-dom-element.js +12 -13
  164. package/src/utils/format-currency.js +6 -2
  165. package/src/utils/has-valid-css-selector.js +2 -2
  166. package/src/utils/is-dom-element.js +1 -1
  167. package/src/utils/process-property-as-dom-element.js +27 -24
  168. package/src/utils/sleep.js +1 -1
  169. package/src/views/__snapshots__/summary.spec.js.snap +103 -113
  170. package/src/views/common/iframe/base-iframe.js +12 -4
  171. package/src/views/common/iframe/event-listeners.js +6 -6
  172. package/src/views/common/iframe/index.js +1 -1
  173. package/src/views/common/iframe/method-iframe.js +3 -6
  174. package/src/views/common/iframe/modal-iframe.js +42 -6
  175. package/src/views/common/iframe/view-iframe.js +3 -5
  176. package/src/views/common/render-utilities.js +3 -3
  177. package/src/views/confirmation.js +34 -25
  178. package/src/views/method-selector/express-methods/apple-pay.js +78 -0
  179. package/src/views/method-selector/express-methods/google-pay.js +24 -0
  180. package/src/views/method-selector/express-methods/paypal.js +7 -0
  181. package/src/views/method-selector/generate-digital-wallet.js +51 -0
  182. package/src/views/method-selector/generate-digital-wallet.spec.js +135 -0
  183. package/src/views/method-selector/get-method-data.js +7 -4
  184. package/src/views/method-selector/get-payment-methods.js +38 -29
  185. package/src/views/method-selector/get-payment-methods.spec.js +26 -33
  186. package/src/views/method-selector/index.js +70 -99
  187. package/src/views/method-selector/method-selector.spec.js +88 -78
  188. package/src/views/method-selector/mount-express-methods.js +36 -60
  189. package/src/views/method-selector/mount-methods.js +32 -21
  190. package/src/views/modal.js +37 -23
  191. package/src/views/result.js +12 -15
  192. package/src/views/summary.js +169 -101
  193. package/src/views/summary.spec.js +99 -74
  194. package/tests/async-utilities.js +22 -0
  195. package/tests/mocks/rebilly-instruments-mock.js +89 -77
  196. package/tests/mocks/storefront-api-mock.js +8 -0
  197. package/tests/mocks/storefront-mock.js +17 -0
  198. package/dist/events/purchase-completed.js +0 -24
  199. package/dist/functions/initialize.js +0 -82
  200. package/dist/functions/initialize.spec.js +0 -34
  201. package/dist/functions/mount/fetch-summary-data.js +0 -31
  202. package/dist/functions/mount/fetch-summary-data.spec.js +0 -45
  203. package/dist/views/method-selector/process-digital-wallet-options.js +0 -35
  204. package/dist/views/method-selector/process-digital-wallet-options.spec.js +0 -80
  205. package/src/events/purchase-completed.js +0 -11
  206. package/src/functions/initialize.js +0 -74
  207. package/src/functions/initialize.spec.js +0 -38
  208. package/src/functions/mount/fetch-summary-data.js +0 -26
  209. package/src/functions/mount/fetch-summary-data.spec.js +0 -46
  210. package/src/views/method-selector/process-digital-wallet-options.js +0 -16
  211. package/src/views/method-selector/process-digital-wallet-options.spec.js +0 -94
@@ -2,46 +2,59 @@ import { ModalIframe } from './common/iframe';
2
2
 
3
3
  const modalTemplate = (isRedirect, method) => `
4
4
  <div class="rebilly-instruments-modal-overlay">
5
- <div class="rebilly-instruments-modal-container ${method ? `rebilly-instruments-${method}` : ''} ${isRedirect ? 'is-redirect' : ''}">
5
+ <div class="rebilly-instruments-modal-container ${
6
+ method ? `rebilly-instruments-${method}` : ''
7
+ } ${isRedirect ? 'is-redirect' : ''}">
8
+ ${isRedirect ? '' : `
6
9
  <svg class="rebilly-instruments-modal-close" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
7
10
  <path d="m15 13.5858 7.2929-7.293c.3905-.3904 1.0237-.3904 1.4142 0 .3905.3906.3905 1.0238 0 1.4143L16.4142 15l7.293 7.2929c.3904.3905.3904 1.0237 0 1.4142-.3906.3905-1.0238.3905-1.4143 0L15 16.4142l-7.2929 7.293c-.3905.3904-1.0237.3904-1.4142 0-.3905-.3906-.3905-1.0238 0-1.4143L13.5858 15l-7.293-7.2929c-.3904-.3905-.3904-1.0237 0-1.4142.3906-.3905 1.0238-.3905 1.4143 0L15 13.5858Z" fill-rule="nonzero"/>
8
11
  </svg>
12
+ `}
9
13
  <div class="rebilly-instruments-modal-content"></div>
10
14
  </div>
11
15
  </div>
12
16
  `;
13
17
 
14
- export async function MountModal({
18
+ export async function mountModal({
15
19
  name = '',
16
20
  url = '',
17
21
  model = {},
18
22
  classListArray = [],
19
- close = () => {}
23
+ close = () => {},
24
+ state = null
20
25
  } = {}) {
21
- const {method} = model?.method;
26
+ const method = model?.method?.method;
22
27
  const isRedirect = name === 'rebilly-instruments-approval-url';
23
- this.form.insertAdjacentHTML('beforeend', modalTemplate(isRedirect, method));
24
-
25
- const modalOverlay = document.querySelector('.rebilly-instruments-modal-overlay');
26
- const modalContainer = document.querySelector('.rebilly-instruments-modal-container');
27
- const closeButton = document.querySelector('.rebilly-instruments-modal-close');
28
- const modalContent = document.querySelector('.rebilly-instruments-modal-content');
28
+ state.form.insertAdjacentHTML('beforeend', modalTemplate(isRedirect, method));
29
+
30
+ const modalOverlay = document.querySelector(
31
+ '.rebilly-instruments-modal-overlay'
32
+ );
33
+ const modalContainer = document.querySelector(
34
+ '.rebilly-instruments-modal-container'
35
+ );
36
+ const closeButton = document.querySelector(
37
+ '.rebilly-instruments-modal-close'
38
+ );
39
+ const modalContent = document.querySelector(
40
+ '.rebilly-instruments-modal-content'
41
+ );
29
42
 
30
43
  document.body.style.overflow = 'hidden';
31
44
  setTimeout(() => {
32
45
  modalOverlay.classList.add('is-visible');
33
46
  modalContainer.classList.add('is-visible');
34
47
  }, 240);
35
- this.loader.addDOMElement({section: 'modal', el: modalContent});
36
- this.loader.startLoading({section: 'modal', id: 'modal-content'});
48
+ state.loader.addDOMElement({ section: 'modal', el: modalContent });
49
+ state.loader.startLoading({ section: 'modal', id: 'modal-content' });
37
50
 
38
51
  const injectedModel = {
39
- configs: this.configs,
40
- options: this.options,
52
+ options: state.options,
41
53
  ...model
42
- }
54
+ };
43
55
 
44
56
  const iframe = await new ModalIframe({
57
+ state,
45
58
  name,
46
59
  url,
47
60
  model: injectedModel,
@@ -54,20 +67,21 @@ export async function MountModal({
54
67
  modalOverlay.classList.remove('is-visible');
55
68
  setTimeout(() => {
56
69
  document.body.style.overflow = 'auto';
57
- modalOverlay.children.forEach(child => child.remove());
70
+ modalOverlay.children.forEach((child) => child.remove());
58
71
  modalOverlay.remove();
59
72
  close(...args);
60
- iframe.component.destroy();
73
+ iframe.destroy();
61
74
  }, 300);
62
- }
63
-
75
+ };
76
+
64
77
  iframe.bindEventListeners({
65
78
  close: closeModal,
66
- loader: this.loader
79
+ loader: state.loader
67
80
  });
68
81
 
69
- modalOverlay.addEventListener('click', closeModal);
70
- closeButton.addEventListener('click', closeModal);
82
+ if (!isRedirect) {
83
+ closeButton.addEventListener('click', closeModal);
84
+ }
71
85
 
72
86
  return iframe;
73
- }
87
+ }
@@ -1,24 +1,21 @@
1
1
  import { ViewIframe } from './common/iframe';
2
2
  import { replaceContent } from './common/render-utilities';
3
3
 
4
- export async function MountResult({
5
- purchase,
6
- mainStyle
7
- }) {
4
+ export async function mountResult({ payload, state }) {
8
5
  const resultContainerClassName = 'rebilly-instruments-result';
9
- replaceContent(this.form, `<div class="${resultContainerClassName}"></div>`);
6
+ replaceContent(state.form, `<div class="${resultContainerClassName}"></div>`);
7
+
8
+ state.loader.startLoading({ id: 'result' });
10
9
 
11
- this.loader.startLoading({id: 'result'});
12
-
13
10
  const container = document.querySelector(`.${resultContainerClassName}`);
14
- const { paymentMethodsUrl } = this.options._computed;
11
+ const { paymentMethodsUrl } = state.options._computed;
15
12
 
16
13
  const model = {
17
- configs: this.configs,
18
- options: this.options,
19
- mainStyle,
20
- purchase
14
+ options: state.options,
15
+ mainStyle: state.mainStyle,
16
+ [state.options.transactionType]: payload
21
17
  };
18
+
22
19
  const iframe = await new ViewIframe({
23
20
  name: 'rebilly-instruments-result',
24
21
  url: `${paymentMethodsUrl}/result`,
@@ -26,8 +23,8 @@ export async function MountResult({
26
23
  model
27
24
  });
28
25
  iframe.bindEventListeners({
29
- loader: this.loader
26
+ loader: state.loader
30
27
  });
31
28
 
32
- this.iframeComponents.push(iframe);
33
- };
29
+ state.iframeComponents.push(iframe);
30
+ }
@@ -1,98 +1,170 @@
1
1
  import { collectData } from '@rebilly/risk-data-collector';
2
2
  import { formatCurrency } from '../utils';
3
- import { FetchSummaryData } from '../functions/mount/fetch-summary-data';
4
-
5
- export const baseSummaryHTML = `
6
- <div class="rebilly-instruments-content">
7
- <div class="rebilly-instruments-summary-line-items"></div>
8
- <div class="rebilly-instruments-summary-breakdown"></div>
9
- </div>
10
- `;
11
-
12
- export function MountSummary({summary, plans, products} = {}) {
13
- this.summary.innerHTML += baseSummaryHTML;
14
- const itemsContainer = document.querySelector('.rebilly-instruments-summary-line-items');
15
- const summaryBreakdown = document.querySelector('.rebilly-instruments-summary-breakdown')
16
- const {intent: {items: optionItems}} = this.options;
17
- const hasThumbnails = optionItems.some(optionItem => optionItem.thumbnail);
3
+ import { fetchData } from '../functions/mount/fetch-data';
18
4
 
5
+ export function lineItemHTML({state, lineItem}) {
6
+ const {products} = state.data;
19
7
  const {
20
- lineItems,
21
- currency,
22
- discountsAmount,
23
- shippingAmount,
24
- subtotalAmount,
25
- taxAmount,
26
- total
27
- } = summary;
28
-
29
- lineItems.forEach(lineItem => {
30
- const {thumbnail} = optionItems.find(optionItem => optionItem.planId === lineItem.planId);
31
- const {name} = plans.find(plan => plan.id === lineItem.planId);
32
- const {description} = products.find(product => product.id === lineItem.productId);
33
-
34
- itemsContainer.innerHTML += `
35
- <div class="rebilly-instruments-summary-line-item">
36
- ${hasThumbnails ? `
37
- <figure class="rebilly-instruments-summary-line-item-figure">
38
- ${thumbnail ? `<img src="${thumbnail}" alt="${name}">` : ''}
39
- </figure>
40
- ` : ''}
41
- <div class="rebilly-instruments-summary-line-item-synopsis">
42
- <p class="rebilly-instruments-summary-line-item-synopsis-title">${name}</p>
43
- ${description ? `
44
- <p class="rebilly-instruments-summary-line-item-synopsis-description">${description}</p>
45
- ` : ''}
46
- </div>
47
- <div class="rebilly-instruments-summary-line-item-price-breakdown">
48
- <p class="rebilly-instruments-summary-line-item-price-breakdown-quantity">${lineItem.quantity}</p>
49
- <svg class="rebilly-instruments-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
50
- <path d="M12 10.5858l2.8284-2.8284c.3906-.3906 1.0237-.3906 1.4142 0 .3906.3905.3906 1.0236 0 1.4142L13.4142 12l2.8284 2.8284c.3906.3906.3906 1.0237 0 1.4142-.3905.3906-1.0236.3906-1.4142 0L12 13.4142l-2.8284 2.8284c-.3906.3906-1.0237.3906-1.4142 0-.3906-.3905-.3906-1.0236 0-1.4142L10.5858 12 7.7574 9.1716c-.3906-.3906-.3906-1.0237 0-1.4142.3905-.3906 1.0236-.3906 1.4142 0L12 10.5858z" fill-rule="nonzero"/>
51
- </svg>
52
- <p class="rebilly-instruments-summary-line-item-price-breakdown-unit-price">${formatCurrency(lineItem.unitPrice, currency)}</p>
53
- </div>
8
+ currency = null,
9
+ } = state.data?.amountAndCurrency;
10
+
11
+ const {
12
+ items: optionItems = []
13
+ } = state.options;
14
+ const hasThumbnails = optionItems?.some((optionItem) => optionItem.thumbnail);
15
+ const { thumbnail = null } = optionItems.find(
16
+ (optionItem) => optionItem.planId === lineItem.planId
17
+ ) || {};
18
+
19
+ const { name } = products.find(
20
+ (product) => product.id === lineItem.productId
21
+ );
22
+
23
+ return `
24
+ <div class="rebilly-instruments-summary-line-item">
25
+ ${
26
+ hasThumbnails
27
+ ? `
28
+ <figure class="rebilly-instruments-summary-line-item-figure">
29
+ ${thumbnail ? `<img src="${thumbnail}" alt="${name}">` : ''}
30
+ </figure>
31
+ `
32
+ : ''
33
+ }
34
+ <div class="rebilly-instruments-summary-line-item-synopsis">
35
+ <p class="rebilly-instruments-summary-line-item-synopsis-title">${name}</p>
36
+ ${
37
+ lineItem.description
38
+ ? `
39
+ <p class="rebilly-instruments-summary-line-item-synopsis-description">${lineItem.description}</p>
40
+ `
41
+ : ''
42
+ }
43
+ </div>
44
+ <div class="rebilly-instruments-summary-line-item-price-breakdown">
45
+ <p class="rebilly-instruments-summary-line-item-price-breakdown-quantity">${
46
+ lineItem.quantity
47
+ }</p>
48
+ <svg class="rebilly-instruments-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
49
+ <path d="M12 10.5858l2.8284-2.8284c.3906-.3906 1.0237-.3906 1.4142 0 .3906.3905.3906 1.0236 0 1.4142L13.4142 12l2.8284 2.8284c.3906.3906.3906 1.0237 0 1.4142-.3905.3906-1.0236.3906-1.4142 0L12 13.4142l-2.8284 2.8284c-.3906.3906-1.0237.3906-1.4142 0-.3906-.3905-.3906-1.0236 0-1.4142L10.5858 12 7.7574 9.1716c-.3906-.3906-.3906-1.0237 0-1.4142.3905-.3906 1.0236-.3906 1.4142 0L12 10.5858z" fill-rule="nonzero"/>
50
+ </svg>
51
+ <p class="rebilly-instruments-summary-line-item-price-breakdown-unit-price">${formatCurrency(
52
+ lineItem.unitPrice,
53
+ currency
54
+ )}</p>
54
55
  </div>
55
- `;
56
- });
57
- summaryBreakdown.innerHTML = `
58
- <table>
59
- <colgroup>
60
- <col>
61
- <col>
62
- </colgroup>
63
- <tr class="rebilly-instruments-summary-breakdown-sub-total">
64
- <td data-rebilly-i18n="summary.subTotal">Sub Total</td>
65
- <td>${formatCurrency(subtotalAmount, currency)}</td>
66
- </tr>
67
- <tr class="rebilly-instruments-summary-breakdown-discounts">
68
- <td data-rebilly-i18n="summary.discounts">Discounts</td>
69
- <td>${formatCurrency(discountsAmount, currency)}</td>
70
- </tr>
71
- <tr class="rebilly-instruments-summary-breakdown-taxes">
72
- <td data-rebilly-i18n="summary.taxes">Taxes</td>
73
- <td>${formatCurrency(taxAmount, currency)}</td>
74
- </tr>
75
- <tr class="rebilly-instruments-summary-breakdown-taxes">
76
- <td data-rebilly-i18n="summary.shipping">Shipping</td>
77
- <td>${formatCurrency(shippingAmount, currency)}</td>
78
- </tr>
79
- </table>
80
- <div class="rebilly-instruments-summary-breakdown-total">
81
- <p data-rebilly-i18n="summary.total">Total</p>
82
- <p class="rebilly-instruments-summary-breakdown-total-amount">
83
- <span class="rebilly-instruments-summary-breakdown-total-amount-currency">${currency}</span>${formatCurrency(total, currency)}
84
- </p>
85
56
  </div>
86
57
  `;
58
+ }
59
+
60
+ export function summaryBreakdownHTML({state, element}) {
61
+ const {
62
+ amount = null,
63
+ currency = null,
64
+ discountsAmount = null,
65
+ shippingAmount = null,
66
+ subtotalAmount = null,
67
+ taxAmount = null,
68
+ } = {
69
+ ...state.data.amountAndCurrency,
70
+ ...state.data.summaryItems
71
+ };
72
+
73
+ let table = null;
74
+ [
75
+ {
76
+ label: 'Sub Total',
77
+ class: 'sub-total',
78
+ i18n: 'summary.subTotal',
79
+ value: subtotalAmount
80
+ },
81
+ {
82
+ label: 'Discounts',
83
+ class: 'discounts',
84
+ i18n: 'summary.discounts',
85
+ value: discountsAmount
86
+ },
87
+ {
88
+ label: 'Taxes',
89
+ class: 'taxes',
90
+ i18n: 'summary.taxes',
91
+ value: taxAmount
92
+ },
93
+ {
94
+ label: 'Shipping',
95
+ class: 'shipping',
96
+ i18n: 'summary.shipping',
97
+ value: shippingAmount
98
+ },
99
+ ].filter(({value}) => value !== null)
100
+ .forEach(item => {
101
+ if (!table) {
102
+ table = document.createElement('table');
103
+ table.insertAdjacentHTML('beforeend', `
104
+ <colgroup>
105
+ <col>
106
+ <col>
107
+ </colgroup>
108
+ `);
109
+ }
110
+
111
+ const row = document.createElement('tr');
112
+ row.setAttribute('class', `rebilly-instruments-summary-breakdown-${item.class}`)
113
+ row.insertAdjacentHTML('beforeend', `
114
+ <td data-rebilly-i18n="${item.i18n}">${item.label}</td>
115
+ <td>${formatCurrency(item.value, currency)}</td>
116
+ `);
117
+ table.appendChild(row);
118
+ });
119
+ if (table) {
120
+ element.appendChild(table);
121
+ }
122
+
123
+ const totalElement = document.createElement('div');
124
+ totalElement.setAttribute('class', 'rebilly-instruments-summary-breakdown-total');
125
+ totalElement.insertAdjacentHTML('beforeend', `
126
+ <p data-rebilly-i18n="summary.total">Total</p>
127
+ <p class="rebilly-instruments-summary-breakdown-total-amount">
128
+ <span class="rebilly-instruments-summary-breakdown-total-amount-currency">
129
+ ${currency}
130
+ </span>
131
+ ${formatCurrency(amount, currency)}
132
+ <p>
133
+ `);
134
+ element.appendChild(totalElement);
135
+
136
+ return element;
137
+ }
138
+
139
+ export async function mountSummary({ state }) {
140
+ const contentContainer = document.createElement('div');
141
+ contentContainer.setAttribute('class', 'rebilly-instruments-content');
142
+
143
+ const lineItems = state.data.summaryLineItems;
144
+ if (lineItems) {
145
+ const lineItemsElement = document.createElement('div');
146
+ lineItemsElement.setAttribute('class', 'rebilly-instruments-summary-line-items');
147
+
148
+ lineItems.forEach((lineItem) => {
149
+ lineItemsElement.insertAdjacentHTML('beforeend', lineItemHTML({ state, lineItem }));
150
+ });
151
+ contentContainer.appendChild(lineItemsElement);
152
+ }
87
153
 
88
- this.translate.translateItems();
154
+ const summaryBreakdownElement = document.createElement('div');
155
+ summaryBreakdownElement.setAttribute('class', 'rebilly-instruments-summary-breakdown');
156
+ contentContainer.appendChild(summaryBreakdownHTML({ state, element: summaryBreakdownElement}));
157
+
158
+ state.summary.appendChild(contentContainer);
159
+ state.summary.style.minHeight = '';
89
160
 
90
- this.loader.stopLoading({section: 'summary', id: 'initSummary'});
91
- };
161
+ state.translate.translateItems();
162
+ state.loader.stopLoading({ section: 'summary', id: 'initSummary' });
163
+ }
92
164
 
93
- export async function UpdateSummary(instrument = null) {
94
- this.loader.startLoading({section: 'summary', id: 'initSummary'});
95
- const {riskMetadata} = await collectData();
165
+ export async function updateSummary({ state, instrument = null }) {
166
+ state.loader.startLoading({ section: 'summary', id: 'initSummary' });
167
+ const { riskMetadata } = await collectData();
96
168
 
97
169
  let summaryPayload;
98
170
 
@@ -102,25 +174,21 @@ export async function UpdateSummary(instrument = null) {
102
174
  };
103
175
  }
104
176
 
105
- const {
106
- readyToPay,
107
- summary: summaryData,
108
- plans,
109
- products
110
- } = await FetchSummaryData.call(this, {
177
+ state.data = await fetchData({
111
178
  riskMetadata,
112
- summaryPayload
179
+ summaryPayload,
180
+ state
113
181
  });
114
182
 
115
- this.summaryOptions = {
116
- summary: summaryData,
117
- readyToPay,
118
- plans,
119
- products,
120
- };
183
+ if (state.data.transaction && state.data.transaction?.type === 'setup') {
184
+ state.options.transactionType = 'setup';
185
+ }
121
186
 
122
- const itemsContainer = this.summary.querySelector('.rebilly-instruments-content');
187
+ const itemsContainer = state.summary.querySelector(
188
+ '.rebilly-instruments-content'
189
+ );
123
190
  itemsContainer?.remove();
124
191
 
125
- MountSummary.call(this, this.summaryOptions)
126
- }
192
+ mountSummary({ state });
193
+ }
194
+
@@ -3,7 +3,8 @@ import PlanModel from '@/storefront/models/plan-model';
3
3
  import ProductModel from '@/storefront/models/product-model';
4
4
  import { Loader } from '../loader';
5
5
  import { Translate } from '../i18n';
6
- import { MountSummary } from './summary';
6
+ import { mountSummary } from './summary';
7
+ import { DataInstance } from '../functions/mount/fetch-data';
7
8
 
8
9
  describe('Summary component', () => {
9
10
  let summaryElement;
@@ -11,110 +12,134 @@ describe('Summary component', () => {
11
12
  document.body.innerHTML = '';
12
13
  summaryElement = document.createElement('div');
13
14
  document.body.append(summaryElement);
14
- })
15
+ });
15
16
 
16
17
  class TestMountSummaryInstance {
17
18
  constructor({
18
- configs = {},
19
19
  options = {},
20
20
  summary = summaryElement,
21
21
  loader = new Loader(),
22
22
  translate = new Translate(),
23
+ data = {},
23
24
  } = {}) {
24
- this.configs = configs;
25
25
  this.options = options;
26
26
  this.summary = summary;
27
27
  this.loader = loader;
28
28
  this.translate = translate;
29
- }
30
-
31
- mountSummary(summaryProductsPlans, ...args) {
32
- this.loader.DOM.summary = this.summary;
33
- return MountSummary.apply(this, args.concat(summaryProductsPlans));
29
+ this.data = new DataInstance({
30
+ state: {options},
31
+ previewPurchase: new SummaryModel({
32
+ currency: 'USD',
33
+ lineItems: [
34
+ {
35
+ type: 'debit',
36
+ description: 'My Awesome Product',
37
+ unitPrice: 30,
38
+ quantity: 1,
39
+ price: 30,
40
+ productId: 'test-product-1',
41
+ planId: 'my-awesome-product'
42
+ },
43
+ {
44
+ type: 'debit',
45
+ description: 'Awesome T-Shirt',
46
+ unitPrice: 20,
47
+ quantity: 2,
48
+ price: 40,
49
+ productId: 'test-product-2',
50
+ planId: 'awesome-t-shirt'
51
+ }
52
+ ],
53
+ subtotalAmount: 70,
54
+ taxAmount: 0,
55
+ shippingAmount: 0,
56
+ discountsAmount: 0,
57
+ total: 70
58
+ }),
59
+ plans: [
60
+ new PlanModel({
61
+ name: 'My Awesome Product',
62
+ id: 'my-awesome-product',
63
+ productId: 'test-product-1'
64
+ }),
65
+ new PlanModel({
66
+ name: 'My Awesome T-Shirt',
67
+ id: 'awesome-t-shirt',
68
+ productId: 'test-product-2'
69
+ })
70
+ ],
71
+ products: [
72
+ new ProductModel({
73
+ name: 'My Awesome Product',
74
+ id: 'test-product-1'
75
+ }),
76
+ new ProductModel({
77
+ name: 'My Awesome T-Shirt',
78
+ id: 'test-product-2'
79
+ })
80
+ ],
81
+ ...data
82
+ });
34
83
  }
35
84
  }
36
85
 
37
86
  const options = {
38
- intent: {
39
- countryCode: 'US',
40
- items: [
41
- {
42
- planId: 'my-awesome-product',
43
- quantity: 1,
44
- thumbnail: '',
45
- },
46
- {
47
- planId: 'awesome-t-shirt',
48
- quantity: 2,
49
- thumbnail: '',
50
- }
51
- ]
52
- }
87
+ websiteId: 'test-website-id',
88
+ countryCode: 'US',
89
+ items: [
90
+ {
91
+ planId: 'my-awesome-product',
92
+ quantity: 1,
93
+ thumbnail: ''
94
+ },
95
+ {
96
+ planId: 'awesome-t-shirt',
97
+ quantity: 2,
98
+ thumbnail: ''
99
+ }
100
+ ]
53
101
  };
54
102
 
55
- const summaryData = new SummaryModel({
56
- currency: "USD",
57
- lineItems: [{
58
- type: "debit",
59
- description: "My Awesome Product",
60
- unitPrice: 30,
61
- quantity: 1,
62
- price: 30,
63
- productId: "test-product-1",
64
- planId: "my-awesome-product"
65
- }, {
66
- type: "debit",
67
- description: "Awesome T-Shirt",
68
- unitPrice: 20,
69
- quantity: 2,
70
- price: 40,
71
- productId: "test-product-2",
72
- planId: "awesome-t-shirt"
73
- }],
74
- subtotalAmount: 70,
75
- taxAmount: 0,
76
- shippingAmount: 0,
77
- discountsAmount: 0,
78
- total: 70
79
- });
80
-
81
- const planData = [
82
- new PlanModel({name: 'My Awesome Product', id: 'my-awesome-product', productId: 'test-product-1'}),
83
- new PlanModel({name: 'My Awesome T-Shirt', id: 'awesome-t-shirt', productId: 'test-product-2'})
84
- ];
85
-
86
- const productData = [
87
- new ProductModel({description: 'My Awesome Product', id: 'test-product-1'}),
88
- new ProductModel({description: 'My Awesome T-Shirt', id: 'test-product-2'})
89
- ];
103
+ it('should render the summary correctly', () => {
104
+ const mountSummaryInstance = new TestMountSummaryInstance({
105
+ options
106
+ });
90
107
 
91
- const configs = {
92
- websiteId: 'test-website-id'
93
- };
108
+ //TODO: improve design to avoid this manual change
109
+ mountSummaryInstance.loader.DOM.summary = mountSummaryInstance.summary;
94
110
 
95
- it('should render the summary correctly', () => {
96
- const mountSummaryInstance = new TestMountSummaryInstance({configs, options});
97
- mountSummaryInstance.mountSummary({summary: summaryData, plans: planData, products: productData});
111
+ mountSummary({ state: mountSummaryInstance });
98
112
 
99
113
  // Number of line items
100
- const itemsContainer = document.querySelector('.rebilly-instruments-summary-line-items');
114
+ const itemsContainer = document.querySelector(
115
+ '.rebilly-instruments-summary-line-items'
116
+ );
101
117
  expect(itemsContainer).toMatchSnapshot();
102
118
 
103
119
  // Breakdown
104
- const summaryBreakdown = document.querySelector('.rebilly-instruments-summary-breakdown');
120
+ const summaryBreakdown = document.querySelector(
121
+ '.rebilly-instruments-summary-breakdown'
122
+ );
105
123
  expect(summaryBreakdown).toMatchSnapshot();
106
124
  });
107
125
 
108
126
  it('should not render the plan description if its falsy', () => {
109
- const mountSummaryInstance = new TestMountSummaryInstance({configs, options});
110
-
127
+ const mountSummaryInstance = new TestMountSummaryInstance({
128
+ options
129
+ });
130
+
111
131
  // Making one product description falsy
112
- productData[0].description = null;
113
- mountSummaryInstance.mountSummary({summary: summaryData, plans: planData, products: productData});
132
+ mountSummaryInstance.data.previewPurchase.lineItems[0].description = null;
133
+
134
+ //TODO: improve design to avoid this manual change
135
+ mountSummaryInstance.loader.DOM.summary = mountSummaryInstance.summary;
136
+
137
+ mountSummary({ state: mountSummaryInstance });
114
138
 
115
139
  // Check that only one description is render
116
- const itemsSynopsysDescription = document.querySelectorAll('.rebilly-instruments-summary-line-item-synopsis-description');
140
+ const itemsSynopsysDescription = document.querySelectorAll(
141
+ '.rebilly-instruments-summary-line-item-synopsis-description'
142
+ );
117
143
  expect(itemsSynopsysDescription.length).toEqual(1);
118
-
119
144
  });
120
- })
145
+ });