diff --git a/lib/three-d-secure-action.jsx b/lib/three-d-secure-action.jsx index de8c7ab..37bfc40 100644 --- a/lib/three-d-secure-action.jsx +++ b/lib/three-d-secure-action.jsx @@ -20,6 +20,16 @@ export default class ThreeDSecureAction extends React.PureComponent { */ actionTokenId: PropTypes.string, + /** + * Called when the 3-D Secure flow is ready for interaction + * @type {ThreeDSecureAction~onReady} + */ + + /** + * @callback ThreeDSecureAction~onReady + */ + onReady: PropTypes.func, + /** * Called when the user has completed the 3D Secure flow * @type {ThreeDSecureAction~onToken} @@ -47,6 +57,7 @@ export default class ThreeDSecureAction extends React.PureComponent { id: undefined, className: undefined, actionTokenId: '', + onReady: () => {}, onToken: () => {}, onError: e => { throw e } }; @@ -62,15 +73,19 @@ export default class ThreeDSecureAction extends React.PureComponent { const { actionTokenId } = props; + this._attached = false; this._container = React.createRef(); this._risk = this.context.recurly.Risk(); this._threeDSecure = this._risk.ThreeDSecure({ actionTokenId }); + this._threeDSecure.on('ready', (...args) => this.props.onReady(...args)); this._threeDSecure.on('token', (...args) => this.props.onToken(...args)); this._threeDSecure.on('error', (...args) => this.props.onError(...args)); } componentDidMount () { + if (this._attached) return; this._threeDSecure.attach(this._container.current); + this._attached = true; } render () { diff --git a/test/three-d-secure-action.test.jsx b/test/three-d-secure-action.test.jsx index 60dc31e..d4d0928 100644 --- a/test/three-d-secure-action.test.jsx +++ b/test/three-d-secure-action.test.jsx @@ -45,6 +45,31 @@ describe('', function () { describe('event handlers', function () { const example = { arbitrary: 'properties' }; + describe('[onReady]', function () { + it('is called when the underlying ThreeDSecure instance is ready', function () { + const subject = jest.fn(); + let fixture; + + render(withRecurlyProvider( + fixture = ref} + /> + )); + + const instance = getThreeDSecureInstanceFrom(fixture); + + // stub strategy so that the readiness chain will not fall apart + instance.strategy = { + attach: () => {} + }; + + instance.emit('ready'); + expect(subject).toHaveBeenCalled(); + }); + }); + describe('[onToken]', function () { it('is called when the underlying ThreeDSecure instance receives a token', function () { const subject = jest.fn(); diff --git a/types/index.d.ts b/types/index.d.ts index e46a1a3..e0407c5 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -117,6 +117,7 @@ export type ThreeDSecureActionProps = { id?: string; className?: string; actionTokenId?: string; + onReady?: () => void; onToken?: (token: TokenPayload) => void; onError?: (e: RecurlyError) => void; };