@rebilly/framepay-react 2.0.0 → 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 +8 -0
- package/README.md +7 -6
- package/env.js +7 -0
- package/package.json +23 -17
- 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
ADDED
|
@@ -0,0 +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
|
+
|
|
8
|
+
## [2.0.1](https://github.com/Rebilly/rebilly/compare/framepay-react-v2.0.0...framepay-react-v2.0.1) (2023-03-15)
|
package/README.md
CHANGED
|
@@ -35,10 +35,11 @@ This package is a wrapper for [FramePay](https://rebilly.github.io/framepay-docs
|
|
|
35
35
|
- [Incorrect](#incorrect)
|
|
36
36
|
- [Correct](#correct)
|
|
37
37
|
- [Developer instructions](#developer-instructions)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
- [Prerequisites](#prerequisites)
|
|
39
|
+
- [Project setup](#project-setup)
|
|
40
|
+
- [Manual preview](#manual-preview)
|
|
41
|
+
- [How to run unit tests?](#how-to-run-unit-tests)
|
|
42
|
+
- [How to run E2E tests?](#how-to-run-e2e-tests)
|
|
42
43
|
|
|
43
44
|
### FramePay documentation
|
|
44
45
|
For more information on FramePay see its [official documentation](https://www.rebilly.com/docs/dev-docs/framepay/).
|
|
@@ -344,5 +345,5 @@ Unit tests can be run using the `test:unit` command.
|
|
|
344
345
|
#### How to run E2E tests?
|
|
345
346
|
- Ensure you are running the preview examples, otherwise all E2E tests will fail
|
|
346
347
|
- Run one of the following commands:
|
|
347
|
-
- `test:
|
|
348
|
-
- `test:
|
|
348
|
+
- `test:e2e:run` - runs all tests headlessly
|
|
349
|
+
- `test:e2e:open` - opens GUI which allows you to interact with each test visually
|
package/env.js
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebilly/framepay-react",
|
|
3
|
-
"version": "2.
|
|
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",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
8
|
-
"repository": "https://github.com/Rebilly/
|
|
8
|
+
"repository": "https://github.com/Rebilly/rebilly",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"framepay",
|
|
@@ -17,7 +17,16 @@
|
|
|
17
17
|
],
|
|
18
18
|
"files": [
|
|
19
19
|
"build",
|
|
20
|
-
"types"
|
|
20
|
+
"types",
|
|
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"
|
|
21
30
|
],
|
|
22
31
|
"scripts": {
|
|
23
32
|
"set-react-14": "cross-env REACT_VERSION=0.14.0 node ./test/e2e/switch-react-version",
|
|
@@ -35,21 +44,19 @@
|
|
|
35
44
|
"fix": "run-s fix:*",
|
|
36
45
|
"fix:prettier": "prettier \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" \"test/**/*.tsx\" \"./*.json\" --write",
|
|
37
46
|
"fix:tslint": "tslint --fix --project .",
|
|
38
|
-
"
|
|
47
|
+
"lint:no-fix": "tslint --project . && prettier \"src/**/*.ts\" \"src/**/*.tsx\" \"test/**/*.ts\" \"test/**/*.tsx\" \"./*.json\" --list-different",
|
|
39
48
|
"test:unit": "jest --config ./test/unit/jest.config.js",
|
|
40
|
-
"test:
|
|
41
|
-
"test:
|
|
42
|
-
"test:
|
|
43
|
-
"test:
|
|
44
|
-
"test:
|
|
45
|
-
"test:
|
|
46
|
-
"test:
|
|
47
|
-
"test:
|
|
48
|
-
"test:
|
|
49
|
+
"test:e2e:base": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test serve:e2e:no-build https-get://localhost:8000 test:e2e:run",
|
|
50
|
+
"test:e2e:react-14": "cross-env REACT_VERSION=0.14.0 run-s set-react-14 dotenv build:e2e clean-react-alias test:e2e:base",
|
|
51
|
+
"test:e2e:react-15": "cross-env REACT_VERSION=15.0.0 run-s set-react-15 dotenv build:e2e clean-react-alias test:e2e:base",
|
|
52
|
+
"test:e2e:react-16": "cross-env REACT_VERSION=16 run-s set-react-16 dotenv build:e2e clean-react-alias test:e2e:base",
|
|
53
|
+
"test:e2e:react-17": "cross-env REACT_VERSION=17 run-s set-react-17 dotenv build:e2e clean-react-alias test:e2e:base",
|
|
54
|
+
"test:e2e:react-18": "cross-env REACT_VERSION=18 run-s set-react-18 dotenv build:e2e clean-react-alias test:e2e:base",
|
|
55
|
+
"test:e2e": "run-s test:e2e:react-*",
|
|
56
|
+
"test:e2e:open": "cypress open",
|
|
57
|
+
"test:e2e:run": "cypress run",
|
|
49
58
|
"watch": "run-s build && run-p \"build -- --w\"",
|
|
50
|
-
"clean:e2e": "rm -rf test/e2e/build"
|
|
51
|
-
"precommit": "run-s fix",
|
|
52
|
-
"prepush": "run-s fix"
|
|
59
|
+
"clean:e2e": "rm -rf test/e2e/build"
|
|
53
60
|
},
|
|
54
61
|
"peerDependencies": {
|
|
55
62
|
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
|
@@ -72,7 +79,6 @@
|
|
|
72
79
|
"cypress-iframe": "^1.0.1",
|
|
73
80
|
"dotenv": "^7.0.0",
|
|
74
81
|
"express": "^4.16.4",
|
|
75
|
-
"husky": "^1.3.1",
|
|
76
82
|
"jest": "^29.3.1",
|
|
77
83
|
"jest-environment-jsdom": "^29.3.1",
|
|
78
84
|
"npm-run-all": "^4.1.5",
|
|
@@ -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
|
+
});
|