@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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +66 -43
  3. package/dist/index.min.js +66 -43
  4. package/package.json +6 -3
  5. package/src/functions/destroy.js +2 -8
  6. package/src/functions/mount/fetch-data.js +2 -9
  7. package/src/functions/mount/index.js +10 -15
  8. package/src/functions/mount/mount.spec.js +11 -10
  9. package/src/functions/mount/setup-framepay-theme.js +72 -30
  10. package/src/functions/mount/setup-options.js +2 -2
  11. package/src/functions/mount/{setup-styles-vars.js → setup-styles.js} +7 -9
  12. package/src/functions/purchase.js +5 -2
  13. package/src/functions/setup.js +6 -3
  14. package/src/functions/show.js +2 -2
  15. package/src/functions/show.spec.js +4 -4
  16. package/src/functions/update.spec.js +3 -4
  17. package/src/instance.js +1 -4
  18. package/src/loader/index.js +33 -57
  19. package/src/storefront/index.js +5 -2
  20. package/src/storefront/payment-instruments.js +0 -7
  21. package/src/style/base/__snapshots__/theme.spec.js.snap +220 -136
  22. package/src/style/base/default-theme.js +14 -187
  23. package/src/style/base/index.js +79 -487
  24. package/src/style/base/theme.js +4 -3
  25. package/src/style/base/theme.spec.js +3 -2
  26. package/src/style/browserslist.js +1 -0
  27. package/src/style/components/accordion.js +140 -0
  28. package/src/style/components/address.js +55 -0
  29. package/src/style/components/button.js +117 -0
  30. package/src/style/components/divider.js +39 -0
  31. package/src/style/components/forms/checkbox.js +75 -0
  32. package/src/style/components/forms/field.js +56 -0
  33. package/src/style/components/forms/form.js +18 -0
  34. package/src/style/components/forms/input.js +77 -0
  35. package/src/style/components/forms/label.js +55 -0
  36. package/src/style/components/forms/radio.js +80 -0
  37. package/src/style/components/forms/select.js +86 -0
  38. package/src/style/components/forms/validation.js +72 -0
  39. package/src/style/components/icons.js +13 -0
  40. package/src/style/components/index.js +39 -0
  41. package/src/style/components/loader.js +41 -0
  42. package/src/style/components/methods.js +97 -0
  43. package/src/style/components/overlay.js +24 -0
  44. package/src/style/helpers/index.js +54 -0
  45. package/src/style/index.js +24 -4
  46. package/src/style/payment-instruments/content.js +8 -0
  47. package/src/style/payment-instruments/index.js +14 -0
  48. package/src/style/payment-instruments/payment-card.js +27 -0
  49. package/src/style/payment-instruments/payment-instrument-list.js +44 -0
  50. package/src/style/payment-instruments/payment-instrument.js +55 -0
  51. package/src/style/utils/color-values.js +1 -1
  52. package/src/style/utils/remove-empty-null.js +10 -0
  53. package/src/style/vendor/framepay.js +28 -0
  54. package/src/style/vendor/postmate.js +18 -0
  55. package/src/style/views/confirmation.js +26 -0
  56. package/src/style/views/index.js +16 -0
  57. package/src/style/views/method-selector.js +11 -0
  58. package/src/style/views/modal.js +91 -0
  59. package/src/style/views/result.js +52 -0
  60. package/src/style/views/summary.js +118 -0
  61. package/src/views/__snapshots__/summary.spec.js.snap +246 -0
  62. package/src/views/common/iframe/base-iframe.js +2 -3
  63. package/src/views/common/iframe/event-listeners.js +9 -12
  64. package/src/views/common/iframe/method-iframe.js +1 -3
  65. package/src/views/common/iframe/modal-iframe.js +2 -4
  66. package/src/views/common/iframe/view-iframe.js +1 -3
  67. package/src/views/confirmation.js +7 -12
  68. package/src/views/method-selector/express-methods/apple-pay.js +92 -0
  69. package/src/views/method-selector/express-methods/index.js +25 -0
  70. package/src/views/method-selector/get-payment-methods.js +1 -0
  71. package/src/views/method-selector/index.js +58 -45
  72. package/src/views/method-selector/method-selector.spec.js +1 -1
  73. package/src/views/method-selector/mount-express-methods.js +26 -66
  74. package/src/views/method-selector/mount-methods.js +178 -0
  75. package/src/views/modal.js +1 -1
  76. package/src/views/result.js +3 -3
  77. package/src/views/summary.js +190 -24
  78. package/src/views/summary.spec.js +145 -0
  79. package/tests/mocks/storefront-api-mock.js +27 -48
  80. package/src/style/utils/minifyCss.js +0 -14
  81. package/src/views/errors.js +0 -95
  82. package/src/views/method-selector/express-methods.js +0 -51
  83. package/src/views/method-selector/generate-framepay-config.js +0 -54
  84. package/src/views/method-selector/generate-framepay-config.spec.js +0 -195
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@rebilly/instruments",
3
- "version": "3.13.2-beta.0",
3
+ "version": "3.13.4-beta.0",
4
4
  "author": "Rebilly",
5
- "license": "MIT",
6
5
  "main": "dist/index.js",
7
6
  "unpkg": "dist/index.min.js",
7
+ "license": "MIT",
8
8
  "scripts": {
9
9
  "build": "yarn rollup -c --environment NODE_ENV:production",
10
10
  "dev": "yarn rollup -c --watch --environment NODE_ENV:development",
@@ -16,13 +16,16 @@
16
16
  "@babel/core": "^7.14.6",
17
17
  "@babel/preset-env": "^7.14.7",
18
18
  "@rebilly/risk-data-collector": "^2.3.0",
19
+ "autoprefixer": "^10.3.4",
20
+ "css": "^3.0.0",
19
21
  "jwt-decode": "^3.1.2",
20
22
  "lodash.camelcase": "^4.3.0",
21
23
  "lodash.isequal": "^4.5.0",
22
24
  "lodash.kebabcase": "^4.1.1",
23
25
  "lodash.merge": "^4.6.2",
24
26
  "popostmate": "^1.6.4",
25
- "rebilly-js-sdk": "^47.3.1",
27
+ "postcss": "^8.4.5",
28
+ "rebilly-js-sdk": "^44.4.0",
26
29
  "values.js": "^2.0.0"
27
30
  },
28
31
  "devDependencies": {
@@ -6,19 +6,13 @@ export async function destroy({ state }) {
6
6
  // wait to allow for cancellation to catch any pending api requests
7
7
  const sleepMilliseconds = 1000;
8
8
  await sleep(sleepMilliseconds);
9
- [
10
- ...(state.iframeComponents?.form || []),
11
- ...(state.iframeComponents?.summary || [])
12
- ].forEach((iframe) => {
9
+ [...(state.iframeComponents || {})].forEach((iframe) => {
13
10
  iframe.destroy();
14
11
  });
15
12
 
16
13
  registeredListeners.removeAll(document);
17
14
 
18
- state.iframeComponents = {
19
- summary: [],
20
- form: [],
21
- };
15
+ state.iframeComponents = [];
22
16
  state.hasMounted = false;
23
17
 
24
18
  state.summary.textContent = '';
@@ -6,7 +6,6 @@ import { fetchSummary } from '../../storefront/summary';
6
6
  import { fetchInvoice as FetchInvoice } from '../../storefront/invoices';
7
7
  import { fetchTransaction as FetchTransaction } from '../../storefront/transactions';
8
8
  import { fetchAccount as FetchAccount } from '../../storefront/account';
9
- import { fetchPaymentInstrument as FetchInstruments } from '../../storefront/payment-instruments';
10
9
 
11
10
  export class DataInstance {
12
11
  constructor({
@@ -109,13 +108,11 @@ export async function fetchData({
109
108
  // Dependency injectable functions
110
109
  fetchInvoice = FetchInvoice,
111
110
  fetchTransaction = FetchTransaction,
112
- fetchAccount = FetchAccount,
113
- fetchInstruments = FetchInstruments
111
+ fetchAccount = FetchAccount
114
112
  }) {
115
113
  try {
116
114
  let transaction = null;
117
115
  let account = null;
118
- let availableInstruments = null;
119
116
  if (state.options?.transactionId) {
120
117
  transaction = await fetchTransaction({data: {
121
118
  id: state.options.transactionId
@@ -137,10 +134,7 @@ export async function fetchData({
137
134
  }
138
135
 
139
136
  if (state.options?.jwt) {
140
- [account, availableInstruments] = await Promise.all([
141
- fetchAccount({state}),
142
- fetchInstruments({state})
143
- ]);
137
+ account = await fetchAccount({state});
144
138
  }
145
139
 
146
140
  state.data = new DataInstance({
@@ -149,7 +143,6 @@ export async function fetchData({
149
143
  transaction,
150
144
  riskMetadata,
151
145
  account,
152
- availableInstruments,
153
146
  });
154
147
 
155
148
  const [readyToPay, previewPurchase] = await Promise.all([
@@ -7,11 +7,10 @@ import setupElement from './setup-element';
7
7
  import setupStorefront from './setup-storefront';
8
8
  import setupOptions from './setup-options';
9
9
  import setupFramepayInstance from './setup-framepay';
10
- import setupStylesVars from './setup-styles-vars';
10
+ import setupStyles from './setup-styles';
11
11
  import setupI18n from './setup-i18n';
12
12
  import setupFramepayTheme from './setup-framepay-theme';
13
13
  import setupUserFlow from './setup-user-flow';
14
- import { showError } from '../../views/errors';
15
14
 
16
15
  /**
17
16
  * @typedef {object} Item
@@ -88,26 +87,23 @@ export async function mount({
88
87
  // Setup DOM
89
88
  state.form = setupElement({ element: 'form', options });
90
89
  state.summary = setupElement({ element: 'summary', options });
91
- // hardcode max-width to not break UX
92
- // PayPal button cannot exceed 750px;
93
- state.form.style.maxWidth = '750px';
94
- state.summary.style.maxWidth = '750px';
95
-
96
- // Setup state
97
- state.options = setupOptions({ options });
98
- state.storefront = setupStorefront({ options });
99
- state.mainStyleVars = setupStylesVars({ options });
100
- state.options.themeFramepay = setupFramepayTheme({ state, options });
101
90
 
102
91
  // Setup loader
103
92
  state.loader.addDOMElement({ el: state.form });
104
93
  state.loader.addDOMElement({ section: 'summary', el: state.summary });
105
- state.loader.startLoading({ state, section: 'summary', id: 'rebilly-instruments-summary' });
106
- state.loader.startLoading({ state, id: 'rebilly-instruments-methods' });
94
+ state.loader.startLoading({ section: 'summary', id: 'initSummary' });
95
+ state.loader.startLoading({ id: 'initForm' });
107
96
 
97
+ // Setup state
98
+ state.options = setupOptions({ options });
99
+ state.storefront = setupStorefront({ options });
100
+
101
+ state.mainStyle = await setupStyles({ options });
102
+
108
103
  state.data = await fetchData({ state });
109
104
  Events.dataReady.dispatch(state.data);
110
105
 
106
+ state.options.themeFramepay = await setupFramepayTheme({ state, options });
111
107
  state.i18n = setupI18n({ state });
112
108
 
113
109
  await setupFramepay(state);
@@ -132,7 +128,6 @@ export async function mount({
132
128
  state.i18n({state});
133
129
  state.hasMounted = true;
134
130
  } catch (error) {
135
- showError(error);
136
131
  throw error;
137
132
  }
138
133
 
@@ -29,22 +29,23 @@ describe('RebillyInstruments instance', () => {
29
29
  };
30
30
  await RenderMockRebillyInstruments(options);
31
31
 
32
- await new Promise((resolve) => {
33
- setTimeout(() => {
34
- resolve();
35
- }, 100);
36
- })
37
-
38
32
  // Mounts form and summary
39
33
  const summarySelector = document.querySelector('.summary-selector');
40
- expect(summarySelector.innerHTML).toContain('<iframe name="rebilly-instruments-summary" class="rebilly-instruments-iframe" loading="lazy" allow="payment" scrolling="no" src="https://forms.local.rebilly.dev:3000/summary"></iframe>');
34
+ expect(summarySelector.innerHTML).toMatch('My Product');
41
35
 
42
36
  // Theme config overrides initial styles
43
- const STYLE_VARS = document.querySelectorAll('style[type="text/css"]')[0];
37
+ const SUMMARY_CONTAINER = summarySelector.querySelector(
38
+ '.rebilly-instruments-content'
39
+ );
40
+ expect(getComputedStyle(SUMMARY_CONTAINER).getPropertyValue('--rebilly-colorBackground')).toEqual(
41
+ '#000'
42
+ );
44
43
 
45
- expect(STYLE_VARS.innerHTML).toMatch(
46
- '--rebilly-colorBackground: #000;'
44
+ // CSS config property overrides initial styles
45
+ const LINE_ITEM_TITLE = document.querySelector(
46
+ '.rebilly-instruments-summary-line-item-synopsis-title'
47
47
  );
48
+ expect(getComputedStyle(LINE_ITEM_TITLE).color).toEqual('rgb(0, 68, 212)');
48
49
 
49
50
  // Mounts default FramePay script
50
51
  const SCRIPTS = [...document.querySelectorAll('head script')];
@@ -1,3 +1,6 @@
1
+ import css from 'css';
2
+ import camelCase from 'lodash.camelcase';
3
+
1
4
  function processCSS(rawCss) {
2
5
  const cssMap = {};
3
6
  [...rawCss.matchAll(/(--rebilly.*(?=:))[:\s](.*(?=;))/g)]
@@ -23,62 +26,101 @@ function processCSS(rawCss) {
23
26
  }
24
27
 
25
28
  function replaceCssVars(rawCss) {
26
- const CssVarsObj = {}
27
29
  processCSS(rawCss).forEach(([key, value]) => {
28
- CssVarsObj[key] = value;
30
+ rawCss = rawCss.split(`var(${key})`).join(value);
29
31
  });
30
- return CssVarsObj;
32
+
33
+ return rawCss;
31
34
  }
32
35
 
33
- const getStyleProps = (obj, particle = '') => {
36
+ const getStyleProps = (ast, selector) => {
37
+ const { rules } = ast.stylesheet;
34
38
  const output = {
35
- color: obj[`--rebilly-${particle}ColorText`],
36
- fontFamily: obj[`--rebilly-${particle}FontFamily`],
37
- fontSize: obj[`--rebilly-${particle}FontSize`],
38
- fontWeight: obj[`--rebilly-${particle}FontWeight`],
39
- lineHeight: obj[`--rebilly-${particle}FontLineHeight`],
40
- background: obj[`--rebilly-${particle}ColorBackground`],
41
- boxShadow: obj[`--rebilly-${particle}BoxShadow`],
39
+ color: null,
40
+ fontFamily: null,
41
+ fontSize: null,
42
+ lineHeight: null,
43
+ fontWeight: null,
44
+ background: null
45
+ }
46
+
47
+ for(let i = 0; i < rules.length; i += 1) {
48
+ const rule = rules[i];
49
+
50
+ if(rule.type === 'rule' && rule.selectors.includes(selector)) {
51
+ rule.declarations.forEach(decl => {
52
+ const propName = camelCase(decl.property);
53
+ if(typeof output[propName] !== 'undefined') {
54
+ output[propName] = decl.value;
55
+ }
56
+ })
57
+ }
42
58
  }
43
59
  return output;
44
60
  }
45
61
 
46
- export default ({
62
+ export default async ({
47
63
  state,
48
64
  options = {}
49
65
  }) => {
50
66
  const fullCss = `
51
- ${state.mainStyleVars}
67
+ ${state.mainStyle}
52
68
  ${options.css || ''}
53
69
  `;
54
70
 
55
- const resolvedCssVarsObj = replaceCssVars(fullCss);
71
+ const resolvedCss = replaceCssVars(fullCss);
72
+ const cssAst = css.parse(resolvedCss);
73
+
74
+ const cssSelectors = {
75
+ input: {
76
+ base: '.rebilly-instruments-form-field-input',
77
+ baseHover: '.rebilly-instruments-form-field-input:hover',
78
+ baseFocus: '.rebilly-instruments-form-field-input:focus',
79
+ basePlaceholder: '.rebilly-instruments-form-field-input::placeholder',
80
+ baseSelection: '.rebilly-instruments-form-field-input::selection',
81
+
82
+ invalid: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input',
83
+ invalidHover: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input:hover',
84
+ invalidFocus: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input:focus',
85
+ invalidPlaceholder: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input::placeholder',
86
+ invalidSelection: '.rebilly-instruments-form-field.is-error .rebilly-instruments-form-field-input::selection'
87
+ },
88
+ button: {
89
+ base: '.rebilly-instruments-button.rebilly-instruments-button-secondary',
90
+ baseHover: '.rebilly-instruments-button.rebilly-instruments-button-secondary:not([disabled]):hover',
91
+ baseFocus: '.rebilly-instruments-button.rebilly-instruments-button-secondary:not([disabled]):active',
92
+
93
+ active: '.rebilly-instruments-button',
94
+ activeHover: '.rebilly-instruments-button:not([disabled]):hover',
95
+ activeFocus: '.rebilly-instruments-button:not([disabled]):active',
96
+ }
97
+ }
56
98
 
57
99
  const framepayStyle = {
58
100
  base: {
59
- ...getStyleProps(resolvedCssVarsObj, 'input'),
60
- ':hover': getStyleProps(resolvedCssVarsObj, 'inputHover'),
61
- ':focus': getStyleProps(resolvedCssVarsObj, 'inputFocus'),
62
- '::placeholder': getStyleProps(resolvedCssVarsObj, 'inputPlaceholder'),
63
- '::selection': getStyleProps(resolvedCssVarsObj, 'inputSelection'),
101
+ ...getStyleProps(cssAst, cssSelectors.input.base),
102
+ ':hover': getStyleProps(cssAst, cssSelectors.input.baseHover),
103
+ ':focus': getStyleProps(cssAst, cssSelectors.input.baseFocus),
104
+ '::placeholder': getStyleProps(cssAst, cssSelectors.input.basePlaceholder),
105
+ '::selection': getStyleProps(cssAst, cssSelectors.input.baseSelection),
64
106
  },
65
107
  invalid: {
66
- ...getStyleProps(resolvedCssVarsObj, 'inputError'),
67
- ':hover': getStyleProps(resolvedCssVarsObj, 'inputErrorHover'),
68
- ':focus': getStyleProps(resolvedCssVarsObj, 'inputErrorFocus'),
69
- '::placeholder': getStyleProps(resolvedCssVarsObj, 'inputErrorPlaceholder'),
70
- '::selection': getStyleProps(resolvedCssVarsObj, 'inputErrorSelection'),
108
+ ...getStyleProps(cssAst, cssSelectors.input.invalid),
109
+ ':hover': getStyleProps(cssAst, cssSelectors.input.invalidHover),
110
+ ':focus': getStyleProps(cssAst, cssSelectors.input.invalidFocus),
111
+ '::placeholder': getStyleProps(cssAst, cssSelectors.input.invalidPlaceholder),
112
+ '::selection': getStyleProps(cssAst, cssSelectors.input.invalidSelection),
71
113
  },
72
114
  buttons: {
73
115
  base: {
74
- ...getStyleProps(resolvedCssVarsObj, 'buttonSecondary'),
75
- ':hover': getStyleProps(resolvedCssVarsObj, 'buttonSecondaryHover'),
76
- ':focus': getStyleProps(resolvedCssVarsObj, 'buttonSecondaryActive'),
116
+ ...getStyleProps(cssAst, cssSelectors.button.base),
117
+ ':hover': getStyleProps(cssAst, cssSelectors.button.baseHover),
118
+ ':focus': getStyleProps(cssAst, cssSelectors.button.baseFocus),
77
119
  },
78
120
  active: {
79
- ...getStyleProps(resolvedCssVarsObj, 'button'),
80
- ':hover': getStyleProps(resolvedCssVarsObj, 'buttonHover'),
81
- ':focus': getStyleProps(resolvedCssVarsObj, 'buttonActive'),
121
+ ...getStyleProps(cssAst, cssSelectors.button.active),
122
+ ':hover': getStyleProps(cssAst, cssSelectors.button.activeHover),
123
+ ':focus': getStyleProps(cssAst, cssSelectors.button.activeFocus),
82
124
  }
83
125
  }
84
126
  }
@@ -5,7 +5,7 @@ export const defaults = {
5
5
  countryCode: 'US',
6
6
  locale: 'auto',
7
7
  theme: {
8
- labels: 'stacked'
8
+ labels: 'floating'
9
9
  },
10
10
  paymentInstruments: {
11
11
  address: {
@@ -15,7 +15,7 @@ export const defaults = {
15
15
  show: [],
16
16
  require: []
17
17
  },
18
- compactExpressInstruments: false,
18
+ compactExpressInstruments: true,
19
19
  googlePay: {
20
20
  displayOptions: {
21
21
  buttonColor: 'black',
@@ -1,20 +1,18 @@
1
- import { mainStyleVars } from '../../style';
1
+ import { mainStyle } from '../../style';
2
2
  import { addDOMElement } from '../../utils';
3
- import { minifyCss } from '../../style/utils/minifyCss';
4
3
 
5
- export default ({
4
+ export default async ({
6
5
  options: {
7
6
  theme = {},
8
7
  css,
9
8
  }
10
9
  } = {}) => {
11
- // Adds base CSS vars stylesheet
12
- const styleVars = mainStyleVars(theme || {});
13
-
10
+ // Adds base stylesheet
11
+ const style = await mainStyle(theme || {});
14
12
  addDOMElement({
15
13
  element: 'style',
16
14
  attributes: { type: 'text/css' },
17
- content: minifyCss(styleVars),
15
+ content: style,
18
16
  target: 'head'
19
17
  });
20
18
 
@@ -23,10 +21,10 @@ export default ({
23
21
  addDOMElement({
24
22
  element: 'style',
25
23
  attributes: { type: 'text/css' },
26
- content: minifyCss(css),
24
+ content: css,
27
25
  target: 'head'
28
26
  });
29
27
  }
30
28
 
31
- return styleVars;
29
+ return style;
32
30
  }
@@ -2,7 +2,6 @@ import { postPurchase, postPayment } from '../storefront/purchase';
2
2
  import Events from '../events';
3
3
  import { mountModal } from '../views/modal';
4
4
  import { DataInstance } from './mount/fetch-data';
5
- import { showError } from '../views/errors';
6
5
 
7
6
  export async function makePayment({ state, payload }) {
8
7
  const {
@@ -110,7 +109,11 @@ export async function purchase({ state, payload }) {
110
109
  }
111
110
  return fields;
112
111
  } catch (error) {
113
- showError(error);
112
+ // TODO: Display error to customer
113
+ console.error(error);
114
+ if(error.status === 422) {
115
+ error.details.forEach(e => console.error(e));
116
+ }
114
117
  return error;
115
118
  }
116
119
  }
@@ -2,8 +2,7 @@ import { setupPaymentInstrument } from '../storefront/payment-instruments';
2
2
  import Events from '../events';
3
3
  import { mountModal } from '../views/modal';
4
4
  import { DataInstance } from './mount/fetch-data';
5
- import { showError } from '../views/errors';
6
-
5
+
7
6
  export async function setup({ state, payload }) {
8
7
  try {
9
8
  const {
@@ -57,7 +56,11 @@ export async function setup({ state, payload }) {
57
56
 
58
57
  return {instrument, transaction};
59
58
  } catch (error) {
60
- showError(error);
59
+ // TODO: Display error to customer
60
+ console.error(error);
61
+ if(error.status === 422) {
62
+ error.details.forEach(e => console.error(e));
63
+ }
61
64
  return error;
62
65
  }
63
66
  }
@@ -16,14 +16,14 @@ import { mountResult } from '../views/result';
16
16
  export async function show({ componentName, payload, state }) {
17
17
  switch (componentName) {
18
18
  case 'result':
19
- state.iframeComponents.form = state.iframeComponents.form.filter((iframe) => {
19
+ state.iframeComponents = state.iframeComponents.filter((iframe) => {
20
20
  iframe.destroy();
21
21
  return false;
22
22
  });
23
23
  mountResult({payload, state});
24
24
  break;
25
25
  case 'confirmation':
26
- state.iframeComponents.form = state.iframeComponents.form.filter((iframe) => {
26
+ state.iframeComponents = state.iframeComponents.filter((iframe) => {
27
27
  iframe.destroy();
28
28
  return false;
29
29
  });
@@ -12,7 +12,7 @@ describe('RebillyInstruments show', () => {
12
12
  .mockReturnValue(Promise.resolve());
13
13
 
14
14
  const instance = new RebillyInstrumentsInstance();
15
- instance.state.iframeComponents.form.push(iframeMock);
15
+ instance.state.iframeComponents.push(iframeMock);
16
16
 
17
17
  const payload = {
18
18
  test: 'value'
@@ -22,7 +22,7 @@ describe('RebillyInstruments show', () => {
22
22
 
23
23
  expect(mountResult).toBeCalledTimes(1);
24
24
  expect(mountResult).toBeCalledWith({payload, state: instance.state});
25
- expect(instance.state.iframeComponents.form).toEqual([]);
25
+ expect(instance.state.iframeComponents).toEqual([]);
26
26
  });
27
27
 
28
28
  it('should show confirmation component', async () => {
@@ -31,7 +31,7 @@ describe('RebillyInstruments show', () => {
31
31
  .mockReturnValue(Promise.resolve());
32
32
 
33
33
  const instance = new RebillyInstrumentsInstance();
34
- instance.state.iframeComponents.form.push(iframeMock);
34
+ instance.state.iframeComponents.push(iframeMock);
35
35
 
36
36
  const payload = {
37
37
  test: 'value'
@@ -41,7 +41,7 @@ describe('RebillyInstruments show', () => {
41
41
 
42
42
  expect(mountConfirmation).toBeCalledTimes(1);
43
43
  expect(mountConfirmation).toBeCalledWith({payload, state: instance.state});
44
- expect(instance.state.iframeComponents.form).toEqual([]);
44
+ expect(instance.state.iframeComponents).toEqual([]);
45
45
  });
46
46
 
47
47
  it('should fail for non supported component', async () => {
@@ -31,7 +31,7 @@ describe('RebillyInstruments Update', () => {
31
31
  call
32
32
  }
33
33
  };
34
- rebillyInstruments.state.iframeComponents.form = [fakeIFrameComponent];
34
+ rebillyInstruments.state.iframeComponents = [fakeIFrameComponent];
35
35
 
36
36
  await rebillyInstruments.update({ locale: 'ja' });
37
37
 
@@ -58,7 +58,7 @@ describe('RebillyInstruments Update', () => {
58
58
  call
59
59
  }
60
60
  };
61
- rebillyInstruments.state.iframeComponents.form = [fakeIFrameComponent];
61
+ rebillyInstruments.state.iframeComponents = [fakeIFrameComponent];
62
62
 
63
63
  await rebillyInstruments.update({
64
64
  countryCode: 'ES'
@@ -83,8 +83,7 @@ describe('RebillyInstruments Update', () => {
83
83
  call
84
84
  }
85
85
  };
86
-
87
- rebillyInstruments.state.iframeComponents.form = [fakeIFrameComponent];
86
+ rebillyInstruments.iframeComponents = [fakeIFrameComponent];
88
87
 
89
88
  await rebillyInstruments.update({
90
89
  items: [
package/src/instance.js CHANGED
@@ -18,10 +18,7 @@ export class InstrumentsState {
18
18
  this.summary = null;
19
19
  this.loader = new Loader();
20
20
  this.translate = new Translate();
21
- this.iframeComponents = {
22
- summary: [],
23
- form: [],
24
- };
21
+ this.iframeComponents = [];
25
22
  this.hasMounted = false;
26
23
  }
27
24
  }
@@ -1,86 +1,54 @@
1
1
  import isDOMElement from '../utils/is-dom-element';
2
2
 
3
- export const summaryLoaderHTML = `
4
- <div class="rebilly-instruments-summary-loader-total rebilly-instruments-loader-display-flex rebilly-instruments-loader-align-center rebilly-instruments-loader-justify-space-between">
5
- <div><p class="is-el-loading">Total</p></div>
6
- <p class="total is-el-loading">$99.99</p>
3
+ export const loaderHTML = `
4
+ <div class="rebilly-instruments-loader is-active">
5
+ <div class="rebilly-instruments-loader-spinner"></div>
6
+ <span class="rebilly-instuments-loader-message"></span>
7
7
  </div>
8
- `;
9
-
10
- export const methodsLoaderHTML = `
11
- <div class="rebilly-instruments-methods-loader">
12
- <div class="rebilly-instruments-loader-form-el is-el-loading"></div>
13
- <div class="rebilly-instruments-divider">
14
- <span class="rebilly-instruments-divider-label"><span class="is-el-loading">Divi</span></span>
15
- </div>
16
- <div class="rebilly-instruments-loader-display-flex rebilly-instruments-loader-justify-end">
17
- <div class="rebilly-instruments-methods-loader-card-icon is-el-loading"></div>
18
- <div class="rebilly-instruments-methods-loader-card-icon is-el-loading"></div>
19
- </div>
20
- <div class="rebilly-instruments-loader-form-el is-el-loading"></div>
21
- <div class="rebilly-instruments-methods-loader-form-fields">
22
- <div class="rebilly-instruments-loader-form-label"><small class="is-el-loading">Card holder name</small></div>
23
- <div class="rebilly-instruments-loader-form-el is-el-loading"></div>
24
- </div>
25
- <div class="rebilly-instruments-loader-form-el is-button">Continue</div>
26
- </div>
27
- `;
28
-
29
- const loaderContainer = (padding = '0px') => `
30
- <div class="rebilly-instruments-loader is-active" style="padding: ${padding}"></div>
31
8
  `
32
9
 
33
- export const basicLoaderHTML = `
34
- <div class="rebilly-instruments-loader-spinner"></div>
35
- `;
36
-
37
10
  export class Loader {
38
11
  constructor() {
39
12
  this.summary = [];
40
13
  this.form = [];
41
14
  this.modal = [];
42
15
  this.DOM = {
43
- loaderContainer,
44
- summaryLoaderHTML,
45
- methodsLoaderHTML,
46
- basicLoaderHTML
16
+ loading: loaderHTML
47
17
  };
48
18
  }
49
19
 
50
20
  addDOMElement({ section = 'form', el = null } = {}) {
51
21
  if (isDOMElement(el)) {
52
22
  el.style.position = 'relative';
23
+
53
24
  this.DOM[section] = el;
54
25
  }
55
26
  }
56
27
 
57
- startLoading({section = 'form', id = ''} = {}) {
28
+ startLoading({state, section = 'form', id = '', message = null } = {}) {
29
+ const minHeight = '200px';
58
30
  this[section].push(id);
59
31
 
60
- const rootEl = document.querySelector(':root');
61
- const contentPadding = 2;
62
- let minHeight = '240px';
63
-
64
32
  if (this.DOM?.[section]) {
65
- let loaderEl = this.DOM[section].querySelector('.rebilly-instruments-loader');
66
- const { padding, paddingTop, paddingBottom } = getComputedStyle(this.DOM?.[section]);
67
- if (!loaderEl) {
68
- this.DOM[section].innerHTML = loaderContainer(`${parseFloat(padding) + contentPadding}px`);
69
- loaderEl = this.DOM[section].querySelector('.rebilly-instruments-loader');
70
- if (section === 'form') {
71
- loaderEl.insertAdjacentHTML('afterbegin', this.DOM.methodsLoaderHTML);
72
- minHeight = getComputedStyle(rootEl).getPropertyValue('--rebilly-methodLoaderMinHeight');
73
- } else if (section === 'summary') {
74
- loaderEl.insertAdjacentHTML('afterbegin', this.DOM.summaryLoaderHTML);
75
- minHeight = getComputedStyle(rootEl).getPropertyValue('--rebilly-summaryLoaderMinHeight');
76
- }
33
+ if (!this.DOM[section].querySelector('.rebilly-instruments-loader')) {
34
+ this.DOM[section].innerHTML = this.DOM.loading;
77
35
  } else {
78
- if (id.includes('confirmation') || id.includes('result') || id.includes('modal')) {
79
- loaderEl.innerHTML = this.DOM.basicLoaderHTML;
80
- }
81
- loaderEl.classList.add('is-active');
36
+ this.DOM[section]
37
+ .querySelector('.rebilly-instruments-loader')
38
+ .classList.add('is-active');
39
+ }
40
+ this.DOM[section].style.minHeight = minHeight;
41
+ }
42
+
43
+ if (message) {
44
+ const loaderMessage = this.DOM[section].querySelector('.rebilly-instuments-loader-message');
45
+ if (loaderMessage) {
46
+ loaderMessage.innerHTML = message;
47
+ loaderMessage.style.marginTop = '10px';
48
+ loaderMessage.setAttribute('data-message-id', id);
49
+ loaderMessage.setAttribute('data-rebilly-i18n', message);
50
+ state.i18n({state});
82
51
  }
83
- this.DOM[section].style.minHeight = `calc(${minHeight} + ${paddingTop} + ${paddingBottom} + ${contentPadding * 2}px)`;
84
52
  }
85
53
  }
86
54
 
@@ -100,6 +68,14 @@ export class Loader {
100
68
  .classList.remove('is-active');
101
69
  this.DOM[section].style.minHeight = '';
102
70
  }
71
+
72
+ const loaderMessage = this.DOM[section].querySelector(`.rebilly-instuments-loader-message[data-message-id="${id}"]`);
73
+ if (loaderMessage) {
74
+ loaderMessage.innerHTML = null;
75
+ loaderMessage.style.marginTop = '0px';
76
+ loaderMessage.removeAttribute('data-message-id');
77
+ loaderMessage.removeAttribute('data-rebilly-i18n');
78
+ }
103
79
  }
104
80
 
105
81
  clearAll() {