@rebilly/instruments 3.3.2-beta.0 → 3.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebilly/instruments",
3
- "version": "3.3.2-beta.0",
3
+ "version": "3.4.0-beta.0",
4
4
  "author": "Rebilly",
5
5
  "main": "dist/index.js",
6
6
  "unpkg": "dist/index.min.js",
@@ -5,7 +5,7 @@ import { fetchData } from './fetch-data';
5
5
  import setupElement from './setup-element';
6
6
  import setupStorefront from './setup-storefront';
7
7
  import setupOptions from './setup-options';
8
- import setupFramepay from './setup-framepay';
8
+ import setupFramepayInstance from './setup-framepay';
9
9
  import setupStyles from './setup-styles';
10
10
  import setupI18n from './setup-i18n';
11
11
  import setupFramepayTheme from './setup-framepay-theme';
@@ -63,6 +63,7 @@ import setupUserFlow from './setup-user-flow';
63
63
  * Mount library with configurations.
64
64
  * @param {object} options - The options object
65
65
  * @param {object} options.state - Global state
66
+ * @param {function} options.setupFramepayInstance - Helper for adding FramePay scripts to the DOM
66
67
  * @param {string | HTMLElement} options.form - The CSS class or HTML element were the form will be mounted.
67
68
  * @param {string | HTMLElement} options.summary - The CSS class or HTML element were the summary will be mounted.
68
69
  * @param {Item[]} options.items - Which plans the customer is purchasing.
@@ -75,6 +76,7 @@ import setupUserFlow from './setup-user-flow';
75
76
  */
76
77
  export async function mount({
77
78
  state,
79
+ setupFramepay = setupFramepayInstance,
78
80
  ...options
79
81
  } = {}) {
80
82
  try {
@@ -100,7 +102,7 @@ export async function mount({
100
102
  state.options.themeFramepay = await setupFramepayTheme({ state, options });
101
103
  state.i18n = setupI18n({ state });
102
104
 
103
- setupFramepay({ state });
105
+ await setupFramepay(state);
104
106
 
105
107
  // Update state options from data
106
108
  if ((!state.options.websiteId) && state.data.transaction?.websiteId) {
@@ -8,9 +8,9 @@ import ProductModel from '@/storefront/models/product-model';
8
8
  import SummaryModel from '@/storefront/models/summary-model';
9
9
 
10
10
  describe('RebillyInstruments instance', () => {
11
- it("should inject HTML to the merchant's website", async () => {
11
+ it('should inject HTML to the merchant\'s website', async () => {
12
12
  const framePayScriptUrl = 'https://framepay.rebilly.com/rebilly.js';
13
- const framePayStyleUrl = 'https://dev.framepay.rebilly.com/rebilly.css';
13
+ const framePayStyleUrl = 'https://framepay.rebilly.com/rebilly.css';
14
14
 
15
15
  const options = {
16
16
  form: '.form-selector',
@@ -34,7 +34,6 @@ describe('RebillyInstruments instance', () => {
34
34
  framePayStyleLink: framePayStyleUrl
35
35
  },
36
36
  };
37
-
38
37
  await RenderMockRebillyInstruments(options);
39
38
 
40
39
  // Mounts form and summary
@@ -64,15 +63,14 @@ describe('RebillyInstruments instance', () => {
64
63
 
65
64
  // Mounts _dev FramePay style
66
65
  const STYLE_LINKS = [...document.querySelectorAll('head link')];
67
- const FRAMEPAY_STYLE = STYLE_LINKS.find(
68
- (script) => script.href === framePayStyleUrl
69
- );
66
+ const FRAMEPAY_STYLE = STYLE_LINKS.find(script => script.href === framePayStyleUrl);
67
+
70
68
  expect(FRAMEPAY_STYLE.href).toEqual(framePayStyleUrl);
71
69
  });
72
70
 
73
71
  it('should mount with JWT pruchase data', async () => {
74
72
  // Use https://www.jwt.io to help encode and decode JWT
75
- const jwt = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkMGYzNWEzYy03N2M0LTQ0NDItOWFhNC03ODA5NDA2NzBjN2IiLCJleHAiOjE2NzM2NDk0ODcsImlhdCI6MTY0MjExMDczOS4zMjYyNDQsImFjbCI6W3sic2NvcGUiOnsib3JnYW5pemF0aW9uSWQiOlsidGVzdC1vcmdhbml6YXRpb24tZCJdLCJ0cmFuc2FjdGlvbklkIjpbInRlc3QtdHJhbnNhY3Rpb24taWQiXX0sInBlcm1pc3Npb25zIjpbMV19XSwiY2xhaW1zIjp7InRyYW5zYWN0aW9uSWQiOiJ0ZXN0LXRyYW5zYWN0aW9uLWlkIn0sIm1lcmNoYW50IjoidGVzdC1vcmdhbml6YXRpb24taWQiLCJjdXN0b21lciI6eyJpZCI6InRlc3QtY3VzdG9tZXItaWQiLCJuYW1lIjoiVGVzdGVyIFRlc3RlcnNvbiIsImNyZWF0ZWRUaW1lIjoiMjAyMi0wMS0xNFQwMDowMDowMCswMDowMCJ9fQ.h4voW-UvXzXRm1JlxkN8cNHhQ_IIPSWWN9BANfBWEHQ`;
73
+ const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkMGYzNWEzYy03N2M0LTQ0NDItOWFhNC03ODA5NDA2NzBjN2IiLCJleHAiOjE2NzM2NDk0ODcsImlhdCI6MTY0MjExMDczOS4zMjYyNDQsImFjbCI6W3sic2NvcGUiOnsib3JnYW5pemF0aW9uSWQiOlsidGVzdC1vcmdhbml6YXRpb24tZCJdLCJ0cmFuc2FjdGlvbklkIjpbInRlc3QtdHJhbnNhY3Rpb24taWQiXX0sInBlcm1pc3Npb25zIjpbMV19XSwiY2xhaW1zIjp7InRyYW5zYWN0aW9uSWQiOiJ0ZXN0LXRyYW5zYWN0aW9uLWlkIn0sIm1lcmNoYW50IjoidGVzdC1vcmdhbml6YXRpb24taWQiLCJjdXN0b21lciI6eyJpZCI6InRlc3QtY3VzdG9tZXItaWQiLCJuYW1lIjoiVGVzdGVyIFRlc3RlcnNvbiIsImNyZWF0ZWRUaW1lIjoiMjAyMi0wMS0xNFQwMDowMDowMCswMDowMCJ9fQ.h4voW-UvXzXRm1JlxkN8cNHhQ_IIPSWWN9BANfBWEHQ';
76
74
 
77
75
  const options = {
78
76
  form: '.form-selector',
@@ -1,41 +1,20 @@
1
- import { addDOMElement } from '../../utils';
1
+ export default async function setupFramepay (state = {}) {
2
+ const {_dev} = state.options || {};
3
+ const urls = {
4
+ script: _dev?.framePayScriptLink || 'https://framepay.rebilly.com/rebilly.js',
5
+ style: _dev?.framePayStyleLink || 'https://framepay.rebilly.com/rebilly.css',
6
+ };
2
7
 
3
- export default ({
4
- state: {
5
- options: {
6
- _dev
7
- }
8
- }
9
- } = {}) => {
10
- const framePayUrls = {
11
- script: _dev
12
- ? _dev.framePayScriptLink || 'https://framepay.rebilly.com/rebilly.js'
13
- : 'https://framepay.rebilly.com/rebilly.js',
14
- style: _dev
15
- ? _dev.framePayStyleLink || 'https://framepay.rebilly.com/rebilly.css'
16
- : 'https://framepay.rebilly.com/rebilly.css'
17
- };
8
+ return new Promise((resolve) => {
9
+ const framepayStyle = document.createElement('link');
10
+ framepayStyle.setAttribute('href', urls.style);
11
+ framepayStyle.setAttribute('rel', 'stylesheet');
12
+ document.head.prepend(framepayStyle);
18
13
 
19
- if (!document.querySelectorAll('[framepay*="script"]').length) {
20
- addDOMElement({
21
- element: 'script',
22
- attributes: {
23
- framepay: 'script',
24
- src: framePayUrls.script
25
- },
26
- target: 'head'
14
+ const framepayScript = document.createElement('script');
15
+ framepayScript.setAttribute('src', urls.script);
16
+
17
+ framepayScript.onload = () => resolve();
18
+ document.head.append(framepayScript);
27
19
  });
28
- }
29
-
30
- if (!document.querySelectorAll('[framepay*="stylesheet"]').length) {
31
- addDOMElement({
32
- element: 'link',
33
- attributes: {
34
- framepay: 'stylesheet',
35
- href: framePayUrls.style,
36
- rel: 'stylesheet'
37
- },
38
- target: 'head'
39
- });
40
- }
41
- }
20
+ }
@@ -6,15 +6,24 @@ import { DataInstance } from './mount/fetch-data';
6
6
  export async function makePayment({ state, payload }) {
7
7
  const {
8
8
  _raw: {
9
- id: token
10
- }
9
+ id
10
+ },
11
+ isExistingInstrument
11
12
  } = payload;
13
+ delete payload.isExistingInstrument;
14
+ delete payload._raw;
12
15
 
13
16
  const data = {
14
- token,
15
17
  ...payload
16
18
  };
17
19
 
20
+ if (isExistingInstrument) {
21
+ data.paymentInstrumentId = id;
22
+
23
+ } else {
24
+ data.token = id;
25
+ }
26
+
18
27
  if (state.options.invoiceId) {
19
28
  data.invoiceId = state.options.invoiceId;
20
29
  }
@@ -5,12 +5,30 @@ import { DataInstance } from './mount/fetch-data';
5
5
 
6
6
  export async function setup({ state, payload }) {
7
7
  try {
8
+ const {
9
+ _raw: {
10
+ id
11
+ },
12
+ isExistingInstrument
13
+ } = payload;
14
+ delete payload.isExistingInstrument;
15
+ delete payload._raw;
16
+
17
+ const data = {
18
+ ...payload
19
+ };
20
+
21
+ if (isExistingInstrument) {
22
+ data.paymentInstrumentId = id;
23
+ } else {
24
+ data.token = id;
25
+ }
26
+
8
27
  const {instrument, transaction} = await setupPaymentInstrument({
9
28
  state,
10
29
  data: {
11
- token: payload._raw.id,
12
30
  websiteId: state.options?.websiteId,
13
- ...payload
31
+ ...data
14
32
  }
15
33
  });
16
34
 
@@ -1,6 +1,7 @@
1
+ import { avoidUnhandledPromises } from 'tests/async-utilities';
2
+ import { setupFramepayMock } from 'tests/mocks/rebilly-instruments-mock';
1
3
  import RebillyInstruments from './index';
2
4
  import { RebillyInstrumentsInstance } from './instance';
3
- import { avoidUnhandledPromises } from 'tests/async-utilities';
4
5
 
5
6
  describe('RebillyInstruments instance', () => {
6
7
  let rebillyInstruments;
@@ -24,7 +25,8 @@ describe('RebillyInstruments instance', () => {
24
25
  const options = {
25
26
  form: '.form-selector',
26
27
  summary: '.summary-selector',
27
- items: []
28
+ items: [],
29
+ setupFramepay: setupFramepayMock,
28
30
  };
29
31
 
30
32
  await rebillyInstruments.mount(options);
@@ -16,10 +16,10 @@ export async function setupPaymentInstrument({ data, state }) {
16
16
  }
17
17
  }
18
18
 
19
- if(state.data.getAmountAndCurrency) {
19
+ if(state.data.amountAndCurrency) {
20
20
  setupPayload.data = {
21
21
  ...setupPayload.data,
22
- ...state.data.getAmountAndCurrency
22
+ ...state.data.amountAndCurrency
23
23
  }
24
24
  }
25
25
 
@@ -8,13 +8,18 @@ export const accordion = (theme) => `
8
8
  /* ACCORDION CLOSED */
9
9
  .rebilly-instruments-accordion {
10
10
  border: 1px solid var(--rebilly-colorMutedBorder);
11
- padding: 0 var(--rebilly-spacings-s);
11
+ padding: 0 calc(var(--rebilly-spacings-m) - 4px);
12
12
  background: var(--rebilly-colorBackground);
13
- transition: border 0.2s ease, box-shadow 0.2s ease;
13
+ transition: border 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
14
14
  margin: var(--rebilly-spacings-s) 0;
15
15
  border-radius: var(--rebilly-borderRadius);
16
16
  overflow: hidden;
17
17
  cursor: pointer;
18
+ opacity: 0.8;
19
+ }
20
+
21
+ .rebilly-instruments-accordion:hover {
22
+ opacity: 1;
18
23
  }
19
24
 
20
25
  .rebilly-instruments-accordion:first-of-type {
@@ -33,37 +38,37 @@ export const accordion = (theme) => `
33
38
  display: flex;
34
39
  align-items: center;
35
40
  list-style: none;
36
- padding: var(--rebilly-spacings-xs) var(--rebilly-spacings-s);
37
- margin: 0px calc(-1 * var(--rebilly-spacings-s));
41
+ padding: calc(var(--rebilly-spacings-m) - 14px) calc(var(--rebilly-spacings-m) - 4px);
42
+ margin: 0px calc(-1 * calc(var(--rebilly-spacings-m) - 4px));
43
+ background: var(--rebilly-colorBackground);
38
44
  }
39
45
 
40
- .rebilly-instruments-accordion .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-radio {
41
- height: 18px;
42
- width: 18px;
46
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-checkmark {
47
+ position: relative;
48
+ width: calc(var(--rebilly-spacings-m) - 4px);
49
+ height: calc(var(--rebilly-spacings-m) - 4px);
43
50
  border-radius: 50%;
44
- margin-right: var(--rebilly-spacings-s);
45
- border: 1px solid var(--rebilly-colorMutedBorder);
46
- box-shadow: 0 0 0 0 transparent;
51
+ background: ${lighten(theme.colorText, 60)};
52
+ margin-left: var(--rebilly-spacings-m);
47
53
  transition: all 0.2s ease;
48
- position: relative;
49
54
  }
50
55
 
51
- .rebilly-instruments-accordion .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-radio::after {
56
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-checkmark::after {
52
57
  content: '';
53
58
  position: absolute;
54
- width: 10px;
55
- height: 10px;
59
+ border: solid var(--rebilly-colorBackground);
60
+ width: calc(var(--rebilly-spacings-xs) + 2px);
61
+ height: calc(var(--rebilly-spacings-xs) - 2px);
62
+ border-width: 2px 2px 0 0;
63
+ border-radius: 2px;
56
64
  top: 50%;
57
65
  left: 50%;
58
- transform: translate(-50%, -50%) scale(0.4);
59
- opacity: 0;
60
- background: var(--rebilly-colorPrimary);
61
- border-radius: 50%;
66
+ transform: translateY(-60%) translateX(-50%) rotate(135deg);
62
67
  transition: all 0.2s ease;
63
68
  }
64
69
 
65
- .rebilly-instruments-accordion:hover .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-radio {
66
- border-color: ${lighten(theme.colorText, 60)};
70
+ .rebilly-instruments-accordion-summary:hover .rebilly-instruments-accordion-summary-checkmark {
71
+ background: var(--rebilly-colorText);
67
72
  }
68
73
 
69
74
  .rebilly-instruments-accordion .rebilly-instruments-accordion-summary img {
@@ -73,55 +78,50 @@ export const accordion = (theme) => `
73
78
  width: 100%;
74
79
  }
75
80
 
76
- .rebilly-instruments-accordion .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-title {
81
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-title {
77
82
  margin: 0;
78
83
  font-weight: 500;
79
84
  flex: 2;
80
85
  }
81
86
 
82
- .rebilly-instruments-accordion .rebilly-instruments-accordion-brands {
87
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-brands {
83
88
  display: inline-flex;
84
89
  justify-content: flex-end;
85
90
  align-items: center;
86
91
  }
87
92
 
88
- .rebilly-instruments-accordion .rebilly-instruments-accordion-brands figure {
93
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-brands figure {
89
94
  margin: auto;
90
95
  padding: 0;
91
96
  height: 26px;
92
97
  }
93
98
 
94
- .rebilly-instruments-accordion .rebilly-instruments-accordion-brands figure img {
99
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-brands figure img {
95
100
  width: auto;
96
101
  height: 100%;
97
102
  border-radius: 4px;
98
103
  margin-right: var(--rebilly-spacings-xs);
99
104
  }
100
105
 
101
- .rebilly-instruments-accordion .rebilly-instruments-accordion-brands span {
106
+ .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-brands span {
102
107
  color: var(--rebilly-colorMutedText);
103
- margin: 0;
108
+ margin: 0 0 0 var(--rebilly-spacings-xs);
104
109
  font-size: calc(var(--rebilly-fontSizeBase) * 0.875);
105
110
  line-height: 1;
106
111
  }
107
112
 
108
113
  /* ACCORDION OPENED */
109
114
  .rebilly-instruments-accordion[open] {
110
- padding: 0 var(--rebilly-spacings-s) var(--rebilly-spacings-s);
115
+ padding: 0 calc(var(--rebilly-spacings-m) - 4px) calc(var(--rebilly-spacings-m) - 4px);
116
+ opacity: 1;
111
117
  }
112
118
 
113
119
  .rebilly-instruments-accordion[open] .rebilly-instruments-accordion-summary {
114
120
  border-bottom: 1px solid var(--rebilly-colorMutedBorder);
115
- margin-bottom: var(--rebilly-spacings-m);
121
+ margin-bottom: calc(var(--rebilly-spacings-m) - 4px);
116
122
  }
117
123
 
118
- .rebilly-instruments-accordion[open] .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-radio {
119
- border-color: var(--rebilly-colorPrimary);
120
- box-shadow: 0 0 0 1px var(--rebilly-colorPrimary);
121
- }
122
-
123
- .rebilly-instruments-accordion[open] .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-radio::after {
124
- transform: translate(-50%, -50%) scale(1);
125
- opacity: 1;
124
+ .rebilly-instruments-accordion[open] .rebilly-instruments-accordion-summary .rebilly-instruments-accordion-summary-checkmark {
125
+ background: ${theme.colorPrimary};
126
126
  }
127
127
  `;
@@ -79,4 +79,21 @@ export const button = () => `
79
79
  .rebilly-instruments-button:first-of-type { margin-top: 0; }
80
80
 
81
81
  .rebilly-instruments-button:last-of-type { margin-bottom: 0; }
82
+
83
+ .rebilly-instruments-button-group {
84
+ display: flex;
85
+ align-items: center;
86
+ }
87
+
88
+ .rebilly-instruments-button-group .rebilly-instruments-button {
89
+ margin: 0 var(--rebilly-spacings-xs);
90
+ }
91
+
92
+ .rebilly-instruments-button-group .rebilly-instruments-button:first-of-type {
93
+ margin-left: 0;
94
+ }
95
+
96
+ .rebilly-instruments-button-group .rebilly-instruments-button:last-of-type {
97
+ margin-right: 0;
98
+ }
82
99
  `;
@@ -1,8 +1,7 @@
1
- import { lighten } from '../../utils/color-values';
2
1
  // -----------------------------------------------------------------------------
3
2
  // This file contains all styles related to the checkbox component.
4
3
  // -----------------------------------------------------------------------------
5
- export const checkbox = (theme) => `
4
+ export const checkbox = () => `
6
5
  /**
7
6
  * Checkbox
8
7
  */
@@ -33,10 +32,10 @@ export const checkbox = (theme) => `
33
32
  position: relative;
34
33
  top: 0;
35
34
  left: 0;
36
- height: 24px;
37
- width: 24px;
35
+ width: calc(var(--rebilly-spacings-m) - 4px);
36
+ height: calc(var(--rebilly-spacings-m) - 4px);
38
37
  border-radius: 4px;
39
- box-shadow: inset 0 0 0 1px var(--rebilly-colorMutedBorder);
38
+ box-shadow: inset 0 0 0 2px var(--rebilly-colorMutedBorder);
40
39
  margin-right: var(--rebilly-spacings-s);
41
40
  background-color: transparent;
42
41
  transition: all 200ms;
@@ -45,17 +44,20 @@ export const checkbox = (theme) => `
45
44
  .rebilly-instruments-form-field-checkbox span:after {
46
45
  content: '';
47
46
  position: absolute;
48
- left: 9px;
49
- top: 4px;
50
- opacity: 0;
51
- width: 5px;
52
- height: 11px;
53
47
  border: solid var(--rebilly-colorPrimary);
54
- border-width: 0 2px 2px 0;
55
- -webkit-transform: rotate(45deg);
56
- -ms-transform: rotate(45deg);
57
- transform: rotate(45deg);
58
- transition: all 200ms;
48
+ width: calc(var(--rebilly-spacings-xs) + 4px);
49
+ height: calc(var(--rebilly-spacings-xs) - 2px);
50
+ border-width: 2px 2px 0 0;
51
+ border-radius: 2px;
52
+ top: 50%;
53
+ left: 50%;
54
+ opacity: 0;
55
+ transform: translateY(-60%) translateX(-50%) rotate(135deg);
56
+ transition: all 0.2s ease;
57
+ }
58
+
59
+ .rebilly-instruments-form-field-checkbox input[type="checkbox"]:focus ~ span {
60
+ box-shadow: inset 0 0 0 2px var(--rebilly-colorPrimary);
59
61
  }
60
62
 
61
63
  .rebilly-instruments-form-field-checkbox input[type="checkbox"]:checked ~ span {
@@ -66,10 +68,6 @@ export const checkbox = (theme) => `
66
68
  opacity: 1;
67
69
  }
68
70
 
69
- .rebilly-instruments-form-field-checkbox input[type="checkbox"]:focus ~ span {
70
- box-shadow: 0 0 0 2px ${lighten(theme.colorPrimary, 80)}, inset 0 0 0 1px var(--rebilly-colorPrimary);
71
- }
72
-
73
71
  .rebilly-instruments-form-field-checkbox input[type="checkbox"]:disabled ~ span {
74
72
  opacity: 0.6;
75
73
  }
@@ -0,0 +1,80 @@
1
+ // -----------------------------------------------------------------------------
2
+ // This file contains all styles related to the radio component.
3
+ // -----------------------------------------------------------------------------
4
+ export const radio = () => `
5
+ /**
6
+ * Checkbox
7
+ */
8
+ .rebilly-instruments-form-field-radio {
9
+ position: relative;
10
+ opacity: 1;
11
+ align-items: center;
12
+ display: flex;
13
+ flex-direction: row-reverse;
14
+ justify-content: start;
15
+ cursor: pointer;
16
+ transform: none;
17
+ }
18
+
19
+ .rebilly-instruments-form-field-radio > * {
20
+ cursor: pointer;
21
+ }
22
+
23
+ .rebilly-instruments-form-field-radio input[type="radio"] {
24
+ position: absolute;
25
+ opacity: 0;
26
+ cursor: pointer;
27
+ height: 0;
28
+ width: 0;
29
+ }
30
+
31
+ .rebilly-instruments-form-field-radio > span {
32
+ position: relative;
33
+ top: 0;
34
+ left: 0;
35
+ height: calc(var(--rebilly-spacings-m) - 4px);
36
+ width: calc(var(--rebilly-spacings-m) - 4px);
37
+ min-width: calc(var(--rebilly-spacings-m) - 4px);
38
+ border-radius: 50%;
39
+ box-shadow: inset 0 0 0 2px var(--rebilly-colorMutedBorder);
40
+ margin-right: var(--rebilly-spacings-s);
41
+ background-color: transparent;
42
+ transition: all 200ms;
43
+ }
44
+
45
+ .rebilly-instruments-form-field-radio > span:after {
46
+ content: '';
47
+ position: absolute;
48
+ left: 50%;
49
+ top: 50%;
50
+ transform: translateX(-50%) translateY(-50%);
51
+ opacity: 0;
52
+ width: calc(var(--rebilly-spacings-xs) + 4px);
53
+ height: calc(var(--rebilly-spacings-xs) + 4px);
54
+ border-radius: 50%;
55
+ background: var(--rebilly-colorMutedBorder);
56
+ transition: all 200ms;
57
+ }
58
+
59
+ .rebilly-instruments-form-field-radio:hover > span:after {
60
+ opacity: 1;
61
+ }
62
+
63
+ .rebilly-instruments-form-field-radio input[type="radio"]:checked ~ span {
64
+ box-shadow: inset 0 0 0 2px var(--rebilly-colorPrimary);
65
+ }
66
+
67
+ .rebilly-instruments-form-field-radio input[type="radio"]:checked ~ span:after {
68
+ background: var(--rebilly-colorPrimary);
69
+ opacity: 1;
70
+ }
71
+
72
+ .rebilly-instruments-form-field-radio input[type="radio"]:focus ~ span {
73
+ opacity: 1;
74
+ box-shadow: inset 0 0 0 2px var(--rebilly-colorPrimary);
75
+ }
76
+
77
+ .rebilly-instruments-form-field-radio input[type="radio"]:disabled ~ span {
78
+ opacity: 0.6;
79
+ }
80
+ `;
@@ -1,6 +1,7 @@
1
1
  import { expressMethods, methods } from './methods';
2
2
  import { form } from './forms/form';
3
3
  import { checkbox } from './forms/checkbox';
4
+ import { radio } from './forms/radio';
4
5
  import { field } from './forms/field';
5
6
  import { input } from './forms/input';
6
7
  import { label } from './forms/label';
@@ -12,7 +13,7 @@ import { loader } from './loader';
12
13
  import { icons } from './icons';
13
14
  import { address } from './address';
14
15
  import { overlay } from './overlay';
15
- import {accordion} from './accordion';
16
+ import { accordion } from './accordion';
16
17
 
17
18
  // Order of components matters for style cascade
18
19
  export const components = (theme) => `
@@ -25,7 +26,8 @@ export const components = (theme) => `
25
26
  ${input(theme)}
26
27
  ${select(theme)}
27
28
  ${label(theme)}
28
- ${checkbox(theme)}
29
+ ${checkbox()}
30
+ ${radio()}
29
31
  ${validation(theme)}
30
32
  ${button(theme)}
31
33
  ${divider(theme)}
@@ -48,4 +48,7 @@ export const helpers = () => `
48
48
  .rebilly-instruments-helper-ml-xl { margin-left: var(--rebilly-spacings-xl) !important }
49
49
  .rebilly-instruments-helper-ml-xxl { margin-left: var(--rebilly-spacings-2xl) !important }
50
50
  .rebilly-instruments-helper-ml-0 { margin-left: 0!important }
51
+
52
+ .rebilly-instruments-display-flex { display: flex!important }
53
+ .rebilly-instruments-align-items-center { align-items: center!important }
51
54
  `;
@@ -1,5 +1,7 @@
1
1
  import { methodContent } from './content';
2
2
  import { paymentCard } from './payment-card';
3
+ import { paymentInstrumentList } from './payment-instrument-list';
4
+ import { paymentInstrument } from './payment-instrument';
3
5
 
4
6
  // Order of components matters for style cascade
5
7
  export const paymentInstruments = (theme) => `
@@ -7,4 +9,6 @@ export const paymentInstruments = (theme) => `
7
9
  ------------------------------------------------------------ */
8
10
  ${methodContent()}
9
11
  ${paymentCard(theme)}
12
+ ${paymentInstrument()}
13
+ ${paymentInstrumentList()}
10
14
  `;
@@ -0,0 +1,44 @@
1
+ // -----------------------------------------------------------------------------
2
+ // This file contains all styles related to the payment instrument list component.
3
+ // -----------------------------------------------------------------------------
4
+ export const paymentInstrumentList = () => `
5
+ .rebilly-instruments-payment-instrument-list {
6
+ margin: 0;
7
+ padding: 0;
8
+ }
9
+
10
+ .rebilly-instruments-payment-instrument-list li {
11
+ display: flex;
12
+ width: 100%;
13
+ align-items: center;
14
+ list-style-type: none;
15
+ padding: var(--rebilly-spacings-xs) 0;
16
+ }
17
+
18
+ .rebilly-instruments-payment-instrument-list li:first-child {
19
+ padding-top: 0;
20
+ }
21
+
22
+ .rebilly-instruments-payment-instrument-list li:last-child {
23
+ padding-bottom: 0;
24
+ }
25
+
26
+ .rebilly-instruments-payment-instrument-list li + li {
27
+ border-top: 1px solid var(--rebilly-colorMutedBorder);
28
+ }
29
+
30
+ .rebilly-instruments-payment-instrument-list.is-relaxed li {
31
+ min-height: var(--rebilly-spacings-form-element-min-height);
32
+ }
33
+
34
+ .rebilly-instruments-payment-instrument-list .rebilly-instruments-form-field {
35
+ width: 100%;
36
+ }
37
+
38
+ .rebilly-instruments-payment-instrument-list li .rebilly-instruments-payment-instrument-list-container {
39
+ display: flex;
40
+ justify-content: space-between;
41
+ align-items: center;
42
+ width: 100%;
43
+ }
44
+ `;