@rebilly/framepay-react 2.0.1 → 2.1.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/.env.example +2 -0
- package/CHANGELOG.md +7 -0
- package/env.js +7 -0
- package/package.json +10 -2
- package/src/index.spec.ts +19 -0
- package/src/index.ts +42 -0
- package/src/lib/components/elements/applepay-element.tsx +36 -0
- package/src/lib/components/elements/bank-element.spec.tsx +119 -0
- package/src/lib/components/elements/bank-element.tsx +74 -0
- package/src/lib/components/elements/base-element.tsx +100 -0
- package/src/lib/components/elements/card-element.spec.tsx +113 -0
- package/src/lib/components/elements/card-element.tsx +73 -0
- package/src/lib/components/elements/googlepay-element.tsx +36 -0
- package/src/lib/components/elements/iban-element.spec.tsx +104 -0
- package/src/lib/components/elements/iban-element.tsx +75 -0
- package/src/lib/components/elements/paypal-element.tsx +36 -0
- package/src/lib/components/injector.spec.tsx +162 -0
- package/src/lib/components/injector.tsx +495 -0
- package/src/lib/components/provider.spec.tsx +98 -0
- package/src/lib/components/provider.tsx +111 -0
- package/src/lib/constants.ts +43 -0
- package/src/lib/context.ts +24 -0
- package/src/lib/dom-util.ts +35 -0
- package/src/lib/framepay-error.ts +59 -0
- package/src/lib/get-rebilly-api.ts +11 -0
- package/test/e2e/assets/prop-types.js +849 -0
- package/test/e2e/assets/react-0.14.0.js +18759 -0
- package/test/e2e/assets/react-15.0.0.js +19309 -0
- package/test/e2e/assets/react-16.js +3318 -0
- package/test/e2e/assets/react-17.js +3357 -0
- package/test/e2e/assets/react-18.js +3342 -0
- package/test/e2e/assets/react-dom-0.14.0.js +42 -0
- package/test/e2e/assets/react-dom-15.0.0.js +42 -0
- package/test/e2e/assets/react-dom-16.js +25147 -0
- package/test/e2e/assets/react-dom-17.js +26292 -0
- package/test/e2e/assets/react-dom-18.js +29869 -0
- package/test/e2e/cypress-support.js +2 -0
- package/test/e2e/cypress.d.ts +1 -0
- package/test/e2e/fixtures/apple-pay.html +15 -0
- package/test/e2e/fixtures/apple-pay.js +63 -0
- package/test/e2e/fixtures/bank-separate.html +12 -0
- package/test/e2e/fixtures/bank-separate.js +323 -0
- package/test/e2e/fixtures/card-separate-brands.html +12 -0
- package/test/e2e/fixtures/card-separate-brands.js +260 -0
- package/test/e2e/fixtures/card-separate-rebilly-fields.html +12 -0
- package/test/e2e/fixtures/card-separate-rebilly-fields.js +281 -0
- package/test/e2e/fixtures/card-separate.html +12 -0
- package/test/e2e/fixtures/card-separate.js +227 -0
- package/test/e2e/fixtures/checkout-combined.html +12 -0
- package/test/e2e/fixtures/checkout-combined.js +199 -0
- package/test/e2e/fixtures/google-pay.html +15 -0
- package/test/e2e/fixtures/google-pay.js +63 -0
- package/test/e2e/fixtures/iban.html +12 -0
- package/test/e2e/fixtures/iban.js +239 -0
- package/test/e2e/fixtures/multiple-methods.html +12 -0
- package/test/e2e/fixtures/multiple-methods.js +470 -0
- package/test/e2e/fixtures/nav.js +20 -0
- package/test/e2e/fixtures/paypal.html +15 -0
- package/test/e2e/fixtures/paypal.js +62 -0
- package/test/e2e/fixtures/style.css +55 -0
- package/test/e2e/fixtures/util.js +71 -0
- package/test/e2e/local-server.mjs +12 -0
- package/test/e2e/server.mjs +43 -0
- package/test/e2e/specs/bank-separate.cy.ts +27 -0
- package/test/e2e/specs/card-separate-brands.cy.ts +70 -0
- package/test/e2e/specs/card-separate.cy.ts +27 -0
- package/test/e2e/specs/checkout-combined.cy.ts +24 -0
- package/test/e2e/specs/google-pay.cy.ts +13 -0
- package/test/e2e/specs/iban.cy.ts +17 -0
- package/test/e2e/specs/multiple-methods.cy.ts +130 -0
- package/test/e2e/specs/paypal.cy.ts +13 -0
- package/test/e2e/specs/react-version.cy.ts +12 -0
- package/test/e2e/switch-react-version.js +42 -0
- package/test/e2e/tsconfig.json +8 -0
- package/test/unit/jest.config.js +29 -0
- package/test/unit/specs/declaration-mock.spec.tsx +143 -0
- package/tsconfig.json +31 -0
- package/tsconfig.spec.json +13 -0
- package/tslint.json +36 -0
package/.env.example
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
+
## [2.1.0](https://github.com/Rebilly/rebilly/compare/framepay-react-v2.0.1...framepay-react-v2.1.0) (2023-03-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add src files to contents of framepay-react package ([#51](https://github.com/Rebilly/rebilly/issues/51)) ([7b03ac1](https://github.com/Rebilly/rebilly/commit/7b03ac11af8b8da17b84926cbe46b37a285bf518))
|
|
7
|
+
|
|
1
8
|
## [2.0.1](https://github.com/Rebilly/rebilly/compare/framepay-react-v2.0.0...framepay-react-v2.0.1) (2023-03-15)
|
package/env.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebilly/framepay-react",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A React wrapper for Rebilly's FramePay offering out-of-the-box support for Redux and other common React features",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"author": "Rebilly",
|
|
@@ -18,7 +18,15 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"build",
|
|
20
20
|
"types",
|
|
21
|
-
"CHANGELOG.md"
|
|
21
|
+
"CHANGELOG.md",
|
|
22
|
+
"src",
|
|
23
|
+
"test",
|
|
24
|
+
".env.example",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"env.js",
|
|
27
|
+
"tsconfig.json",
|
|
28
|
+
"tsconfig.spec.json",
|
|
29
|
+
"tslint.json"
|
|
22
30
|
],
|
|
23
31
|
"scripts": {
|
|
24
32
|
"set-react-14": "cross-env REACT_VERSION=0.14.0 node ./test/e2e/switch-react-version",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as lib from './index';
|
|
2
|
+
|
|
3
|
+
const exportKeys: ReadonlyArray<string> = [
|
|
4
|
+
'SUPPORTED_CARD_BRANDS',
|
|
5
|
+
'FramePayProvider',
|
|
6
|
+
'withFramePay',
|
|
7
|
+
'withFramePayApplePayComponent',
|
|
8
|
+
'withFramePayCardComponent',
|
|
9
|
+
'withFramePayBankComponent',
|
|
10
|
+
'withFramePayGooglePayComponent',
|
|
11
|
+
'withFramePayIBANComponent',
|
|
12
|
+
'withFramePayPaypalComponent'
|
|
13
|
+
].sort();
|
|
14
|
+
|
|
15
|
+
describe('lib/index', () => {
|
|
16
|
+
it('Library exports list correctly', () => {
|
|
17
|
+
expect(Object.keys(lib).sort()).toEqual(exportKeys);
|
|
18
|
+
});
|
|
19
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SUPPORTED_CARD_BRANDS } from './lib/constants';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
withFramePay,
|
|
5
|
+
withFramePayApplePayComponent,
|
|
6
|
+
withFramePayBankComponent,
|
|
7
|
+
withFramePayCardComponent,
|
|
8
|
+
withFramePayGooglePayComponent,
|
|
9
|
+
withFramePayIBANComponent,
|
|
10
|
+
withFramePayPaypalComponent
|
|
11
|
+
} from './lib/components/injector';
|
|
12
|
+
|
|
13
|
+
import FramePayProvider from './lib/components/provider';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
FramePayApplePayProps,
|
|
17
|
+
FramePayBankProps,
|
|
18
|
+
FramePayCardProps,
|
|
19
|
+
FramePayComponentProps,
|
|
20
|
+
FramePayGooglePayProps,
|
|
21
|
+
FramePayIBANProps,
|
|
22
|
+
FramePayPaypalProps
|
|
23
|
+
} from '../types/injector';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
SUPPORTED_CARD_BRANDS,
|
|
27
|
+
FramePayProvider,
|
|
28
|
+
withFramePay,
|
|
29
|
+
withFramePayCardComponent,
|
|
30
|
+
withFramePayBankComponent,
|
|
31
|
+
withFramePayIBANComponent,
|
|
32
|
+
withFramePayApplePayComponent,
|
|
33
|
+
withFramePayGooglePayComponent,
|
|
34
|
+
withFramePayPaypalComponent,
|
|
35
|
+
FramePayComponentProps,
|
|
36
|
+
FramePayCardProps,
|
|
37
|
+
FramePayBankProps,
|
|
38
|
+
FramePayIBANProps,
|
|
39
|
+
FramePayApplePayProps,
|
|
40
|
+
FramePayGooglePayProps,
|
|
41
|
+
FramePayPaypalProps
|
|
42
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import FramePayError from '../../framepay-error';
|
|
3
|
+
import BaseElement from './base-element';
|
|
4
|
+
|
|
5
|
+
export default class ApplePayElement extends BaseElement<
|
|
6
|
+
ApplePayProps,
|
|
7
|
+
ApplePayState
|
|
8
|
+
> {
|
|
9
|
+
setupElement() {
|
|
10
|
+
const { Rebilly } = this.props;
|
|
11
|
+
|
|
12
|
+
const makeElement = () => {
|
|
13
|
+
// elementNode already checked in BaseElement.handleSetupElement
|
|
14
|
+
// just ts checks fix
|
|
15
|
+
if (!this.elementNode) {
|
|
16
|
+
throw FramePayError({
|
|
17
|
+
code: FramePayError.codes.elementMountError,
|
|
18
|
+
details: `ApplePayElement invalid elementNode`
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return Rebilly.applePay.mount(this.elementNode);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
throw FramePayError({
|
|
26
|
+
code: FramePayError.codes.elementMountError,
|
|
27
|
+
details: `ApplePayElement error in remote api call`,
|
|
28
|
+
trace: e
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const element = makeElement();
|
|
34
|
+
this.setState({ element });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Arg, Substitute } from '@fluffy-spoon/substitute';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import FramePayError from '../../framepay-error';
|
|
5
|
+
import BankElement from './bank-element';
|
|
6
|
+
|
|
7
|
+
describe('BankElement', () => {
|
|
8
|
+
it('should not setup the element while api is not ready', () => {
|
|
9
|
+
const props = Substitute.for<BankProps>();
|
|
10
|
+
|
|
11
|
+
const setupElementSpy = jest.spyOn(
|
|
12
|
+
BankElement.prototype,
|
|
13
|
+
'setupElement'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
render(
|
|
17
|
+
<BankElement
|
|
18
|
+
{...props}
|
|
19
|
+
Rebilly={{
|
|
20
|
+
...props.Rebilly,
|
|
21
|
+
bankAccount: props.Rebilly.bankAccount,
|
|
22
|
+
ready: false
|
|
23
|
+
}}
|
|
24
|
+
elementType="bankRoutingNumber"
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(setupElementSpy).not.toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should setup the element when api is ready', () => {
|
|
32
|
+
const props = Substitute.for<BankProps>();
|
|
33
|
+
|
|
34
|
+
const setupElementSpy = jest.spyOn(
|
|
35
|
+
BankElement.prototype,
|
|
36
|
+
'setupElement'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
render(
|
|
40
|
+
<BankElement
|
|
41
|
+
{...props}
|
|
42
|
+
Rebilly={{
|
|
43
|
+
...props.Rebilly,
|
|
44
|
+
bankAccount: props.Rebilly.bankAccount,
|
|
45
|
+
ready: true
|
|
46
|
+
}}
|
|
47
|
+
elementType="bankAccountType"
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(setupElementSpy).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should destroy the element on component unmount', done => {
|
|
55
|
+
const props = Substitute.for<BankProps>();
|
|
56
|
+
const element = Substitute.for<PaymentElement>();
|
|
57
|
+
|
|
58
|
+
element.destroy().mimicks(() => {
|
|
59
|
+
done();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
props.Rebilly.bankAccount.mount(Arg.any(), Arg.any()).returns(element);
|
|
63
|
+
|
|
64
|
+
class TmpComponent extends React.Component {
|
|
65
|
+
render() {
|
|
66
|
+
return (
|
|
67
|
+
<BankElement
|
|
68
|
+
{...props}
|
|
69
|
+
Rebilly={{
|
|
70
|
+
...props.Rebilly,
|
|
71
|
+
bankAccount: props.Rebilly.bankAccount,
|
|
72
|
+
ready: true
|
|
73
|
+
}}
|
|
74
|
+
elementType="bankRoutingNumber"
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const { unmount } = render(<TmpComponent />);
|
|
81
|
+
unmount();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should render the empty div element', () => {
|
|
85
|
+
const props = Substitute.for<BankProps>();
|
|
86
|
+
|
|
87
|
+
props.Rebilly.ready.returns(true);
|
|
88
|
+
|
|
89
|
+
const { container } = render(
|
|
90
|
+
<BankElement {...props} Rebilly={props.Rebilly} />
|
|
91
|
+
);
|
|
92
|
+
expect(container.firstChild).toMatchInlineSnapshot(`<div />`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should fail the element mount on remote error', () => {
|
|
96
|
+
const props = Substitute.for<BankProps>();
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
render(
|
|
100
|
+
<BankElement
|
|
101
|
+
{...props}
|
|
102
|
+
Rebilly={{
|
|
103
|
+
...props.Rebilly,
|
|
104
|
+
bankAccount: {
|
|
105
|
+
...props.Rebilly.bankAccount,
|
|
106
|
+
mount: null
|
|
107
|
+
},
|
|
108
|
+
ready: true
|
|
109
|
+
}}
|
|
110
|
+
elementType="bankAccountNumber"
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
// never
|
|
114
|
+
expect(true).toEqual(false);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
expect(error.code).toEqual(FramePayError.codes.elementMountError);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import FramePayError from '../../framepay-error';
|
|
3
|
+
import BaseElement from './base-element';
|
|
4
|
+
|
|
5
|
+
export default class BankElement extends BaseElement<BankProps, BankState> {
|
|
6
|
+
setupElement() {
|
|
7
|
+
const { onReady, onChange, onFocus, onBlur, elementType } = this.props;
|
|
8
|
+
|
|
9
|
+
const makeElement = () => {
|
|
10
|
+
// elementNode already checked in BaseElement.handleSetupElement
|
|
11
|
+
// just ts checks fix
|
|
12
|
+
if (!this.elementNode) {
|
|
13
|
+
throw FramePayError({
|
|
14
|
+
code: FramePayError.codes.elementMountError,
|
|
15
|
+
details: `BankElement invalid elementNode, elementType: ${elementType ||
|
|
16
|
+
'default'}`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
return this.props.Rebilly.bankAccount.mount(
|
|
22
|
+
this.elementNode,
|
|
23
|
+
elementType
|
|
24
|
+
);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
throw FramePayError({
|
|
27
|
+
code: FramePayError.codes.elementMountError,
|
|
28
|
+
details: `BankElement error in remote api call, elementType: ${elementType ||
|
|
29
|
+
'default'}`,
|
|
30
|
+
trace: e
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const element = makeElement();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
element.on('ready', () => {
|
|
39
|
+
this.setState({ ready: true }, () => {
|
|
40
|
+
if (onReady) {
|
|
41
|
+
onReady();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
element.on('change', (data: PaymentElementOnChangeEventData) => {
|
|
47
|
+
if (onChange) {
|
|
48
|
+
onChange(data);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
element.on('focus', () => {
|
|
53
|
+
if (onFocus) {
|
|
54
|
+
onFocus();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
element.on('blur', () => {
|
|
59
|
+
if (onBlur) {
|
|
60
|
+
onBlur();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.setState({ element });
|
|
65
|
+
} catch (e) {
|
|
66
|
+
throw FramePayError({
|
|
67
|
+
code: FramePayError.codes.elementMountError,
|
|
68
|
+
details: `BankElement events binding error, elementType: ${elementType ||
|
|
69
|
+
'default'}`,
|
|
70
|
+
trace: e
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export default class BaseElement<
|
|
4
|
+
T extends PaymentComponentProps,
|
|
5
|
+
S extends PaymentComponentState
|
|
6
|
+
> extends React.Component<T, S> {
|
|
7
|
+
readonly state = {
|
|
8
|
+
element: null,
|
|
9
|
+
mounted: false,
|
|
10
|
+
ready: false
|
|
11
|
+
} as S;
|
|
12
|
+
|
|
13
|
+
/* tslint:disable:readonly-keyword */
|
|
14
|
+
protected elementNode: HTMLDivElement | null = null;
|
|
15
|
+
|
|
16
|
+
/* tslint:enable:readonly-keyword */
|
|
17
|
+
|
|
18
|
+
componentWillUnmount() {
|
|
19
|
+
if (this.state.mounted && !this.state.element) {
|
|
20
|
+
// tslint:disable-next-line:no-console
|
|
21
|
+
console.log(
|
|
22
|
+
`WARNING Element does not exist, please fix the setupElement method and add setState({element})`
|
|
23
|
+
);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (this.state.element) {
|
|
27
|
+
this.state.element.destroy();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
componentDidMount() {
|
|
32
|
+
this.handleSetupElement();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setupElement() {
|
|
36
|
+
throw new Error(`Please implement method setupElement`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
handleSetupElement() {
|
|
40
|
+
if (!this.props.Rebilly.ready) {
|
|
41
|
+
/**
|
|
42
|
+
* The remote api isn't ready
|
|
43
|
+
*/
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (this.state.mounted) {
|
|
47
|
+
/**
|
|
48
|
+
* The field already mounted
|
|
49
|
+
*/
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!this.elementNode) {
|
|
53
|
+
/**
|
|
54
|
+
* Component dom element not mounted
|
|
55
|
+
*/
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Setup field
|
|
60
|
+
*/
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
this.state.mounted = true;
|
|
63
|
+
this.setupElement();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
shouldComponentUpdate(nextProps: any, nextState: any) {
|
|
67
|
+
// we can't use the componentDidUpdate, componentWillReceiveProps methods
|
|
68
|
+
// also, we can't return true here (to avoid the dom element re-render)
|
|
69
|
+
// so, in that case we had to use that method as componentDidUpdate or componentWillReceiveProps
|
|
70
|
+
// with some magic
|
|
71
|
+
const rules: ReadonlyArray<any> = [
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
[this.props.Rebilly.ready, nextProps.Rebilly.ready],
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
[this.state.mounted, nextState.mounted],
|
|
76
|
+
// @ts-ignore
|
|
77
|
+
[this.state.ready, nextState.ready],
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
[!!this.state.element, !!nextState.element]
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const shouldUpdate = rules.find(([prev, next]) => prev !== next);
|
|
83
|
+
|
|
84
|
+
if (shouldUpdate) {
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
this.props = nextProps;
|
|
87
|
+
this.handleSetupElement();
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
this.state = { ...nextState };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
render() {
|
|
96
|
+
return (
|
|
97
|
+
<div id={this.props.id} ref={node => (this.elementNode = node)} />
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Substitute } from '@fluffy-spoon/substitute';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import FramePayError from '../../framepay-error';
|
|
5
|
+
import CardElement from './card-element';
|
|
6
|
+
|
|
7
|
+
describe('CardElement', () => {
|
|
8
|
+
it('should not setup the element while api is not ready', () => {
|
|
9
|
+
const props = Substitute.for<CardProps>();
|
|
10
|
+
|
|
11
|
+
props.Rebilly.ready.returns(false);
|
|
12
|
+
|
|
13
|
+
const setupElementSpy = jest.spyOn(
|
|
14
|
+
CardElement.prototype,
|
|
15
|
+
'setupElement'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
render(<CardElement {...props} Rebilly={props.Rebilly} />);
|
|
19
|
+
|
|
20
|
+
expect(setupElementSpy).not.toHaveBeenCalled();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should setup the element when api is ready', () => {
|
|
24
|
+
const props = Substitute.for<CardProps>();
|
|
25
|
+
const setupElementSpy = jest.spyOn(
|
|
26
|
+
CardElement.prototype,
|
|
27
|
+
'setupElement'
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const { rerender } = render(
|
|
31
|
+
<CardElement
|
|
32
|
+
Rebilly={{
|
|
33
|
+
ready: false
|
|
34
|
+
}}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(setupElementSpy).toHaveBeenCalledTimes(0);
|
|
39
|
+
|
|
40
|
+
rerender(
|
|
41
|
+
<CardElement
|
|
42
|
+
Rebilly={{
|
|
43
|
+
card: props.Rebilly.card,
|
|
44
|
+
ready: true
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(setupElementSpy).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should render the empty div element', () => {
|
|
53
|
+
const props = Substitute.for<CardProps>();
|
|
54
|
+
const { container } = render(
|
|
55
|
+
<CardElement {...props} Rebilly={props.Rebilly} />
|
|
56
|
+
);
|
|
57
|
+
expect(container.firstChild).toMatchInlineSnapshot(`<div />`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should destroy the element on component unmount', done => {
|
|
61
|
+
const props = Substitute.for<CardProps>();
|
|
62
|
+
const element = Substitute.for<PaymentElement>();
|
|
63
|
+
|
|
64
|
+
element.destroy().mimicks(() => {
|
|
65
|
+
done();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
class TmpComponent extends React.Component {
|
|
69
|
+
render() {
|
|
70
|
+
return (
|
|
71
|
+
<CardElement
|
|
72
|
+
{...props}
|
|
73
|
+
Rebilly={{
|
|
74
|
+
...props.Rebilly,
|
|
75
|
+
card: {
|
|
76
|
+
...props.Rebilly.card,
|
|
77
|
+
mount: () => element
|
|
78
|
+
},
|
|
79
|
+
ready: true
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { unmount } = render(<TmpComponent />);
|
|
87
|
+
unmount();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should fail the element mount on remote error', () => {
|
|
91
|
+
const props = Substitute.for<CardProps>();
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
render(
|
|
95
|
+
<CardElement
|
|
96
|
+
{...props}
|
|
97
|
+
Rebilly={{
|
|
98
|
+
...props.Rebilly,
|
|
99
|
+
card: {
|
|
100
|
+
...props.Rebilly.card,
|
|
101
|
+
mount: null
|
|
102
|
+
},
|
|
103
|
+
ready: true
|
|
104
|
+
}}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
// never
|
|
108
|
+
expect(true).toEqual(false);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
expect(error.code).toEqual(FramePayError.codes.elementMountError);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import FramePayError from '../../framepay-error';
|
|
3
|
+
import BaseElement from './base-element';
|
|
4
|
+
|
|
5
|
+
export default class CardElement extends BaseElement<CardProps, CardState> {
|
|
6
|
+
setupElement() {
|
|
7
|
+
const { onReady, onChange, onFocus, onBlur, elementType } = this.props;
|
|
8
|
+
|
|
9
|
+
const makeElement = () => {
|
|
10
|
+
// elementNode already checked in BaseElement.handleSetupElement
|
|
11
|
+
// just ts checks fix
|
|
12
|
+
if (!this.elementNode) {
|
|
13
|
+
throw FramePayError({
|
|
14
|
+
code: FramePayError.codes.elementMountError,
|
|
15
|
+
details: `CardElement invalid elementNode, elementType: ${elementType ||
|
|
16
|
+
'default'}`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
return this.props.Rebilly.card.mount(
|
|
22
|
+
this.elementNode,
|
|
23
|
+
elementType
|
|
24
|
+
);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
throw FramePayError({
|
|
27
|
+
code: FramePayError.codes.elementMountError,
|
|
28
|
+
details: `CardElement error in remote api call, elementType: ${elementType ||
|
|
29
|
+
'default'}`,
|
|
30
|
+
trace: e
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const element = makeElement();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
element.on('ready', () => {
|
|
39
|
+
this.setState({ ready: true }, () => {
|
|
40
|
+
if (onReady) {
|
|
41
|
+
onReady();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
element.on('change', (data: PaymentElementOnChangeEventData) => {
|
|
47
|
+
if (onChange) {
|
|
48
|
+
onChange(data);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
element.on('focus', () => {
|
|
53
|
+
if (onFocus) {
|
|
54
|
+
onFocus();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
element.on('blur', () => {
|
|
58
|
+
if (onBlur) {
|
|
59
|
+
onBlur();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
this.setState({ element });
|
|
64
|
+
} catch (e) {
|
|
65
|
+
throw FramePayError({
|
|
66
|
+
code: FramePayError.codes.elementMountError,
|
|
67
|
+
details: `CardElement events binding error, elementType: ${elementType ||
|
|
68
|
+
'default'}`,
|
|
69
|
+
trace: e
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import FramePayError from '../../framepay-error';
|
|
3
|
+
import BaseElement from './base-element';
|
|
4
|
+
|
|
5
|
+
export default class GooglePayElement extends BaseElement<
|
|
6
|
+
GooglePayProps,
|
|
7
|
+
GooglePayState
|
|
8
|
+
> {
|
|
9
|
+
setupElement() {
|
|
10
|
+
const { Rebilly } = this.props;
|
|
11
|
+
|
|
12
|
+
const makeElement = () => {
|
|
13
|
+
// elementNode already checked in BaseElement.handleSetupElement
|
|
14
|
+
// just ts checks fix
|
|
15
|
+
if (!this.elementNode) {
|
|
16
|
+
throw FramePayError({
|
|
17
|
+
code: FramePayError.codes.elementMountError,
|
|
18
|
+
details: `GooglePayElement invalid elementNode`
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return Rebilly.googlePay.mount(this.elementNode);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
throw FramePayError({
|
|
26
|
+
code: FramePayError.codes.elementMountError,
|
|
27
|
+
details: `GooglePayElement error in remote api call`,
|
|
28
|
+
trace: e
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const element = makeElement();
|
|
34
|
+
this.setState({ element });
|
|
35
|
+
}
|
|
36
|
+
}
|