@rebilly/instruments 3.2.0-beta.0 → 3.3.2-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.
@@ -3,11 +3,57 @@ import { mountModal } from '../modal';
3
3
  import { MethodIframe } from '../common/iframe';
4
4
  import { getMethodData } from './get-method-data';
5
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.insertAdjacentHTML(
37
+ 'beforeend',
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
+
6
50
  export function MountMethods({
7
51
  state,
8
52
  METHODS_CONTAINER,
9
53
  METHODS
10
54
  }) {
55
+ const isAccordion = METHODS.length > 1;
56
+
11
57
  METHODS.forEach(async (method) => {
12
58
  const { METHOD_ID: methodId, METHOD_TYPE: methodType } =
13
59
  getMethodData(method);
@@ -15,9 +61,9 @@ export function MountMethods({
15
61
  state.options._computed || 'https://www.example.com';
16
62
 
17
63
  const selector = `rebilly-instruments-${methodId}`;
18
- const isiFrame =
19
- methodId === 'payment-card' &&
20
- !state.options.paymentInstruments[methodType]?.popup;
64
+ const isPopup = [
65
+ 'payment-card'
66
+ ].includes(methodId) && state.options.paymentInstruments[methodType]?.popup;
21
67
  const model = {
22
68
  options: state.options,
23
69
  mainStyle: state.mainStyle,
@@ -26,33 +72,56 @@ export function MountMethods({
26
72
  method
27
73
  };
28
74
 
29
- METHODS_CONTAINER.insertAdjacentHTML(
30
- 'beforeend',
31
- `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
32
- );
33
- const container = document.querySelector(`#${selector}`);
75
+ if (isAccordion) {
76
+ METHODS_CONTAINER.insertAdjacentHTML(
77
+ 'beforeend',
78
+ `<details class="rebilly-instruments-accordion for-${methodId}">
79
+ <summary class="rebilly-instruments-accordion-summary">
80
+ <span class="rebilly-instruments-accordion-summary-radio"></span>
81
+ <img class="rebilly-instruments-accordion-${method.method}-img" src="${method.metadata.logo}" alt="${method.method}"/>
82
+ <span class="rebilly-instruments-accordion-title" data-rebilly-i18n="paymentMethods.${method.method}">${method.metadata.name}</span>
83
+ </summary>
84
+ <div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>
85
+ </details>`
86
+ );
87
+
88
+ if ([
89
+ 'payment-card'
90
+ ].includes(method.method)) {
91
+ const summary = document.querySelector(`.for-${methodId} > .rebilly-instruments-accordion-summary`);
92
+ displayBrands({summary, method});
93
+ }
94
+ state.loader.stopLoading({ id: method.method });
95
+
96
+ METHODS_CONTAINER.insertAdjacentHTML(
97
+ 'beforeend',
98
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
99
+ );
100
+ const container = document.querySelector(`#${selector}`);
34
101
 
35
- if (isiFrame) {
36
- const iframe = await new MethodIframe({
37
- name: methodId,
38
- url: `${paymentMethodsUrl}/${methodId}`,
102
+ state.loader.stopLoading({ id: method.method });
103
+ mountInline(state, {
104
+ methodId,
105
+ paymentMethodsUrl,
39
106
  container,
40
- model
41
- });
42
- iframe.bindEventListeners({
43
- loader: state.loader,
44
- id: method.method
107
+ model,
108
+ method
45
109
  });
46
- state.iframeComponents.push(iframe);
47
- } else {
110
+ } else if (isPopup) {
111
+ METHODS_CONTAINER.insertAdjacentHTML(
112
+ 'beforeend',
113
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
114
+ );
115
+ const container = document.querySelector(`#${selector}`);
116
+
48
117
  container.insertAdjacentHTML(
49
118
  'beforeend',
50
119
  `<button class="${selector} rebilly-instruments-button" data-rebilly-i18n="paymentMethods.${
51
120
  method.method
52
121
  }">${camelCase(method.method)}</button>`
53
122
  );
54
- const paymentCardButton = document.querySelector(`.${selector}`);
55
- paymentCardButton.addEventListener('click', async () => {
123
+ const button = document.querySelector(`.${selector}`);
124
+ button.addEventListener('click', async () => {
56
125
  const iframe = await mountModal({
57
126
  state,
58
127
  name: methodId,
@@ -61,7 +130,40 @@ export function MountMethods({
61
130
  });
62
131
  state.iframeComponents.push(iframe);
63
132
  });
133
+ } else {
134
+ METHODS_CONTAINER.insertAdjacentHTML(
135
+ 'beforeend',
136
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
137
+ );
138
+ const container = document.querySelector(`#${selector}`);
139
+
64
140
  state.loader.stopLoading({ id: method.method });
141
+ mountInline(state, {
142
+ methodId,
143
+ paymentMethodsUrl,
144
+ container,
145
+ model,
146
+ method
147
+ });
65
148
  }
66
149
  });
150
+
151
+ if (isAccordion) {
152
+ // Fetch all the accordion elements.
153
+ const details = document.querySelectorAll('.rebilly-instruments-accordion');
154
+ // Add the onclick listeners.
155
+ details.forEach((targetDetail) => {
156
+ targetDetail.addEventListener('click', () => {
157
+ // Close all the accordion that are not targetDetail.
158
+ details.forEach((detail) => {
159
+ detail.removeAttribute('open');
160
+ });
161
+ });
162
+ });
163
+
164
+ // Open the first accordion by default
165
+ details[0].open = true;
166
+ }
167
+
168
+ state.translate.translateItems();
67
169
  }
@@ -0,0 +1,9 @@
1
+ import { ok, get } from 'msw-when-then';
2
+
3
+ export const rebillyURL = '*';
4
+
5
+ export const initRebillyApiMocks = (when) => {
6
+ when(get(`${rebillyURL}/payment-methods`)).thenReturn((() => {
7
+ return ok([])
8
+ })());
9
+ };
@@ -3,6 +3,7 @@ import {rest} from 'msw';
3
3
  import {whenThen} from 'msw-when-then';
4
4
  import {handlers} from './handlers';
5
5
  import {initStoreFrontApiMocks} from '../mocks/storefront-api-mock';
6
+ import {initRebillyApiMocks} from '../mocks/rebilly-api-mock';
6
7
 
7
8
  export const server = setupServer(...handlers);
8
9
 
@@ -10,4 +11,5 @@ export const {when} = whenThen(server, rest);
10
11
 
11
12
  export const initGlobalHandlers = () => {
12
13
  initStoreFrontApiMocks(when);
14
+ initRebillyApiMocks(when);
13
15
  };