@rebilly/instruments 3.13.2-beta.0 → 3.13.4-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.
- package/README.md +1 -1
- package/dist/index.js +66 -43
- package/dist/index.min.js +66 -43
- package/package.json +6 -3
- package/src/functions/destroy.js +2 -8
- package/src/functions/mount/fetch-data.js +2 -9
- package/src/functions/mount/index.js +10 -15
- package/src/functions/mount/mount.spec.js +11 -10
- package/src/functions/mount/setup-framepay-theme.js +72 -30
- package/src/functions/mount/setup-options.js +2 -2
- package/src/functions/mount/{setup-styles-vars.js → setup-styles.js} +7 -9
- package/src/functions/purchase.js +5 -2
- package/src/functions/setup.js +6 -3
- package/src/functions/show.js +2 -2
- package/src/functions/show.spec.js +4 -4
- package/src/functions/update.spec.js +3 -4
- package/src/instance.js +1 -4
- package/src/loader/index.js +33 -57
- package/src/storefront/index.js +5 -2
- package/src/storefront/payment-instruments.js +0 -7
- package/src/style/base/__snapshots__/theme.spec.js.snap +220 -136
- package/src/style/base/default-theme.js +14 -187
- package/src/style/base/index.js +79 -487
- package/src/style/base/theme.js +4 -3
- package/src/style/base/theme.spec.js +3 -2
- package/src/style/browserslist.js +1 -0
- package/src/style/components/accordion.js +140 -0
- package/src/style/components/address.js +55 -0
- package/src/style/components/button.js +117 -0
- package/src/style/components/divider.js +39 -0
- package/src/style/components/forms/checkbox.js +75 -0
- package/src/style/components/forms/field.js +56 -0
- package/src/style/components/forms/form.js +18 -0
- package/src/style/components/forms/input.js +77 -0
- package/src/style/components/forms/label.js +55 -0
- package/src/style/components/forms/radio.js +80 -0
- package/src/style/components/forms/select.js +86 -0
- package/src/style/components/forms/validation.js +72 -0
- package/src/style/components/icons.js +13 -0
- package/src/style/components/index.js +39 -0
- package/src/style/components/loader.js +41 -0
- package/src/style/components/methods.js +97 -0
- package/src/style/components/overlay.js +24 -0
- package/src/style/helpers/index.js +54 -0
- package/src/style/index.js +24 -4
- package/src/style/payment-instruments/content.js +8 -0
- package/src/style/payment-instruments/index.js +14 -0
- package/src/style/payment-instruments/payment-card.js +27 -0
- package/src/style/payment-instruments/payment-instrument-list.js +44 -0
- package/src/style/payment-instruments/payment-instrument.js +55 -0
- package/src/style/utils/color-values.js +1 -1
- package/src/style/utils/remove-empty-null.js +10 -0
- package/src/style/vendor/framepay.js +28 -0
- package/src/style/vendor/postmate.js +18 -0
- package/src/style/views/confirmation.js +26 -0
- package/src/style/views/index.js +16 -0
- package/src/style/views/method-selector.js +11 -0
- package/src/style/views/modal.js +91 -0
- package/src/style/views/result.js +52 -0
- package/src/style/views/summary.js +118 -0
- package/src/views/__snapshots__/summary.spec.js.snap +246 -0
- package/src/views/common/iframe/base-iframe.js +2 -3
- package/src/views/common/iframe/event-listeners.js +9 -12
- package/src/views/common/iframe/method-iframe.js +1 -3
- package/src/views/common/iframe/modal-iframe.js +2 -4
- package/src/views/common/iframe/view-iframe.js +1 -3
- package/src/views/confirmation.js +7 -12
- package/src/views/method-selector/express-methods/apple-pay.js +92 -0
- package/src/views/method-selector/express-methods/index.js +25 -0
- package/src/views/method-selector/get-payment-methods.js +1 -0
- package/src/views/method-selector/index.js +58 -45
- package/src/views/method-selector/method-selector.spec.js +1 -1
- package/src/views/method-selector/mount-express-methods.js +26 -66
- package/src/views/method-selector/mount-methods.js +178 -0
- package/src/views/modal.js +1 -1
- package/src/views/result.js +3 -3
- package/src/views/summary.js +190 -24
- package/src/views/summary.spec.js +145 -0
- package/tests/mocks/storefront-api-mock.js +27 -48
- package/src/style/utils/minifyCss.js +0 -14
- package/src/views/errors.js +0 -95
- package/src/views/method-selector/express-methods.js +0 -51
- package/src/views/method-selector/generate-framepay-config.js +0 -54
- package/src/views/method-selector/generate-framepay-config.spec.js +0 -195
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import camelCase from 'lodash.camelcase';
|
|
2
|
+
import { mountModal } from '../modal';
|
|
3
|
+
import { MethodIframe } from '../common/iframe';
|
|
4
|
+
import { getMethodData } from './get-method-data';
|
|
5
|
+
|
|
6
|
+
async function mountInline(state, {
|
|
7
|
+
methodId,
|
|
8
|
+
paymentMethodsUrl,
|
|
9
|
+
container,
|
|
10
|
+
model,
|
|
11
|
+
method
|
|
12
|
+
}) {
|
|
13
|
+
const iframe = await new MethodIframe({
|
|
14
|
+
name: methodId,
|
|
15
|
+
url: `${paymentMethodsUrl}/${methodId}`,
|
|
16
|
+
container,
|
|
17
|
+
model
|
|
18
|
+
});
|
|
19
|
+
iframe.bindEventListeners({
|
|
20
|
+
loader: state.loader,
|
|
21
|
+
id: method.method
|
|
22
|
+
});
|
|
23
|
+
state.iframeComponents.push(iframe);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function displayBrands({summary, method}) {
|
|
27
|
+
function renderBrand(brand) {
|
|
28
|
+
return `
|
|
29
|
+
<figure>
|
|
30
|
+
<img alt="${brand}" src="https://forms.secure-payments.app/payment-instruments/brand/${brand.replace(/\s/, '')}.svg" />
|
|
31
|
+
</figure>
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const {brands} = method;
|
|
36
|
+
summary.querySelector('.rebilly-instruments-accordion-title').insertAdjacentHTML(
|
|
37
|
+
'afterend',
|
|
38
|
+
`<div class="rebilly-instruments-accordion-brands">${(() => {
|
|
39
|
+
if (brands.length >= 4) {
|
|
40
|
+
const truncatedBrands = brands.slice(0, 3);
|
|
41
|
+
return `${truncatedBrands.map(brand => renderBrand(brand)).join('')}
|
|
42
|
+
<span data-rebilly-i18n="forms.andMore">and more</span>
|
|
43
|
+
`
|
|
44
|
+
}
|
|
45
|
+
return brands.map(brand => renderBrand(brand)).join('');
|
|
46
|
+
})()}</div>`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function MountMethods({
|
|
51
|
+
state,
|
|
52
|
+
METHODS_CONTAINER,
|
|
53
|
+
METHODS
|
|
54
|
+
}) {
|
|
55
|
+
const isAccordion = METHODS.length > 1;
|
|
56
|
+
|
|
57
|
+
METHODS.forEach(async (method) => {
|
|
58
|
+
const { METHOD_ID: methodId, METHOD_TYPE: methodType } =
|
|
59
|
+
getMethodData(method);
|
|
60
|
+
const { paymentMethodsUrl } =
|
|
61
|
+
state.options._computed || 'https://www.example.com';
|
|
62
|
+
|
|
63
|
+
const selector = `rebilly-instruments-${methodId}`;
|
|
64
|
+
const isPopup = [
|
|
65
|
+
'payment-card'
|
|
66
|
+
].includes(methodId) && state.options.paymentInstruments[methodType]?.popup;
|
|
67
|
+
const model = {
|
|
68
|
+
account: state.data?.account,
|
|
69
|
+
options: state.options,
|
|
70
|
+
mainStyle: state.mainStyle,
|
|
71
|
+
plans: state.data.plans,
|
|
72
|
+
products: state.data.products,
|
|
73
|
+
method,
|
|
74
|
+
readyToPay: state.data.readyToPay,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (isAccordion) {
|
|
78
|
+
METHODS_CONTAINER.insertAdjacentHTML(
|
|
79
|
+
'beforeend',
|
|
80
|
+
`<details class="rebilly-instruments-accordion for-${methodId}">
|
|
81
|
+
<summary class="rebilly-instruments-accordion-summary">
|
|
82
|
+
<img class="rebilly-instruments-accordion-${method.method}-img" src="${method.metadata.logo}" alt="${method.method}"/>
|
|
83
|
+
<span class="rebilly-instruments-accordion-title" data-rebilly-i18n="paymentMethods.${method.method}">${method.metadata.name}</span>
|
|
84
|
+
<span class="rebilly-instruments-accordion-summary-checkmark"></span>
|
|
85
|
+
</summary>
|
|
86
|
+
<div id="${selector}" data-rebilly-instruments-type="${methodId}" style="position: relative"></div>
|
|
87
|
+
</details>`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if ([
|
|
91
|
+
'payment-card'
|
|
92
|
+
].includes(method.method)) {
|
|
93
|
+
const summary = document.querySelector(`.for-${methodId} > .rebilly-instruments-accordion-summary`);
|
|
94
|
+
displayBrands({summary, method});
|
|
95
|
+
}
|
|
96
|
+
state.loader.stopLoading({ id: method.method });
|
|
97
|
+
|
|
98
|
+
METHODS_CONTAINER.insertAdjacentHTML(
|
|
99
|
+
'beforeend',
|
|
100
|
+
`<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
|
|
101
|
+
);
|
|
102
|
+
const container = document.querySelector(`#${selector}`);
|
|
103
|
+
|
|
104
|
+
state.loader.stopLoading({ id: method.method });
|
|
105
|
+
mountInline(state, {
|
|
106
|
+
methodId,
|
|
107
|
+
paymentMethodsUrl,
|
|
108
|
+
container,
|
|
109
|
+
model,
|
|
110
|
+
method
|
|
111
|
+
});
|
|
112
|
+
} else if (isPopup) {
|
|
113
|
+
METHODS_CONTAINER.insertAdjacentHTML(
|
|
114
|
+
'beforeend',
|
|
115
|
+
`<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
|
|
116
|
+
);
|
|
117
|
+
const container = document.querySelector(`#${selector}`);
|
|
118
|
+
|
|
119
|
+
container.insertAdjacentHTML(
|
|
120
|
+
'beforeend',
|
|
121
|
+
`<button class="${selector} rebilly-instruments-button" data-rebilly-i18n="paymentMethods.${
|
|
122
|
+
method.method
|
|
123
|
+
}">${camelCase(method.method)}</button>`
|
|
124
|
+
);
|
|
125
|
+
const button = document.querySelector(`.${selector}`);
|
|
126
|
+
button.addEventListener('click', async () => {
|
|
127
|
+
const iframe = await mountModal({
|
|
128
|
+
state,
|
|
129
|
+
name: methodId,
|
|
130
|
+
url: `${paymentMethodsUrl}/${methodId}`,
|
|
131
|
+
model
|
|
132
|
+
});
|
|
133
|
+
state.iframeComponents.push(iframe);
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
METHODS_CONTAINER.insertAdjacentHTML(
|
|
137
|
+
'beforeend',
|
|
138
|
+
`<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
|
|
139
|
+
);
|
|
140
|
+
const container = document.querySelector(`#${selector}`);
|
|
141
|
+
|
|
142
|
+
mountInline(state, {
|
|
143
|
+
methodId,
|
|
144
|
+
paymentMethodsUrl,
|
|
145
|
+
container,
|
|
146
|
+
model,
|
|
147
|
+
method
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
state.loader.stopLoading({ id: method.method });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (isAccordion) {
|
|
154
|
+
// Fetch all the accordion elements.
|
|
155
|
+
const details = document.querySelectorAll('.rebilly-instruments-accordion');
|
|
156
|
+
// Add the onclick listeners.
|
|
157
|
+
details.forEach((targetDetail) => {
|
|
158
|
+
// set to open by default, will be closed by timeout below
|
|
159
|
+
targetDetail.open = true;
|
|
160
|
+
targetDetail.querySelector('summary').addEventListener('click', () => {
|
|
161
|
+
// Close all the accordion that are not targetDetail.
|
|
162
|
+
details.forEach((detail) => {
|
|
163
|
+
detail.removeAttribute('open');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// All details must be open to connect to Postmate than closed
|
|
169
|
+
setTimeout(() => {
|
|
170
|
+
details.forEach((detail) => {
|
|
171
|
+
detail.removeAttribute('open');
|
|
172
|
+
});
|
|
173
|
+
details[0].open = true;
|
|
174
|
+
}, 100);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
state.translate.translateItems();
|
|
178
|
+
}
|
package/src/views/modal.js
CHANGED
|
@@ -46,7 +46,7 @@ export async function mountModal({
|
|
|
46
46
|
modalContainer.classList.add('is-visible');
|
|
47
47
|
}, 240);
|
|
48
48
|
state.loader.addDOMElement({ section: 'modal', el: modalContent });
|
|
49
|
-
state.loader.startLoading({ section: 'modal', id:
|
|
49
|
+
state.loader.startLoading({ section: 'modal', id: 'modal-content' });
|
|
50
50
|
|
|
51
51
|
const injectedModel = {
|
|
52
52
|
options: state.options,
|
package/src/views/result.js
CHANGED
|
@@ -5,7 +5,7 @@ export async function mountResult({ payload, state }) {
|
|
|
5
5
|
const resultContainerClassName = 'rebilly-instruments-result';
|
|
6
6
|
replaceContent(state.form, `<div class="${resultContainerClassName}"></div>`);
|
|
7
7
|
|
|
8
|
-
state.loader.startLoading({ id: '
|
|
8
|
+
state.loader.startLoading({ id: 'result' });
|
|
9
9
|
state.loader.stopLoading({ id: 'express-purchase' });
|
|
10
10
|
|
|
11
11
|
const container = document.querySelector(`.${resultContainerClassName}`);
|
|
@@ -13,7 +13,7 @@ export async function mountResult({ payload, state }) {
|
|
|
13
13
|
|
|
14
14
|
const model = {
|
|
15
15
|
options: state.options,
|
|
16
|
-
|
|
16
|
+
mainStyle: state.mainStyle,
|
|
17
17
|
[state.options.transactionType]: payload
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -27,5 +27,5 @@ export async function mountResult({ payload, state }) {
|
|
|
27
27
|
loader: state.loader
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
state.iframeComponents.
|
|
30
|
+
state.iframeComponents.push(iframe);
|
|
31
31
|
}
|
package/src/views/summary.js
CHANGED
|
@@ -1,34 +1,200 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { collectData } from '@rebilly/risk-data-collector';
|
|
2
|
+
import { formatCurrency } from '../utils';
|
|
3
|
+
import { fetchData } from '../functions/mount/fetch-data';
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export function lineItemHTML({state, lineItem}) {
|
|
6
|
+
const {products} = state.data;
|
|
7
|
+
const {
|
|
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>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
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
|
|
9
71
|
};
|
|
10
|
-
const { paymentMethodsUrl } = state.options._computed;
|
|
11
72
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
73
|
+
let table = null;
|
|
74
|
+
[
|
|
75
|
+
{
|
|
76
|
+
label: 'Sub Total',
|
|
77
|
+
class: 'sub-total',
|
|
78
|
+
i18n: 'summary.subTotal',
|
|
79
|
+
value: subtotalAmount,
|
|
80
|
+
show: true,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: 'Discounts',
|
|
84
|
+
class: 'discounts',
|
|
85
|
+
i18n: 'summary.discounts',
|
|
86
|
+
value: discountsAmount,
|
|
87
|
+
show: discountsAmount > 0
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: 'Taxes',
|
|
91
|
+
class: 'taxes',
|
|
92
|
+
i18n: 'summary.taxes',
|
|
93
|
+
value: taxAmount,
|
|
94
|
+
show: true,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: 'Shipping',
|
|
98
|
+
class: 'shipping',
|
|
99
|
+
i18n: 'summary.shipping',
|
|
100
|
+
value: shippingAmount,
|
|
101
|
+
show: state.data.isShippingRequired,
|
|
102
|
+
},
|
|
103
|
+
].filter(({value}) => value !== null)
|
|
104
|
+
.forEach(item => {
|
|
105
|
+
if (!table) {
|
|
106
|
+
table = document.createElement('table');
|
|
107
|
+
table.insertAdjacentHTML('beforeend', `
|
|
108
|
+
<colgroup>
|
|
109
|
+
<col>
|
|
110
|
+
<col>
|
|
111
|
+
</colgroup>
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (item.show) {
|
|
116
|
+
const row = document.createElement('tr');
|
|
117
|
+
row.setAttribute('class', `rebilly-instruments-summary-breakdown-${item.class}`)
|
|
118
|
+
row.insertAdjacentHTML('beforeend', `
|
|
119
|
+
<td data-rebilly-i18n="${item.i18n}">${item.label}</td>
|
|
120
|
+
<td>${formatCurrency(item.value, currency)}</td>
|
|
121
|
+
`);
|
|
122
|
+
table.appendChild(row);
|
|
123
|
+
}
|
|
17
124
|
});
|
|
18
|
-
|
|
19
|
-
|
|
125
|
+
if (table) {
|
|
126
|
+
element.appendChild(table);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const totalElement = document.createElement('div');
|
|
130
|
+
totalElement.setAttribute('class', 'rebilly-instruments-summary-breakdown-total');
|
|
131
|
+
totalElement.insertAdjacentHTML('beforeend', `
|
|
132
|
+
<p data-rebilly-i18n="summary.total">Total</p>
|
|
133
|
+
<p class="rebilly-instruments-summary-breakdown-total-amount">
|
|
134
|
+
<span class="rebilly-instruments-summary-breakdown-total-amount-currency">
|
|
135
|
+
${currency}
|
|
136
|
+
</span>
|
|
137
|
+
${formatCurrency(amount, currency)}
|
|
138
|
+
<p>
|
|
139
|
+
`);
|
|
140
|
+
element.appendChild(totalElement);
|
|
141
|
+
|
|
142
|
+
return element;
|
|
20
143
|
}
|
|
21
144
|
|
|
22
|
-
export async function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
145
|
+
export async function mountSummary({ state }) {
|
|
146
|
+
const contentContainer = document.createElement('div');
|
|
147
|
+
contentContainer.setAttribute('class', 'rebilly-instruments-content');
|
|
148
|
+
|
|
149
|
+
const lineItems = state.data.summaryLineItems;
|
|
150
|
+
if (lineItems) {
|
|
151
|
+
const lineItemsElement = document.createElement('div');
|
|
152
|
+
lineItemsElement.setAttribute('class', 'rebilly-instruments-summary-line-items');
|
|
153
|
+
|
|
154
|
+
lineItems.forEach((lineItem) => {
|
|
155
|
+
lineItemsElement.insertAdjacentHTML('beforeend', lineItemHTML({ state, lineItem }));
|
|
31
156
|
});
|
|
157
|
+
contentContainer.appendChild(lineItemsElement);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const summaryBreakdownElement = document.createElement('div');
|
|
161
|
+
summaryBreakdownElement.setAttribute('class', 'rebilly-instruments-summary-breakdown');
|
|
162
|
+
contentContainer.appendChild(summaryBreakdownHTML({ state, element: summaryBreakdownElement}));
|
|
163
|
+
|
|
164
|
+
state.summary.appendChild(contentContainer);
|
|
165
|
+
state.summary.style.minHeight = '';
|
|
166
|
+
|
|
167
|
+
state.translate.translateItems();
|
|
168
|
+
state.loader.stopLoading({ section: 'summary', id: 'initSummary' });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function updateSummary({ state, instrument = null }) {
|
|
172
|
+
state.loader.startLoading({ section: 'summary', id: 'initSummary' });
|
|
173
|
+
const { riskMetadata } = await collectData();
|
|
174
|
+
|
|
175
|
+
let summaryPayload;
|
|
176
|
+
|
|
177
|
+
if (instrument?.billingAddress) {
|
|
178
|
+
summaryPayload = {
|
|
179
|
+
billingAddress: instrument.billingAddress
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
state.data = await fetchData({
|
|
184
|
+
riskMetadata,
|
|
185
|
+
summaryPayload,
|
|
186
|
+
state
|
|
32
187
|
});
|
|
188
|
+
|
|
189
|
+
if (state.data.transaction && state.data.transaction?.type === 'setup') {
|
|
190
|
+
state.options.transactionType = 'setup';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const itemsContainer = state.summary.querySelector(
|
|
194
|
+
'.rebilly-instruments-content'
|
|
195
|
+
);
|
|
196
|
+
itemsContainer?.remove();
|
|
197
|
+
|
|
198
|
+
mountSummary({ state });
|
|
33
199
|
}
|
|
34
200
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import SummaryModel from '@/storefront/models/summary-model';
|
|
2
|
+
import PlanModel from '@/storefront/models/plan-model';
|
|
3
|
+
import ProductModel from '@/storefront/models/product-model';
|
|
4
|
+
import { Loader } from '../loader';
|
|
5
|
+
import { Translate } from '../i18n';
|
|
6
|
+
import { mountSummary } from './summary';
|
|
7
|
+
import { DataInstance } from '../functions/mount/fetch-data';
|
|
8
|
+
|
|
9
|
+
describe('Summary component', () => {
|
|
10
|
+
let summaryElement;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
document.body.innerHTML = '';
|
|
13
|
+
summaryElement = document.createElement('div');
|
|
14
|
+
document.body.append(summaryElement);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
class TestMountSummaryInstance {
|
|
18
|
+
constructor({
|
|
19
|
+
options = {},
|
|
20
|
+
summary = summaryElement,
|
|
21
|
+
loader = new Loader(),
|
|
22
|
+
translate = new Translate(),
|
|
23
|
+
data = {},
|
|
24
|
+
} = {}) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.summary = summary;
|
|
27
|
+
this.loader = loader;
|
|
28
|
+
this.translate = translate;
|
|
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
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const options = {
|
|
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
|
+
]
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
it('should render the summary correctly', () => {
|
|
104
|
+
const mountSummaryInstance = new TestMountSummaryInstance({
|
|
105
|
+
options
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
//TODO: improve design to avoid this manual change
|
|
109
|
+
mountSummaryInstance.loader.DOM.summary = mountSummaryInstance.summary;
|
|
110
|
+
|
|
111
|
+
mountSummary({ state: mountSummaryInstance });
|
|
112
|
+
|
|
113
|
+
// Number of line items
|
|
114
|
+
const itemsContainer = document.querySelector(
|
|
115
|
+
'.rebilly-instruments-summary-line-items'
|
|
116
|
+
);
|
|
117
|
+
expect(itemsContainer).toMatchSnapshot();
|
|
118
|
+
|
|
119
|
+
// Breakdown
|
|
120
|
+
const summaryBreakdown = document.querySelector(
|
|
121
|
+
'.rebilly-instruments-summary-breakdown'
|
|
122
|
+
);
|
|
123
|
+
expect(summaryBreakdown).toMatchSnapshot();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should not render the plan description if its falsy', () => {
|
|
127
|
+
const mountSummaryInstance = new TestMountSummaryInstance({
|
|
128
|
+
options
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Making one product description falsy
|
|
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 });
|
|
138
|
+
|
|
139
|
+
// Check that only one description is render
|
|
140
|
+
const itemsSynopsysDescription = document.querySelectorAll(
|
|
141
|
+
'.rebilly-instruments-summary-line-item-synopsis-description'
|
|
142
|
+
);
|
|
143
|
+
expect(itemsSynopsysDescription.length).toEqual(1);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -3,52 +3,31 @@ import {ok, get, post} from 'msw-when-then';
|
|
|
3
3
|
export const storefrontURL = '*/storefront';
|
|
4
4
|
|
|
5
5
|
export const initStoreFrontApiMocks = (when) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
method: post,
|
|
34
|
-
response: () => ok([])
|
|
35
|
-
}, {
|
|
36
|
-
url: '/preview-purchase',
|
|
37
|
-
method: post,
|
|
38
|
-
response: () => ok({})
|
|
39
|
-
}, {
|
|
40
|
-
url: '/payment-instruments',
|
|
41
|
-
method: post,
|
|
42
|
-
response: () => ok([])
|
|
43
|
-
}, {
|
|
44
|
-
url: '/payment-instruments/*/setup',
|
|
45
|
-
method: post,
|
|
46
|
-
response: () => ok([])
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
mocks.forEach(api => {
|
|
51
|
-
const http = api.method(`${storefrontURL}${api.url}`)
|
|
52
|
-
when(http).thenReturn(api.response());
|
|
53
|
-
});
|
|
6
|
+
when(post(`${storefrontURL}/ready-to-pay`)).thenReturn((() => {
|
|
7
|
+
return ok([]);
|
|
8
|
+
})());
|
|
9
|
+
|
|
10
|
+
when(post(`${storefrontURL}/preview-purchase`)).thenReturn((() => {
|
|
11
|
+
return ok({});
|
|
12
|
+
})());
|
|
13
|
+
|
|
14
|
+
when(get(`${storefrontURL}/plans`)).thenReturn((() => {
|
|
15
|
+
return ok([]);
|
|
16
|
+
})());
|
|
17
|
+
|
|
18
|
+
when(get(`${storefrontURL}/products`)).thenReturn((() => {
|
|
19
|
+
return ok([]);
|
|
20
|
+
})());
|
|
21
|
+
|
|
22
|
+
when(post(`${storefrontURL}/payment-instruments`)).thenReturn((() => {
|
|
23
|
+
return ok([]);
|
|
24
|
+
})());
|
|
25
|
+
|
|
26
|
+
when(post(`${storefrontURL}/payment-instruments/*/setup`)).thenReturn((() => {
|
|
27
|
+
return ok([]);
|
|
28
|
+
})());
|
|
29
|
+
|
|
30
|
+
when(get(`${storefrontURL}/transactions/test-transaction-id`)).thenReturn((() => {
|
|
31
|
+
return ok({});
|
|
32
|
+
})());
|
|
54
33
|
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export function minifyCss(_content) {
|
|
2
|
-
let content = _content;
|
|
3
|
-
// Remove all comments and new lines
|
|
4
|
-
content = content.replace( /\/\*(?:(?!\*\/)[\s\S])*\*\/|[\r\n\t]+/g, '' );
|
|
5
|
-
// Remove spaces between selectors
|
|
6
|
-
content = content.replace( /\s{2,}/g, ' ' );
|
|
7
|
-
// Remove spaces of nested { }
|
|
8
|
-
content = content.replace( /\s([{:}])\s/g, '$1' );
|
|
9
|
-
// Remove spaces between , and ;
|
|
10
|
-
content = content.replace( /([;,])\s/g, '$1' );
|
|
11
|
-
// Remove important! spaces
|
|
12
|
-
content = content.replace( /\s!/g, '!' );
|
|
13
|
-
return content;
|
|
14
|
-
}
|