UNPKG

@pactsafe/pactsafe-react-sdk

Version:

PactSafe React SDK - React SDK for easy Clickwrap/Browsewrap implementation leveraging PactSafe's JavaScript API

233 lines (213 loc) 8.13 kB
/* global _ps */ import React from 'react'; import PropTypes from 'prop-types'; import isRequiredIf from 'react-proptype-conditional-require'; import uuid from 'uuid/v4'; import PSSnippet from './PSSnippet'; class PSClickWrap extends React.Component { constructor(props) { super(props); this.createClickWrap = this.createClickWrap.bind(this); this.state = { clickwrapGroupKey: null, dynamicGroup: false, }; this.propsEventMap = { onAll: 'all', onSent: 'sent', onRetrieved: 'retrieved', onSet: 'set', onSetSignerId: 'set:signer_id', onValid: 'valid', onInvalid: 'invalid', onRendered: 'rendered', onDisplayed: 'displayed', onScrolledContract: 'scrolled:contract', onScrolled: 'scrolled', onError: 'error', }; const { psScriptUrl, backupScriptURL, accessId, testMode, disableSending, dynamic, signerId, debug } = this.props; if (!PSSnippet.isSnippetLoaded(psScriptUrl, backupScriptURL)) { PSSnippet.injectSnippet(psScriptUrl, backupScriptURL); } _ps('create', accessId, { test_mode: testMode, disable_sending: disableSending, dynamic, signer_id: signerId, }); if (debug) { _ps.debug = true; } } componentDidMount() { this.createClickWrap(); } componentDidUpdate(prevProps) { const { clickWrapStyle, renderData, filter, groupKey, } = this.props; const { clickwrapGroupKey, dynamicGroup } = this.state; if (clickWrapStyle !== prevProps.clickWrapStyle && !dynamicGroup) { _ps.getByKey(clickwrapGroupKey).site.set('style', clickWrapStyle); _ps.getByKey(clickwrapGroupKey).retrieveHTML(); } if (renderData !== prevProps.renderData) { _ps(`${clickwrapGroupKey}:retrieveHTML`, renderData); } if (clickWrapStyle !== prevProps.clickWrapStyle && dynamicGroup) { this.createClickWrap(); } if (filter !== prevProps.filter && dynamicGroup) { this.createClickWrap(); } if (groupKey !== prevProps.groupKey && !dynamicGroup) { this.createClickWrap(); _ps.getByKey(clickwrapGroupKey).retrieveHTML(); } } componentWillUnmount() { const { groupKey } = this.props; if (_ps && _ps.getByKey(groupKey) && _ps.getByKey(groupKey).rendered) { _ps.getByKey(groupKey).rendered = false; } this.unregisterEventListeners(); } registerEventListener(eventProp, groupKey) { const eventCallbackFn = (...args) => { let shouldFireListener = false; args.forEach((arg) => { // We need to check the context variable and see if it matches the groupKey, if it does -> fire the event (context argument position varies) if (arg.get && arg.get('key') && arg.get('key') === groupKey) { shouldFireListener = true; // Else we should check if the context is for the entire site, and as such the context variable is a Site object. } else if (arg.toString() === '[object Site]') { shouldFireListener = true; } }); if (shouldFireListener) { this.props[eventProp](...args); } }; // In order to handle unregistration of event listeners, we override the toString function to identify the // function by a UUID rather than the default toString of a function. const newEventListenerID = uuid(); eventCallbackFn.toString = () => newEventListenerID; _ps.on(this.propsEventMap[eventProp], eventCallbackFn); return eventCallbackFn.toString(); } registerEventListeners(groupKey) { const eventListeners = {}; Object.keys(this.propsEventMap).forEach((eventProp) => { if (this.props[eventProp]) { eventListeners[this.propsEventMap[eventProp]] = this.registerEventListener(eventProp, groupKey); } }); // Store event listeners in state so we can unregister them later on unmount this.setState({ eventListeners }); } unregisterEventListeners() { Object.keys(this.state.eventListeners).forEach((event) => { const eventUUID = this.state.eventListeners[event]; // In order to unregister the event, we must create a fake function (typeof passed to _ps.off must be a function), // that returns the UUID we want to unregister. const fakeEventListener = () => eventUUID; fakeEventListener.toString = () => eventUUID; _ps.off(event, fakeEventListener); }); } createClickWrap() { const { filter, containerId, signerIdSelector, clickWrapStyle, displayAll, renderData, displayImmediately, forceScroll, groupKey, confirmationEmail } = this.props; const options = { filter, container_selector: containerId, confirmation_email: confirmationEmail, signer_id_selector: signerIdSelector, style: clickWrapStyle, display_all: displayAll, render_data: renderData, auto_run: displayImmediately, force_scroll: forceScroll }; if (groupKey) this.setState({ clickwrapGroupKey: groupKey, dynamicGroup: false }); const isDynamic = !groupKey; const eventCallback = (err, group) => { if (group) { const key = groupKey || group.get('key'); const state = { clickwrapGroupKey: key }; if (isDynamic) state.dynamicGroup = true; this.setState(state); if (!isDynamic) group.render(); this.registerEventListeners(key); } }; if (groupKey) _ps('load', groupKey, { ...options, event_callback: eventCallback }); else _ps('load', { ...options, event_callback: eventCallback }); } render() { const { containerId } = this.props; return <div id={containerId} />; } } PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE = 'PSClickWrap Error: You must provide either a groupKey or filter prop in order to use the PactSafe ClickWrap component!'; PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE = 'PSClickWrap Error: You must provide a renderData prop when passing down the dynamic prop'; PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR = 'PSClickWrap Error: You must provide either a signer ID or a signer ID selector'; PSClickWrap.propTypes = { accessId: PropTypes.string.isRequired, clickWrapStyle: PropTypes.oneOf([ 'full', 'scroll', 'checkbox', 'combined', 'embedded', ]), confirmationEmail: PropTypes.bool, disableSending: PropTypes.bool, displayAll: PropTypes.bool, displayImmediately: PropTypes.bool, dynamic: PropTypes.bool, containerId: PropTypes.string, filter: isRequiredIf( PropTypes.string, props => !props.hasOwnProperty('groupKey'), PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE, ), forceScroll: PropTypes.bool, groupKey: isRequiredIf( PropTypes.string, props => !props.hasOwnProperty('filter'), PSClickWrap.FILTER_OR_GROUPKEY_REQUIRED_ERROR_MESSAGE, ), psScriptUrl: PropTypes.string, backupScriptURL: PropTypes.string, renderData: isRequiredIf( PropTypes.object, props => props.hasOwnProperty('dynamic') && props.dynamic === true, PSClickWrap.MUST_PROVIDE_RENDER_DATA_ERROR_MESSAGE, ), signerIdSelector: isRequiredIf( PropTypes.string, props => !props.hasOwnProperty('signerId'), PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR, ), signerId: isRequiredIf( PropTypes.string, props => !props.hasOwnProperty('signerIdSelector'), PSClickWrap.MUST_PROVIDE_SIGNER_ID_OR_SIGNER_ID_SELECTOR, ), testMode: PropTypes.bool, debug: PropTypes.bool, onAll: PropTypes.func, onSent: PropTypes.func, onRetrieved: PropTypes.func, onSet: PropTypes.func, onSetSignerId: PropTypes.func, onValid: PropTypes.func, onInvalid: PropTypes.func, onRendered: PropTypes.func, onDisplayed: PropTypes.func, onScrolledContract: PropTypes.func, onScrolled: PropTypes.func, onError: PropTypes.func, }; PSClickWrap.defaultProps = { psScriptUrl: '//vault.pactsafe.io/ps.min.js', backupScriptURL: '//d3l1mqnl5xpsuc.cloudfront.net/ps.min.js', containerId: 'ps-clickwrap', displayImmediately: true, disableSending: false, displayAll: true, dynamic: false, testMode: false, }; export default PSClickWrap;