* Move tracking usage question to modal

* Fix PHPCS errors

* Adjust button alignment, update PHPCS version.

* Fix options check

* Handle PR feedback
This commit is contained in:
Justin Shreve 2019-10-10 10:05:13 -04:00 committed by GitHub
parent ae413cb03e
commit d8132942f3
7 changed files with 278 additions and 89 deletions

View File

@ -3,13 +3,12 @@
* External dependencies * External dependencies
*/ */
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { FormToggle } from '@wordpress/components'; import { Button } from 'newspack-components';
import { Button, CheckboxControl } from 'newspack-components';
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { withDispatch } from '@wordpress/data'; import { withDispatch } from '@wordpress/data';
import { filter } from 'lodash'; import { filter, get } from 'lodash';
/** /**
* WooCommerce depdencies * WooCommerce depdencies
@ -27,63 +26,56 @@ import SpeedIcon from './images/flash_on';
import MobileAppIcon from './images/phone_android'; import MobileAppIcon from './images/phone_android';
import PrintIcon from './images/print'; import PrintIcon from './images/print';
import withSelect from 'wc-api/with-select'; import withSelect from 'wc-api/with-select';
import UsageModal from '../usage-modal';
import { recordEvent } from 'lib/tracks'; import { recordEvent } from 'lib/tracks';
class Start extends Component { class Start extends Component {
constructor() { constructor( props ) {
super( ...arguments ); super( props );
this.state = { this.state = {
allowTracking: true, showUsageModal: false,
continueAction: '',
}; };
this.onTrackingChange = this.onTrackingChange.bind( this );
this.startWizard = this.startWizard.bind( this ); this.startWizard = this.startWizard.bind( this );
this.skipWizard = this.skipWizard.bind( this ); this.skipWizard = this.skipWizard.bind( this );
} }
componentDidMount() { componentDidMount() {
const { updateProfileItems } = this.props;
if ( if (
this.props.activePlugins.includes( 'jetpack' ) && this.props.activePlugins.includes( 'jetpack' ) &&
this.props.activePlugins.includes( 'woocommerce-services' ) this.props.activePlugins.includes( 'woocommerce-services' )
) { ) {
updateProfileItems( { wcs_jetpack: 'already-installed' } );
return updateQueryString( { step: 'store-details' } ); return updateQueryString( { step: 'store-details' } );
} }
} }
async updateTracking() {
const { updateSettings } = this.props;
const allowTracking = this.state.allowTracking ? 'yes' : 'no';
await updateSettings( { advanced: { woocommerce_allow_tracking: allowTracking } } );
}
async skipWizard() { async skipWizard() {
const { createNotice, isProfileItemsError, updateProfileItems, isSettingsError } = this.props; const { createNotice, isProfileItemsError, updateProfileItems } = this.props;
recordEvent( 'storeprofiler_welcome_clicked', { proceed_without_install: true } ); await updateProfileItems( { skipped: true, wcs_jetpack: 'skipped' } );
await updateProfileItems( { skipped: true } ); if ( isProfileItemsError ) {
await this.updateTracking();
if ( isProfileItemsError || isSettingsError ) {
createNotice( createNotice(
'error', 'error',
__( 'There was a problem updating your preferences.', 'woocommerce-admin' ) __( 'There was a problem updating your preferences.', 'woocommerce-admin' )
); );
} else {
recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } );
} }
} }
async startWizard() { async startWizard() {
const { updateOptions, createNotice, isSettingsError } = this.props; const { createNotice, isProfileItemsError, updateProfileItems, updateOptions } = this.props;
recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } );
await updateOptions( { await updateOptions( {
woocommerce_setup_jetpack_opted_in: true, woocommerce_setup_jetpack_opted_in: true,
} ); } );
await this.updateTracking(); await updateProfileItems( { wcs_jetpack: 'wizard' } );
if ( ! isSettingsError ) { if ( ! isProfileItemsError ) {
recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } );
this.props.goToNextStep(); this.props.goToNextStep();
} else { } else {
createNotice( createNotice(
@ -93,12 +85,6 @@ class Start extends Component {
} }
} }
onTrackingChange() {
this.setState( {
allowTracking: ! this.state.allowTracking,
} );
}
renderBenefit( benefit ) { renderBenefit( benefit ) {
const { description, icon, title } = benefit; const { description, icon, title } = benefit;
@ -186,26 +172,23 @@ class Start extends Component {
} }
render() { render() {
const { allowTracking } = this.state; const { showUsageModal, continueAction } = this.state;
const { activePlugins } = this.props; const { activePlugins } = this.props;
const pluginNames = activePlugins.includes( 'jetpack' ) const pluginNames = activePlugins.includes( 'jetpack' )
? __( 'WooCommerce Services', 'woocommerce-admin' ) ? __( 'WooCommerce Services', 'woocommerce-admin' )
: __( 'Jetpack & WooCommerce Services', 'woocommerce-admin' ); : __( 'Jetpack & WooCommerce Services', 'woocommerce-admin' );
const trackingLabel = interpolateComponents( {
mixedString: __(
'Help improve WooCommerce with {{link}}usage tracking{{/link}}',
'woocommerce-admin'
),
components: {
link: (
<Link href="https://woocommerce.com/usage-tracking" target="_blank" type="external" />
),
},
} );
return ( return (
<Fragment> <Fragment>
{ showUsageModal && (
<UsageModal
onContinue={ () =>
'wizard' === continueAction ? this.startWizard() : this.skipWizard()
}
onClose={ () => this.setState( { showUsageModal: false, continueAction: '' } ) }
/>
) }
<H className="woocommerce-profile-wizard__header-title"> <H className="woocommerce-profile-wizard__header-title">
{ __( 'Start setting up your WooCommerce store', 'woocommerce-admin' ) } { __( 'Start setting up your WooCommerce store', 'woocommerce-admin' ) }
</H> </H>
@ -229,23 +212,6 @@ class Start extends Component {
<Card> <Card>
{ this.renderBenefits() } { this.renderBenefits() }
<div className="woocommerce-profile-wizard__tracking">
<CheckboxControl
className="woocommerce-profile-wizard__tracking-checkbox"
checked={ allowTracking }
label={ __( trackingLabel, 'woocommerce-admin' ) }
onChange={ this.onTrackingChange }
/>
<FormToggle
aria-hidden="true"
checked={ allowTracking }
onChange={ this.onTrackingChange }
onClick={ e => e.stopPropagation() }
tabIndex="-1"
/>
</div>
<p className="woocommerce-profile-wizard__tos"> <p className="woocommerce-profile-wizard__tos">
{ interpolateComponents( { { interpolateComponents( {
mixedString: __( mixedString: __(
@ -268,7 +234,7 @@ class Start extends Component {
<Button <Button
isPrimary isPrimary
onClick={ this.startWizard } onClick={ () => this.setState( { showUsageModal: true, continueAction: 'wizard' } ) }
className="woocommerce-profile-wizard__continue" className="woocommerce-profile-wizard__continue"
> >
{ __( 'Get started', 'woocommerce-admin' ) } { __( 'Get started', 'woocommerce-admin' ) }
@ -276,7 +242,11 @@ class Start extends Component {
</Card> </Card>
<p> <p>
<Button isLink className="woocommerce-profile-wizard__skip" onClick={ this.skipWizard }> <Button
isLink
className="woocommerce-profile-wizard__skip"
onClick={ () => this.setState( { showUsageModal: true, continueAction: 'skip' } ) }
>
{ sprintf( __( 'Proceed without %s', 'woocommerce-admin' ), pluginNames ) } { sprintf( __( 'Proceed without %s', 'woocommerce-admin' ), pluginNames ) }
</Button> </Button>
</p> </p>
@ -287,37 +257,29 @@ class Start extends Component {
export default compose( export default compose(
withSelect( select => { withSelect( select => {
const { const { getProfileItemsError, getActivePlugins, getOptions } = select( 'wc-api' );
getProfileItemsError,
getSettings,
getSettingsError,
isGetSettingsRequesting,
getActivePlugins,
} = select( 'wc-api' );
const isSettingsError = Boolean( getSettingsError( 'advanced' ) );
const isSettingsRequesting = isGetSettingsRequesting( 'advanced' );
const isProfileItemsError = Boolean( getProfileItemsError() ); const isProfileItemsError = Boolean( getProfileItemsError() );
const options = getOptions( [ 'woocommerce_allow_tracking' ] );
const allowTracking = 'yes' === get( options, [ 'woocommerce_allow_tracking' ], false );
const activePlugins = getActivePlugins(); const activePlugins = getActivePlugins();
return { return {
getSettings,
isSettingsError,
isProfileItemsError, isProfileItemsError,
isSettingsRequesting,
activePlugins, activePlugins,
allowTracking,
}; };
} ), } ),
withDispatch( dispatch => { withDispatch( dispatch => {
const { updateProfileItems, updateOptions, updateSettings } = dispatch( 'wc-api' ); const { updateProfileItems, updateOptions } = dispatch( 'wc-api' );
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
return { return {
createNotice, createNotice,
updateProfileItems, updateProfileItems,
updateOptions, updateOptions,
updateSettings,
}; };
} ) } )
)( Start ); )( Start );

View File

@ -19,11 +19,16 @@ import {
StoreAddress, StoreAddress,
validateStoreAddress, validateStoreAddress,
} from '../../components/settings/general/store-address'; } from '../../components/settings/general/store-address';
import UsageModal from './usage-modal';
class StoreDetails extends Component { class StoreDetails extends Component {
constructor() { constructor() {
super( ...arguments ); super( ...arguments );
this.state = {
showUsageModal: false,
};
this.initialValues = { this.initialValues = {
addressLine1: '', addressLine1: '',
addressLine2: '', addressLine2: '',
@ -34,6 +39,18 @@ class StoreDetails extends Component {
}; };
this.onContinue = this.onContinue.bind( this ); this.onContinue = this.onContinue.bind( this );
this.onSubmit = this.onSubmit.bind( this );
}
onSubmit( values ) {
const { profileItems } = this.props;
if ( 'already-installed' === profileItems.wcs_jetpack ) {
this.setState( { showUsageModal: true } );
return;
}
this.onContinue( values );
} }
async onContinue( values ) { async onContinue( values ) {
@ -74,6 +91,7 @@ class StoreDetails extends Component {
} }
render() { render() {
const { showUsageModal } = this.state;
return ( return (
<Fragment> <Fragment>
<H className="woocommerce-profile-wizard__header-title"> <H className="woocommerce-profile-wizard__header-title">
@ -89,11 +107,17 @@ class StoreDetails extends Component {
<Card> <Card>
<Form <Form
initialValues={ this.initialValues } initialValues={ this.initialValues }
onSubmitCallback={ this.onContinue } onSubmitCallback={ this.onSubmit }
validate={ validateStoreAddress } validate={ validateStoreAddress }
> >
{ ( { getInputProps, handleSubmit, isValidForm } ) => ( { ( { getInputProps, handleSubmit, values, isValidForm } ) => (
<Fragment> <Fragment>
{ showUsageModal && (
<UsageModal
onContinue={ () => this.onContinue( values ) }
onClose={ () => this.setState( { showUsageModal: false } ) }
/>
) }
<StoreAddress getInputProps={ getInputProps } /> <StoreAddress getInputProps={ getInputProps } />
<CheckboxControl <CheckboxControl
label={ __( "I'm setting up a store for a client", 'woocommerce-admin' ) } label={ __( "I'm setting up a store for a client", 'woocommerce-admin' ) }
@ -114,16 +138,29 @@ class StoreDetails extends Component {
export default compose( export default compose(
withSelect( select => { withSelect( select => {
const { getSettings, getSettingsError, isGetSettingsRequesting, getProfileItemsError } = select( const {
'wc-api' getSettings,
); getSettingsError,
isGetSettingsRequesting,
getProfileItemsError,
getProfileItems,
} = select( 'wc-api' );
const profileItems = getProfileItems();
const settings = getSettings( 'general' ); const settings = getSettings( 'general' );
const isSettingsError = Boolean( getSettingsError( 'general' ) ); const isSettingsError = Boolean( getSettingsError( 'general' ) );
const isSettingsRequesting = isGetSettingsRequesting( 'general' ); const isSettingsRequesting = isGetSettingsRequesting( 'general' );
const isProfileItemsError = Boolean( getProfileItemsError() ); const isProfileItemsError = Boolean( getProfileItemsError() );
return { getSettings, isProfileItemsError, isSettingsError, isSettingsRequesting, settings }; return {
getSettings,
isProfileItemsError,
profileItems,
isSettingsError,
isSettingsRequesting,
settings,
};
} ), } ),
withDispatch( dispatch => { withDispatch( dispatch => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );

View File

@ -0,0 +1,143 @@
/** @format */
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Button, CheckboxControl } from 'newspack-components';
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data';
import { get } from 'lodash';
import interpolateComponents from 'interpolate-components';
import { FormToggle, Modal } from '@wordpress/components';
/**
* Internal depdencies
*/
import { Link } from '@woocommerce/components';
import withSelect from 'wc-api/with-select';
class UsageModal extends Component {
constructor( props ) {
super( props );
this.state = {
allowTracking: props.allowTracking,
};
this.onTrackingChange = this.onTrackingChange.bind( this );
}
onTrackingChange() {
this.setState( {
allowTracking: ! this.state.allowTracking,
} );
}
async componentDidUpdate( prevProps ) {
const { hasErrors, isRequesting, onClose, onContinue, createNotice } = this.props;
const isRequestSuccessful = ! isRequesting && prevProps.isRequesting && ! hasErrors;
const isRequestError = ! isRequesting && prevProps.isRequesting && hasErrors;
if ( isRequestSuccessful ) {
onClose();
onContinue();
}
if ( isRequestError ) {
createNotice(
'error',
__( 'There was a problem updating your preferences.', 'woocommerce-admin' )
);
onClose();
}
}
updateTracking() {
const { updateOptions } = this.props;
const allowTracking = this.state.allowTracking ? 'yes' : 'no';
updateOptions( {
woocommerce_allow_tracking: allowTracking,
} );
}
render() {
const { allowTracking } = this.state;
const { isRequesting } = this.props;
const trackingMessage = interpolateComponents( {
mixedString: __(
"Learn more about how usage tracking works, and how you'll be helping {{link}}here{{/link}}.",
'woocommerce-admin'
),
components: {
link: (
<Link href="https://woocommerce.com/usage-tracking" target="_blank" type="external" />
),
},
} );
return (
<Modal
title={ __( 'Help improve WooCommerce with usage tracking', 'woocommerce-admin' ) }
onRequestClose={ () => this.props.onClose() }
className="woocommerce-profile-wizard__usage-modal"
>
<div className="woocommerce-profile-wizard__usage-wrapper">
<div className="woocommerce-profile-wizard__usage-modal-message">{ trackingMessage }</div>
<div className="woocommerce-profile-wizard__tracking">
<CheckboxControl
className="woocommerce-profile-wizard__tracking-checkbox"
checked={ allowTracking }
label={ __(
'Enable usage tracking and help improve WooCommerce',
'woocommerce-admin'
) }
onChange={ this.onTrackingChange }
/>
<FormToggle
aria-hidden="true"
checked={ allowTracking }
onChange={ this.onTrackingChange }
onClick={ e => e.stopPropagation() }
tabIndex="-1"
/>
</div>
<Button
isPrimary
isDefault
isBusy={ isRequesting }
onClick={ () => this.updateTracking() }
>
{ __( 'Continue', 'woocommerce-admin' ) }
</Button>
</div>
</Modal>
);
}
}
export default compose(
withSelect( select => {
const { getOptions, getOptionsError, isUpdateOptionsRequesting } = select( 'wc-api' );
const options = getOptions( [ 'woocommerce_allow_tracking' ] );
const allowTracking = 'yes' === get( options, [ 'woocommerce_allow_tracking' ], false );
const isRequesting = Boolean( isUpdateOptionsRequesting( [ 'woocommerce_allow_tracking' ] ) );
const hasErrors = Boolean( getOptionsError( [ 'woocommerce_allow_tracking' ] ) );
return {
allowTracking,
isRequesting,
hasErrors,
};
} ),
withDispatch( dispatch => {
const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' );
return {
createNotice,
updateOptions,
};
} )
)( UsageModal );

View File

@ -12,6 +12,10 @@
font-weight: normal; font-weight: normal;
} }
.woocommerce-profile-wizard__continue {
line-height: 32px;
}
.woocommerce-card { .woocommerce-card {
margin-top: $gap; margin-top: $gap;
@ -341,3 +345,32 @@
margin: 0; margin: 0;
} }
} }
.components-modal__frame.woocommerce-profile-wizard__usage-modal {
min-width: 600px;
.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 {
min-width: 160px;
align-self: flex-end;
margin-bottom: 0;
}
.muriel-checkbox label.components-checkbox-control__label {
font-size: 13px;
}
}
}

View File

@ -216,6 +216,18 @@ class OnboardingProfile extends \WC_REST_Data_Controller {
'readonly' => true, 'readonly' => true,
'validate_callback' => 'rest_validate_request_arg', 'validate_callback' => 'rest_validate_request_arg',
), ),
'wcs_jetpack' => array(
'type' => 'string',
'description' => __( 'How the Jetpack/WooCommerce Services step was handled.', 'woocommerce-admin' ),
'context' => array( 'view' ),
'readonly' => true,
'validate_callback' => 'rest_validate_request_arg',
'enum' => array(
'skipped',
'already-installed',
'wizard',
),
),
'account_type' => array( 'account_type' => array(
'type' => 'string', 'type' => 'string',
'description' => __( 'Account type used for Jetpack.', 'woocommerce-admin' ), 'description' => __( 'Account type used for Jetpack.', 'woocommerce-admin' ),

View File

@ -57,10 +57,10 @@ class Onboarding {
if ( $this->should_show_tasks() ) { if ( $this->should_show_tasks() ) {
OnboardingTasks::get_instance(); OnboardingTasks::get_instance();
} }
// old settings injection. // Old settings injection.
// Run after Automattic\WooCommerce\Admin\Loader. // Run after Automattic\WooCommerce\Admin\Loader.
add_filter( 'woocommerce_components_settings', array( $this, 'component_settings' ), 20 ); add_filter( 'woocommerce_components_settings', array( $this, 'component_settings' ), 20 );
// new settings injection. // New settings injection.
add_filter( 'woocommerce_shared_settings', array( $this, 'component_settings' ), 20 ); add_filter( 'woocommerce_shared_settings', array( $this, 'component_settings' ), 20 );
add_filter( 'woocommerce_component_settings_preload_endpoints', array( $this, 'add_preload_endpoints' ) ); add_filter( 'woocommerce_component_settings_preload_endpoints', array( $this, 'add_preload_endpoints' ) );
add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) ); add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) );
@ -352,10 +352,11 @@ class Onboarding {
* @return array * @return array
*/ */
public function preload_options( $options ) { public function preload_options( $options ) {
if ( ! $this->should_show_tasks() ) { if ( ! $this->should_show_tasks() && ! $this->should_show_profiler() ) {
return $options; return $options;
} }
$options[] = 'woocommerce_onboarding_payments'; $options[] = 'woocommerce_onboarding_payments';
$options[] = 'woocommerce_allow_tracking';
$options[] = 'woocommerce_stripe_settings'; $options[] = 'woocommerce_stripe_settings';
return $options; return $options;
} }

View File

@ -99,7 +99,7 @@ class WC_Tests_API_Onboarding_Profiles extends WC_REST_Unit_Test_Case {
$data = $response->get_data(); $data = $response->get_data();
$properties = $data['schema']['properties']; $properties = $data['schema']['properties'];
$this->assertCount( 14, $properties ); $this->assertCount( 15, $properties );
$this->assertArrayHasKey( 'completed', $properties ); $this->assertArrayHasKey( 'completed', $properties );
$this->assertArrayHasKey( 'skipped', $properties ); $this->assertArrayHasKey( 'skipped', $properties );
$this->assertArrayHasKey( 'account_type', $properties ); $this->assertArrayHasKey( 'account_type', $properties );
@ -113,6 +113,7 @@ class WC_Tests_API_Onboarding_Profiles extends WC_REST_Unit_Test_Case {
$this->assertArrayHasKey( 'theme', $properties ); $this->assertArrayHasKey( 'theme', $properties );
$this->assertArrayHasKey( 'wccom_connected', $properties ); $this->assertArrayHasKey( 'wccom_connected', $properties );
$this->assertArrayHasKey( 'items_purchased', $properties ); $this->assertArrayHasKey( 'items_purchased', $properties );
$this->assertArrayHasKey( 'wcs_jetpack', $properties );
$this->assertArrayHasKey( 'setup_client', $properties ); $this->assertArrayHasKey( 'setup_client', $properties );
} }