Add/wcpay usage tracking request (https://github.com/woocommerce/woocommerce-admin/pull/5248)
Fixes woocommerce/woocommerce-admin#5294 and Automattic/woocommerce-paymentswoocommerce/woocommerce-admin#810 . Changes: * Update UsageModal UI to have two action buttons instead of 'Count me in' checkbox. * Make UsageModal configurable with custom title, message and buttons text. * Add customized modal to request site usage tracking after WC Payments KYC flow is completed.
This commit is contained in:
parent
cc5e500085
commit
5dab25382f
|
@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
|
||||||
import { get, isFunction, identity } from 'lodash';
|
import { get, isFunction, identity } from 'lodash';
|
||||||
import { parse } from 'qs';
|
import { parse } from 'qs';
|
||||||
import { Spinner } from '@woocommerce/components';
|
import { Spinner } from '@woocommerce/components';
|
||||||
import { getHistory } from '@woocommerce/navigation';
|
import { getHistory, getQuery } from '@woocommerce/navigation';
|
||||||
import { getSetting } from '@woocommerce/wc-admin-settings';
|
import { getSetting } from '@woocommerce/wc-admin-settings';
|
||||||
import {
|
import {
|
||||||
PLUGINS_STORE_NAME,
|
PLUGINS_STORE_NAME,
|
||||||
|
@ -32,6 +32,12 @@ const StoreAlerts = lazy( () =>
|
||||||
import( /* webpackChunkName: "store-alerts" */ './store-alerts' )
|
import( /* webpackChunkName: "store-alerts" */ './store-alerts' )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const WCPayUsageModal = lazy( () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "wcpay-usage-modal" */ '../task-list/tasks/payments/wcpay-usage-modal'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export class PrimaryLayout extends Component {
|
export class PrimaryLayout extends Component {
|
||||||
render() {
|
render() {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
|
@ -121,6 +127,15 @@ class _Layout extends Component {
|
||||||
return parse( search );
|
return parse( search );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isWCPaySettingsPage() {
|
||||||
|
const { page, section, tab } = getQuery();
|
||||||
|
return (
|
||||||
|
page === 'wc-settings' &&
|
||||||
|
tab === 'checkout' &&
|
||||||
|
section === 'woocommerce_payments'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isEmbedded, ...restProps } = this.props;
|
const { isEmbedded, ...restProps } = this.props;
|
||||||
const { location, page } = this.props;
|
const { location, page } = this.props;
|
||||||
|
@ -146,6 +161,12 @@ class _Layout extends Component {
|
||||||
</div>
|
</div>
|
||||||
</PrimaryLayout>
|
</PrimaryLayout>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
|
{ isEmbedded && this.isWCPaySettingsPage() && (
|
||||||
|
<Suspense fallback={ null }>
|
||||||
|
<WCPayUsageModal />
|
||||||
|
</Suspense>
|
||||||
|
) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,69 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.components-modal__frame.woocommerce-usage-modal {
|
||||||
|
width: 600px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
.components-modal__header {
|
||||||
|
border-bottom: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-usage-modal__wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $studio-gray-60;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.is-primary {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-usage-modal__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: $gap;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: $gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-payments__usage-modal {
|
||||||
|
.components-modal__header {
|
||||||
|
height: auto;
|
||||||
|
padding: 24px 24px 0 24px;
|
||||||
|
|
||||||
|
.components-modal__header-heading {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-payments__usage-modal-message {
|
||||||
|
padding: $gap 0;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.woocommerce-payments__usage-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: $gap 0;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: $gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.components-modal__frame.woocommerce-cart-modal {
|
.components-modal__frame.woocommerce-cart-modal {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
@ -6,12 +6,7 @@ import { Component } from '@wordpress/element';
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
import { withDispatch, withSelect } from '@wordpress/data';
|
import { withDispatch, withSelect } from '@wordpress/data';
|
||||||
import interpolateComponents from 'interpolate-components';
|
import interpolateComponents from 'interpolate-components';
|
||||||
import {
|
import { Button, Modal } from '@wordpress/components';
|
||||||
Button,
|
|
||||||
CheckboxControl,
|
|
||||||
FormToggle,
|
|
||||||
Modal,
|
|
||||||
} from '@wordpress/components';
|
|
||||||
import { Link } from '@woocommerce/components';
|
import { Link } from '@woocommerce/components';
|
||||||
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||||
|
|
||||||
|
@ -19,17 +14,9 @@ class UsageModal extends Component {
|
||||||
constructor( props ) {
|
constructor( props ) {
|
||||||
super( props );
|
super( props );
|
||||||
this.state = {
|
this.state = {
|
||||||
allowTracking: props.allowTracking,
|
|
||||||
isLoadingScripts: false,
|
isLoadingScripts: false,
|
||||||
|
isRequestStarted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onTrackingChange = this.onTrackingChange.bind( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
onTrackingChange() {
|
|
||||||
this.setState( {
|
|
||||||
allowTracking: ! this.state.allowTracking,
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidUpdate( prevProps, prevState ) {
|
async componentDidUpdate( prevProps, prevState ) {
|
||||||
|
@ -40,7 +27,13 @@ class UsageModal extends Component {
|
||||||
onContinue,
|
onContinue,
|
||||||
createNotice,
|
createNotice,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { isLoadingScripts } = this.state;
|
const { isLoadingScripts, isRequestStarted } = this.state;
|
||||||
|
|
||||||
|
// We can't rely on isRequesting props only because option update might be triggered by other component.
|
||||||
|
if ( ! isRequestStarted ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const isRequestSuccessful =
|
const isRequestSuccessful =
|
||||||
! isRequesting &&
|
! isRequesting &&
|
||||||
! isLoadingScripts &&
|
! isLoadingScripts &&
|
||||||
|
@ -66,13 +59,17 @@ class UsageModal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTracking() {
|
updateTracking( { allowTracking } ) {
|
||||||
const { allowTracking } = this.state;
|
|
||||||
const { updateOptions } = this.props;
|
const { updateOptions } = this.props;
|
||||||
|
|
||||||
if ( allowTracking && typeof window.wcTracks.enable === 'function' ) {
|
if ( allowTracking && typeof window.wcTracks.enable === 'function' ) {
|
||||||
this.setState( { isLoadingScripts: true } );
|
this.setState( { isLoadingScripts: true } );
|
||||||
window.wcTracks.enable( () => {
|
window.wcTracks.enable( () => {
|
||||||
|
// Don't update state if component is unmounted already
|
||||||
|
if ( ! this._isMounted ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState( { isLoadingScripts: false } );
|
this.setState( { isLoadingScripts: false } );
|
||||||
} );
|
} );
|
||||||
} else if ( ! allowTracking ) {
|
} else if ( ! allowTracking ) {
|
||||||
|
@ -80,11 +77,20 @@ class UsageModal extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackingValue = allowTracking ? 'yes' : 'no';
|
const trackingValue = allowTracking ? 'yes' : 'no';
|
||||||
|
this.setState( { isRequestStarted: true } );
|
||||||
updateOptions( {
|
updateOptions( {
|
||||||
woocommerce_allow_tracking: trackingValue,
|
woocommerce_allow_tracking: trackingValue,
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._isMounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._isMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// Bail if site has already opted in to tracking
|
// Bail if site has already opted in to tracking
|
||||||
if ( this.props.allowTracking ) {
|
if ( this.props.allowTracking ) {
|
||||||
|
@ -94,9 +100,10 @@ class UsageModal extends Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { allowTracking } = this.state;
|
const {
|
||||||
const { isRequesting } = this.props;
|
isRequesting,
|
||||||
const trackingMessage = interpolateComponents( {
|
title = __( 'Build a better WooCommerce', 'woocommerce-admin' ),
|
||||||
|
message = interpolateComponents( {
|
||||||
mixedString: __(
|
mixedString: __(
|
||||||
'Get improved features and faster fixes by sharing non-sensitive data via {{link}}usage tracking{{/link}} ' +
|
'Get improved features and faster fixes by sharing non-sensitive data via {{link}}usage tracking{{/link}} ' +
|
||||||
'that shows us how WooCommerce is used. No personal data is tracked or stored.',
|
'that shows us how WooCommerce is used. No personal data is tracked or stored.',
|
||||||
|
@ -111,48 +118,46 @@ class UsageModal extends Component {
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
} );
|
} ),
|
||||||
|
dismissActionText = __( 'No thanks', 'woocommerce-admin' ),
|
||||||
|
acceptActionText = __( 'Yes, count me in!', 'woocommerce-admin' ),
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { isRequestStarted } = this.state;
|
||||||
|
const isBusy = isRequestStarted && isRequesting;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={ __(
|
title={ title }
|
||||||
'Build a better WooCommerce',
|
isDismissible={ this.props.isDismissible }
|
||||||
'woocommerce-admin'
|
|
||||||
) }
|
|
||||||
onRequestClose={ () => this.props.onClose() }
|
onRequestClose={ () => this.props.onClose() }
|
||||||
className="woocommerce-profile-wizard__usage-modal"
|
className="woocommerce-usage-modal"
|
||||||
>
|
>
|
||||||
<div className="woocommerce-profile-wizard__usage-wrapper">
|
<div className="woocommerce-usage-modal__wrapper">
|
||||||
<div className="woocommerce-profile-wizard__usage-modal-message">
|
<div className="woocommerce-usage-modal__message">
|
||||||
{ trackingMessage }
|
{ message }
|
||||||
</div>
|
|
||||||
<div className="woocommerce-profile-wizard__tracking">
|
|
||||||
<CheckboxControl
|
|
||||||
className="woocommerce-profile-wizard__tracking-checkbox"
|
|
||||||
checked={ allowTracking }
|
|
||||||
label={ __(
|
|
||||||
'Yes, count me in!',
|
|
||||||
'woocommerce-admin'
|
|
||||||
) }
|
|
||||||
onChange={ this.onTrackingChange }
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormToggle
|
|
||||||
aria-hidden="true"
|
|
||||||
checked={ allowTracking }
|
|
||||||
onChange={ this.onTrackingChange }
|
|
||||||
onClick={ ( e ) => e.stopPropagation() }
|
|
||||||
tabIndex="-1"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="woocommerce-usage-modal__actions">
|
||||||
|
<Button
|
||||||
|
isSecondary
|
||||||
|
isBusy={ isBusy }
|
||||||
|
onClick={ () =>
|
||||||
|
this.updateTracking( { allowTracking: false } )
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ dismissActionText }
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
isPrimary
|
isPrimary
|
||||||
isBusy={ isRequesting }
|
isBusy={ isBusy }
|
||||||
onClick={ () => this.updateTracking() }
|
onClick={ () =>
|
||||||
|
this.updateTracking( { allowTracking: true } )
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{ __( 'Continue', 'woocommerce-admin' ) }
|
{ acceptActionText }
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,30 +474,6 @@
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
.components-modal__frame.woocommerce-profile-wizard__usage-modal {
|
|
||||||
width: 600px;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
.components-modal__header {
|
|
||||||
border-bottom: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-profile-wizard__usage-wrapper {
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $studio-gray-60;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.is-primary {
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-business-extensions {
|
.woocommerce-business-extensions {
|
||||||
margin-left: -$gap;
|
margin-left: -$gap;
|
||||||
margin-right: -$gap;
|
margin-right: -$gap;
|
||||||
|
|
|
@ -65,7 +65,7 @@ class Payments extends Component {
|
||||||
: 'stripe';
|
: 'stripe';
|
||||||
}
|
}
|
||||||
|
|
||||||
markConfigured( method ) {
|
markConfigured( method, queryParams = {} ) {
|
||||||
const { clearTaskStatusCache } = this.props;
|
const { clearTaskStatusCache } = this.props;
|
||||||
const { enabledMethods } = this.state;
|
const { enabledMethods } = this.state;
|
||||||
|
|
||||||
|
@ -82,7 +82,9 @@ class Payments extends Component {
|
||||||
payment_method: method,
|
payment_method: method,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
getHistory().push( getNewPath( { task: 'payments' }, '/', {} ) );
|
getHistory().push(
|
||||||
|
getNewPath( { ...queryParams, task: 'payments' }, '/', {} )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentMethod() {
|
getCurrentMethod() {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import PayPal from './paypal';
|
||||||
import Klarna from './klarna';
|
import Klarna from './klarna';
|
||||||
import PayFast from './payfast';
|
import PayFast from './payfast';
|
||||||
import EWay from './eway';
|
import EWay from './eway';
|
||||||
|
import WCPayUsageModal from './wcpay-usage-modal';
|
||||||
|
|
||||||
export function installActivateAndConnectWcpay(
|
export function installActivateAndConnectWcpay(
|
||||||
resolve,
|
resolve,
|
||||||
|
@ -147,6 +148,7 @@ export function getPaymentMethods( {
|
||||||
{ wcPayIsConnected && wcPaySettingsLink }
|
{ wcPayIsConnected && wcPaySettingsLink }
|
||||||
{ ! wcPayIsConnected && <p>{ tosPrompt }</p> }
|
{ ! wcPayIsConnected && <p>{ tosPrompt }</p> }
|
||||||
{ profileItems.setup_client && <p>{ wcPayDocPrompt }</p> }
|
{ profileItems.setup_client && <p>{ wcPayDocPrompt }</p> }
|
||||||
|
<WCPayUsageModal />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
),
|
),
|
||||||
before: <WCPayIcon />,
|
before: <WCPayIcon />,
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { useState } from '@wordpress/element';
|
||||||
|
import { getQuery, updateQueryString } from '@woocommerce/navigation';
|
||||||
|
import interpolateComponents from 'interpolate-components';
|
||||||
|
import { Link } from '@woocommerce/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import UsageModal from '../../../profile-wizard/steps/usage-modal';
|
||||||
|
|
||||||
|
const WCPayUsageModal = () => {
|
||||||
|
const query = getQuery();
|
||||||
|
const shouldDisplayModal = query[ 'wcpay-connection-success' ] === '1';
|
||||||
|
const [ isOpen, setIsOpen ] = useState( shouldDisplayModal );
|
||||||
|
|
||||||
|
if ( ! isOpen ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setIsOpen( false );
|
||||||
|
updateQueryString( { 'wcpay-connection-success': undefined } );
|
||||||
|
};
|
||||||
|
|
||||||
|
const title = __(
|
||||||
|
'Help us build a better WooCommerce Payments experience',
|
||||||
|
'woocommerce-admin'
|
||||||
|
);
|
||||||
|
const trackingMessage = interpolateComponents( {
|
||||||
|
mixedString: __(
|
||||||
|
'By agreeing to share non-sensitive {{link}}usage data{{/link}}, you’ll help us improve features and optimize the WooCommerce Payments experience. You can opt out at any time.',
|
||||||
|
'woocommerce-admin'
|
||||||
|
),
|
||||||
|
components: {
|
||||||
|
link: (
|
||||||
|
<Link
|
||||||
|
href="https://woocommerce.com/usage-tracking"
|
||||||
|
target="_blank"
|
||||||
|
type="external"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
} );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UsageModal
|
||||||
|
isDismissible={ false }
|
||||||
|
title={ title }
|
||||||
|
message={ trackingMessage }
|
||||||
|
acceptActionText={ __( 'I agree', 'woocommerce-admin' ) }
|
||||||
|
dismissActionText={ __( 'No thanks', 'woocommerce-admin' ) }
|
||||||
|
onContinue={ closeModal }
|
||||||
|
onClose={ closeModal }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WCPayUsageModal;
|
|
@ -19,7 +19,7 @@ class WCPay extends Component {
|
||||||
'woocommerce-admin'
|
'woocommerce-admin'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
markConfigured( 'wcpay' );
|
markConfigured( 'wcpay', { 'wcpay-connection-success': '1' } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,13 +73,13 @@ export async function completeStoreDetailsSection( storeDetails = {} ) {
|
||||||
text: 'Build a better WooCommerce',
|
text: 'Build a better WooCommerce',
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Query for "Continue" buttons
|
// Query for primary buttons: "Continue" and "Yes, count me in"
|
||||||
const continueButtons = await page.$$( 'button.is-primary' );
|
const primaryButtons = await page.$$( 'button.is-primary' );
|
||||||
expect( continueButtons ).toHaveLength( 2 );
|
expect( primaryButtons ).toHaveLength( 2 );
|
||||||
|
|
||||||
await Promise.all( [
|
await Promise.all( [
|
||||||
// Click on "Continue" button of the usage pop-up window to move to the next step
|
// Click on "No thanks" button of the usage pop-up window to move to the next step
|
||||||
continueButtons[ 1 ].click(),
|
await page.click( 'button.is-secondary', { text: 'No thanks' } ),
|
||||||
|
|
||||||
// Wait for "In which industry does the store operate?" section to load
|
// Wait for "In which industry does the store operate?" section to load
|
||||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||||
|
|
Loading…
Reference in New Issue