@rebilly/instruments 3.1.4-beta.0 → 3.3.0-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.
@@ -12,6 +12,7 @@ import { loader } from './loader';
12
12
  import { icons } from './icons';
13
13
  import { address } from './address';
14
14
  import { overlay } from './overlay';
15
+ import {accordion} from './accordion';
15
16
 
16
17
  // Order of components matters for style cascade
17
18
  export const components = (theme) => `
@@ -32,4 +33,5 @@ export const components = (theme) => `
32
33
  ${icons(theme)}
33
34
  ${address(theme)}
34
35
  ${overlay(theme)}
36
+ ${accordion(theme)}
35
37
  `;
@@ -45,7 +45,7 @@ export function generateDigitalWallet({ state, EXPRESS_METHODS }) {
45
45
  applePayDisplayOptions: paymentInstruments.applePay.displayOptions
46
46
  }
47
47
  }
48
- })
48
+ });
49
49
 
50
50
  return output;
51
51
  }
@@ -3,7 +3,7 @@ import { getMethodData } from './get-method-data';
3
3
 
4
4
  const SUPPORTED_EXPRESS_METHODS = ['Google Pay', 'Apple Pay', 'paypal'];
5
5
 
6
- const SUPPORTED_METHODS = ['payment-card'];
6
+ const SUPPORTED_METHODS = ['payment-card', 'ach'];
7
7
 
8
8
  const isExpressMethod = ({ method, feature }) => {
9
9
  return (
@@ -23,7 +23,7 @@ export function getPaymentMethods({ state }) {
23
23
  METHODS: []
24
24
  };
25
25
 
26
- state.data.readyToPay.forEach((method) => {
26
+ state.data.readyToPay?.forEach((method) => {
27
27
  if (isExpressMethod(method)) {
28
28
  const { METHOD_TYPE } = getMethodData(method);
29
29
  // Add loader entry per express method
@@ -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);
@@ -26,25 +72,63 @@ 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
+ }
34
94
 
35
- if (isiFrame) {
36
- const iframe = await new MethodIframe({
37
- name: methodId,
38
- url: `${paymentMethodsUrl}/${methodId}`,
95
+ state.loader.stopLoading({ id: method.method });
96
+ METHODS_CONTAINER.insertAdjacentHTML(
97
+ 'beforeend',
98
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
99
+ );
100
+ const container = document.querySelector(`#${selector}`);
101
+
102
+ state.loader.stopLoading({ id: method.method });
103
+ mountInline(state, {
104
+ methodId,
105
+ paymentMethodsUrl,
39
106
  container,
40
- model
107
+ model,
108
+ method
41
109
  });
42
- iframe.bindEventListeners({
43
- loader: state.loader,
44
- id: method.method
110
+ } else if (isiFrame) {
111
+ METHODS_CONTAINER.insertAdjacentHTML(
112
+ 'beforeend',
113
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
114
+ );
115
+ const container = document.querySelector(`#${selector}`);
116
+
117
+ state.loader.stopLoading({ id: method.method });
118
+ mountInline(state, {
119
+ methodId,
120
+ paymentMethodsUrl,
121
+ container,
122
+ model,
123
+ method
45
124
  });
46
- state.iframeComponents.push(iframe);
47
125
  } else {
126
+ METHODS_CONTAINER.insertAdjacentHTML(
127
+ 'beforeend',
128
+ `<div id="${selector}" data-rebilly-instruments-type="${methodId}"></div>`
129
+ );
130
+ const container = document.querySelector(`#${selector}`);
131
+
48
132
  container.insertAdjacentHTML(
49
133
  'beforeend',
50
134
  `<button class="${selector} rebilly-instruments-button" data-rebilly-i18n="paymentMethods.${
@@ -64,4 +148,23 @@ export function MountMethods({
64
148
  state.loader.stopLoading({ id: method.method });
65
149
  }
66
150
  });
151
+
152
+ if (isAccordion) {
153
+ // Fetch all the accordion elements.
154
+ const details = document.querySelectorAll('.rebilly-instruments-accordion');
155
+ // Add the onclick listeners.
156
+ details.forEach((targetDetail) => {
157
+ targetDetail.addEventListener('click', () => {
158
+ // Close all the accordion that are not targetDetail.
159
+ details.forEach((detail) => {
160
+ detail.removeAttribute('open');
161
+ });
162
+ });
163
+ });
164
+
165
+ // Open the first accordion by default
166
+ details[0].open = true;
167
+ }
168
+
169
+ state.translate.translateItems();
67
170
  }
@@ -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
+ };
@@ -87,7 +87,8 @@ export async function RenderMockRebillyInstruments(options = {}) {
87
87
  'items',
88
88
  'invoiceId',
89
89
  'transactionId',
90
- 'money'
90
+ 'money',
91
+ 'jwt'
91
92
  ].some(key => Object.keys(options).includes(key))
92
93
  if(!hasPurchaseData) {
93
94
  defaultOptions.items = [
@@ -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
  };