From d8132942f33de66d34f3230a9d81b79c62374b4e Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Thu, 10 Oct 2019 10:05:13 -0400 Subject: [PATCH] Change usage tracking prompt to modal (https://github.com/woocommerce/woocommerce-admin/pull/2968) * Move tracking usage question to modal * Fix PHPCS errors * Adjust button alignment, update PHPCS version. * Fix options check * Handle PR feedback --- .../profile-wizard/steps/start/index.js | 118 +++++---------- .../profile-wizard/steps/store-details.js | 49 +++++- .../profile-wizard/steps/usage-modal.js | 143 ++++++++++++++++++ .../dashboard/profile-wizard/style.scss | 33 ++++ .../src/API/OnboardingProfile.php | 14 +- .../src/Features/Onboarding.php | 7 +- .../tests/api/onboarding-profile.php | 3 +- 7 files changed, 278 insertions(+), 89 deletions(-) create mode 100644 plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/usage-modal.js diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/start/index.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/start/index.js index 5f85fb00581..c96ac540e47 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/start/index.js +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/start/index.js @@ -3,13 +3,12 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { FormToggle } from '@wordpress/components'; -import { Button, CheckboxControl } from 'newspack-components'; +import { Button } from 'newspack-components'; import { Component, Fragment } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import interpolateComponents from 'interpolate-components'; import { withDispatch } from '@wordpress/data'; -import { filter } from 'lodash'; +import { filter, get } from 'lodash'; /** * WooCommerce depdencies @@ -27,63 +26,56 @@ import SpeedIcon from './images/flash_on'; import MobileAppIcon from './images/phone_android'; import PrintIcon from './images/print'; import withSelect from 'wc-api/with-select'; +import UsageModal from '../usage-modal'; import { recordEvent } from 'lib/tracks'; class Start extends Component { - constructor() { - super( ...arguments ); - + constructor( props ) { + super( props ); this.state = { - allowTracking: true, + showUsageModal: false, + continueAction: '', }; - - this.onTrackingChange = this.onTrackingChange.bind( this ); this.startWizard = this.startWizard.bind( this ); this.skipWizard = this.skipWizard.bind( this ); } componentDidMount() { + const { updateProfileItems } = this.props; if ( this.props.activePlugins.includes( 'jetpack' ) && this.props.activePlugins.includes( 'woocommerce-services' ) ) { + updateProfileItems( { wcs_jetpack: 'already-installed' } ); 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() { - 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 } ); - await this.updateTracking(); - - if ( isProfileItemsError || isSettingsError ) { + if ( isProfileItemsError ) { createNotice( 'error', __( 'There was a problem updating your preferences.', 'woocommerce-admin' ) ); + } else { + recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } ); } } async startWizard() { - const { updateOptions, createNotice, isSettingsError } = this.props; - - recordEvent( 'storeprofiler_welcome_clicked', { get_started: true } ); + const { createNotice, isProfileItemsError, updateProfileItems, updateOptions } = this.props; await updateOptions( { 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(); } else { createNotice( @@ -93,12 +85,6 @@ class Start extends Component { } } - onTrackingChange() { - this.setState( { - allowTracking: ! this.state.allowTracking, - } ); - } - renderBenefit( benefit ) { const { description, icon, title } = benefit; @@ -186,26 +172,23 @@ class Start extends Component { } render() { - const { allowTracking } = this.state; + const { showUsageModal, continueAction } = this.state; const { activePlugins } = this.props; + const pluginNames = activePlugins.includes( '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: ( - - ), - }, - } ); - return ( + { showUsageModal && ( + + 'wizard' === continueAction ? this.startWizard() : this.skipWizard() + } + onClose={ () => this.setState( { showUsageModal: false, continueAction: '' } ) } + /> + ) } { __( 'Start setting up your WooCommerce store', 'woocommerce-admin' ) } @@ -229,23 +212,6 @@ class Start extends Component { { this.renderBenefits() } -
- - -
-

{ interpolateComponents( { mixedString: __( @@ -268,7 +234,7 @@ class Start extends Component {

@@ -287,37 +257,29 @@ class Start extends Component { export default compose( withSelect( select => { - const { - getProfileItemsError, - getSettings, - getSettingsError, - isGetSettingsRequesting, - getActivePlugins, - } = select( 'wc-api' ); + const { getProfileItemsError, getActivePlugins, getOptions } = select( 'wc-api' ); - const isSettingsError = Boolean( getSettingsError( 'advanced' ) ); - const isSettingsRequesting = isGetSettingsRequesting( 'advanced' ); const isProfileItemsError = Boolean( getProfileItemsError() ); + const options = getOptions( [ 'woocommerce_allow_tracking' ] ); + const allowTracking = 'yes' === get( options, [ 'woocommerce_allow_tracking' ], false ); + const activePlugins = getActivePlugins(); return { - getSettings, - isSettingsError, isProfileItemsError, - isSettingsRequesting, activePlugins, + allowTracking, }; } ), withDispatch( dispatch => { - const { updateProfileItems, updateOptions, updateSettings } = dispatch( 'wc-api' ); + const { updateProfileItems, updateOptions } = dispatch( 'wc-api' ); const { createNotice } = dispatch( 'core/notices' ); return { createNotice, updateProfileItems, updateOptions, - updateSettings, }; } ) )( Start ); diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/store-details.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/store-details.js index 12fe2ee4163..3dd06f24050 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/store-details.js +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/store-details.js @@ -19,11 +19,16 @@ import { StoreAddress, validateStoreAddress, } from '../../components/settings/general/store-address'; +import UsageModal from './usage-modal'; class StoreDetails extends Component { constructor() { super( ...arguments ); + this.state = { + showUsageModal: false, + }; + this.initialValues = { addressLine1: '', addressLine2: '', @@ -34,6 +39,18 @@ class StoreDetails extends Component { }; 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 ) { @@ -74,6 +91,7 @@ class StoreDetails extends Component { } render() { + const { showUsageModal } = this.state; return ( @@ -89,11 +107,17 @@ class StoreDetails extends Component {
- { ( { getInputProps, handleSubmit, isValidForm } ) => ( + { ( { getInputProps, handleSubmit, values, isValidForm } ) => ( + { showUsageModal && ( + this.onContinue( values ) } + onClose={ () => this.setState( { showUsageModal: false } ) } + /> + ) } { - const { getSettings, getSettingsError, isGetSettingsRequesting, getProfileItemsError } = select( - 'wc-api' - ); + const { + getSettings, + getSettingsError, + isGetSettingsRequesting, + getProfileItemsError, + getProfileItems, + } = select( 'wc-api' ); + + const profileItems = getProfileItems(); const settings = getSettings( 'general' ); const isSettingsError = Boolean( getSettingsError( 'general' ) ); const isSettingsRequesting = isGetSettingsRequesting( 'general' ); const isProfileItemsError = Boolean( getProfileItemsError() ); - return { getSettings, isProfileItemsError, isSettingsError, isSettingsRequesting, settings }; + return { + getSettings, + isProfileItemsError, + profileItems, + isSettingsError, + isSettingsRequesting, + settings, + }; } ), withDispatch( dispatch => { const { createNotice } = dispatch( 'core/notices' ); diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/usage-modal.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/usage-modal.js new file mode 100644 index 00000000000..b1269720b63 --- /dev/null +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/usage-modal.js @@ -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: ( + + ), + }, + } ); + + return ( + this.props.onClose() } + className="woocommerce-profile-wizard__usage-modal" + > +
+
{ trackingMessage }
+
+ + +
+ +
+
+ ); + } +} + +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 ); diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss b/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss index ff820d995a4..e4fb9318db5 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/style.scss @@ -12,6 +12,10 @@ font-weight: normal; } + .woocommerce-profile-wizard__continue { + line-height: 32px; + } + .woocommerce-card { margin-top: $gap; @@ -341,3 +345,32 @@ 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; + } + } +} diff --git a/plugins/woocommerce-admin/src/API/OnboardingProfile.php b/plugins/woocommerce-admin/src/API/OnboardingProfile.php index 9977e4d45c9..10bc2786c8c 100644 --- a/plugins/woocommerce-admin/src/API/OnboardingProfile.php +++ b/plugins/woocommerce-admin/src/API/OnboardingProfile.php @@ -216,6 +216,18 @@ class OnboardingProfile extends \WC_REST_Data_Controller { 'readonly' => true, '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( 'type' => 'string', 'description' => __( 'Account type used for Jetpack.', 'woocommerce-admin' ), @@ -278,7 +290,7 @@ class OnboardingProfile extends \WC_REST_Data_Controller { 'brick-mortar-other', ), ), - 'revenue' => array( + 'revenue' => array( 'type' => 'string', 'description' => __( 'Current annual revenue of the store.', 'woocommerce-admin' ), 'context' => array( 'view' ), diff --git a/plugins/woocommerce-admin/src/Features/Onboarding.php b/plugins/woocommerce-admin/src/Features/Onboarding.php index 60af08843b9..1591141f27e 100644 --- a/plugins/woocommerce-admin/src/Features/Onboarding.php +++ b/plugins/woocommerce-admin/src/Features/Onboarding.php @@ -57,10 +57,10 @@ class Onboarding { if ( $this->should_show_tasks() ) { OnboardingTasks::get_instance(); } - // old settings injection. + // Old settings injection. // Run after Automattic\WooCommerce\Admin\Loader. 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_component_settings_preload_endpoints', array( $this, 'add_preload_endpoints' ) ); add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) ); @@ -352,10 +352,11 @@ class Onboarding { * @return array */ public function preload_options( $options ) { - if ( ! $this->should_show_tasks() ) { + if ( ! $this->should_show_tasks() && ! $this->should_show_profiler() ) { return $options; } $options[] = 'woocommerce_onboarding_payments'; + $options[] = 'woocommerce_allow_tracking'; $options[] = 'woocommerce_stripe_settings'; return $options; } diff --git a/plugins/woocommerce-admin/tests/api/onboarding-profile.php b/plugins/woocommerce-admin/tests/api/onboarding-profile.php index 1baf66c889e..2621589afb0 100644 --- a/plugins/woocommerce-admin/tests/api/onboarding-profile.php +++ b/plugins/woocommerce-admin/tests/api/onboarding-profile.php @@ -99,7 +99,7 @@ class WC_Tests_API_Onboarding_Profiles extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertCount( 14, $properties ); + $this->assertCount( 15, $properties ); $this->assertArrayHasKey( 'completed', $properties ); $this->assertArrayHasKey( 'skipped', $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( 'wccom_connected', $properties ); $this->assertArrayHasKey( 'items_purchased', $properties ); + $this->assertArrayHasKey( 'wcs_jetpack', $properties ); $this->assertArrayHasKey( 'setup_client', $properties ); }