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:
Vasily Belolapotkov 2020-11-26 03:27:37 +03:00 committed by GitHub
parent cc5e500085
commit 5dab25382f
9 changed files with 232 additions and 101 deletions

View File

@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
import { get, isFunction, identity } from 'lodash';
import { parse } from 'qs';
import { Spinner } from '@woocommerce/components';
import { getHistory } from '@woocommerce/navigation';
import { getHistory, getQuery } from '@woocommerce/navigation';
import { getSetting } from '@woocommerce/wc-admin-settings';
import {
PLUGINS_STORE_NAME,
@ -32,6 +32,12 @@ const StoreAlerts = lazy( () =>
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 {
render() {
const { children } = this.props;
@ -121,6 +127,15 @@ class _Layout extends Component {
return parse( search );
}
isWCPaySettingsPage() {
const { page, section, tab } = getQuery();
return (
page === 'wc-settings' &&
tab === 'checkout' &&
section === 'woocommerce_payments'
);
}
render() {
const { isEmbedded, ...restProps } = this.props;
const { location, page } = this.props;
@ -146,6 +161,12 @@ class _Layout extends Component {
</div>
</PrimaryLayout>
) }
{ isEmbedded && this.isWCPaySettingsPage() && (
<Suspense fallback={ null }>
<WCPayUsageModal />
</Suspense>
) }
</div>
);
}

View File

@ -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 {
width: 600px;
max-width: 100%;

View File

@ -6,12 +6,7 @@ import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import interpolateComponents from 'interpolate-components';
import {
Button,
CheckboxControl,
FormToggle,
Modal,
} from '@wordpress/components';
import { Button, Modal } from '@wordpress/components';
import { Link } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
@ -19,17 +14,9 @@ class UsageModal extends Component {
constructor( props ) {
super( props );
this.state = {
allowTracking: props.allowTracking,
isLoadingScripts: false,
isRequestStarted: false,
};
this.onTrackingChange = this.onTrackingChange.bind( this );
}
onTrackingChange() {
this.setState( {
allowTracking: ! this.state.allowTracking,
} );
}
async componentDidUpdate( prevProps, prevState ) {
@ -40,7 +27,13 @@ class UsageModal extends Component {
onContinue,
createNotice,
} = 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 =
! isRequesting &&
! isLoadingScripts &&
@ -66,13 +59,17 @@ class UsageModal extends Component {
}
}
updateTracking() {
const { allowTracking } = this.state;
updateTracking( { allowTracking } ) {
const { updateOptions } = this.props;
if ( allowTracking && typeof window.wcTracks.enable === 'function' ) {
this.setState( { isLoadingScripts: true } );
window.wcTracks.enable( () => {
// Don't update state if component is unmounted already
if ( ! this._isMounted ) {
return;
}
this.setState( { isLoadingScripts: false } );
} );
} else if ( ! allowTracking ) {
@ -80,11 +77,20 @@ class UsageModal extends Component {
}
const trackingValue = allowTracking ? 'yes' : 'no';
this.setState( { isRequestStarted: true } );
updateOptions( {
woocommerce_allow_tracking: trackingValue,
} );
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
// Bail if site has already opted in to tracking
if ( this.props.allowTracking ) {
@ -94,64 +100,63 @@ class UsageModal extends Component {
return null;
}
const { allowTracking } = this.state;
const { isRequesting } = this.props;
const trackingMessage = interpolateComponents( {
mixedString: __(
'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.',
'woocommerce-admin'
),
components: {
link: (
<Link
href="https://woocommerce.com/usage-tracking"
target="_blank"
type="external"
/>
const {
isRequesting,
title = __( 'Build a better WooCommerce', 'woocommerce-admin' ),
message = interpolateComponents( {
mixedString: __(
'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.',
'woocommerce-admin'
),
},
} );
components: {
link: (
<Link
href="https://woocommerce.com/usage-tracking"
target="_blank"
type="external"
/>
),
},
} ),
dismissActionText = __( 'No thanks', 'woocommerce-admin' ),
acceptActionText = __( 'Yes, count me in!', 'woocommerce-admin' ),
} = this.props;
const { isRequestStarted } = this.state;
const isBusy = isRequestStarted && isRequesting;
return (
<Modal
title={ __(
'Build a better WooCommerce',
'woocommerce-admin'
) }
title={ title }
isDismissible={ this.props.isDismissible }
onRequestClose={ () => this.props.onClose() }
className="woocommerce-profile-wizard__usage-modal"
className="woocommerce-usage-modal"
>
<div className="woocommerce-profile-wizard__usage-wrapper">
<div className="woocommerce-profile-wizard__usage-modal-message">
{ trackingMessage }
<div className="woocommerce-usage-modal__wrapper">
<div className="woocommerce-usage-modal__message">
{ 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 className="woocommerce-usage-modal__actions">
<Button
isSecondary
isBusy={ isBusy }
onClick={ () =>
this.updateTracking( { allowTracking: false } )
}
>
{ dismissActionText }
</Button>
<Button
isPrimary
isBusy={ isBusy }
onClick={ () =>
this.updateTracking( { allowTracking: true } )
}
>
{ acceptActionText }
</Button>
</div>
<Button
isPrimary
isBusy={ isRequesting }
onClick={ () => this.updateTracking() }
>
{ __( 'Continue', 'woocommerce-admin' ) }
</Button>
</div>
</Modal>
);

View File

@ -474,30 +474,6 @@
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 {
margin-left: -$gap;
margin-right: -$gap;

View File

@ -65,7 +65,7 @@ class Payments extends Component {
: 'stripe';
}
markConfigured( method ) {
markConfigured( method, queryParams = {} ) {
const { clearTaskStatusCache } = this.props;
const { enabledMethods } = this.state;
@ -82,7 +82,9 @@ class Payments extends Component {
payment_method: method,
} );
getHistory().push( getNewPath( { task: 'payments' }, '/', {} ) );
getHistory().push(
getNewPath( { ...queryParams, task: 'payments' }, '/', {} )
);
}
getCurrentMethod() {

View File

@ -28,6 +28,7 @@ import PayPal from './paypal';
import Klarna from './klarna';
import PayFast from './payfast';
import EWay from './eway';
import WCPayUsageModal from './wcpay-usage-modal';
export function installActivateAndConnectWcpay(
resolve,
@ -147,6 +148,7 @@ export function getPaymentMethods( {
{ wcPayIsConnected && wcPaySettingsLink }
{ ! wcPayIsConnected && <p>{ tosPrompt }</p> }
{ profileItems.setup_client && <p>{ wcPayDocPrompt }</p> }
<WCPayUsageModal />
</Fragment>
),
before: <WCPayIcon />,

View File

@ -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}}, youll 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;

View File

@ -19,7 +19,7 @@ class WCPay extends Component {
'woocommerce-admin'
)
);
markConfigured( 'wcpay' );
markConfigured( 'wcpay', { 'wcpay-connection-success': '1' } );
}
}

View File

@ -73,13 +73,13 @@ export async function completeStoreDetailsSection( storeDetails = {} ) {
text: 'Build a better WooCommerce',
} );
// Query for "Continue" buttons
const continueButtons = await page.$$( 'button.is-primary' );
expect( continueButtons ).toHaveLength( 2 );
// Query for primary buttons: "Continue" and "Yes, count me in"
const primaryButtons = await page.$$( 'button.is-primary' );
expect( primaryButtons ).toHaveLength( 2 );
await Promise.all( [
// Click on "Continue" button of the usage pop-up window to move to the next step
continueButtons[ 1 ].click(),
// Click on "No thanks" button of the usage pop-up window to move to the next step
await page.click( 'button.is-secondary', { text: 'No thanks' } ),
// Wait for "In which industry does the store operate?" section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),