Update product editor CES modal to match new designs (#38592)

* Add new fields and copy update to product CES modal

* Update styling for product CES modal

* Record email in tracks callback if provided

* Fix modal header spacing

* Fix record callback types

* Add optional tag styling

* Allow submitting form when a reason has been selected

* Fix modal padding bottom

* Update modal width to match designs

* Add changelog entries

* Update margin for legend

* Add props to doc comment

* Make className prop optional

* Update tests for recording score with email
This commit is contained in:
Joshua T Flowers 2023-06-12 10:58:00 -07:00 committed by GitHub
parent 0fc765beb3
commit 90e4ddbf29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 73 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add props to allow passing a classname to the feedback modal

View File

@ -5,6 +5,7 @@ import { createElement, useState } from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button, Modal } from '@wordpress/components'; import { Button, Modal } from '@wordpress/components';
import { Text } from '@woocommerce/experimental'; import { Text } from '@woocommerce/experimental';
import classnames from 'classnames';
/** /**
* Provides a modal requesting customer feedback. * Provides a modal requesting customer feedback.
@ -20,6 +21,7 @@ import { Text } from '@woocommerce/experimental';
* @param {string} props.cancelButtonLabel Label for the cancel button. * @param {string} props.cancelButtonLabel Label for the cancel button.
* @param {Function} props.onModalClose Callback for when user closes modal by clicking cancel. * @param {Function} props.onModalClose Callback for when user closes modal by clicking cancel.
* @param {Function} props.children Children to be rendered. * @param {Function} props.children Children to be rendered.
* @param {string} props.className Class name to addd to the modal.
*/ */
function FeedbackModal( { function FeedbackModal( {
onSubmit, onSubmit,
@ -30,6 +32,7 @@ function FeedbackModal( {
isSubmitButtonDisabled, isSubmitButtonDisabled,
submitButtonLabel, submitButtonLabel,
cancelButtonLabel, cancelButtonLabel,
className,
}: { }: {
onSubmit: () => void; onSubmit: () => void;
title: string; title: string;
@ -39,6 +42,7 @@ function FeedbackModal( {
isSubmitButtonDisabled?: boolean; isSubmitButtonDisabled?: boolean;
submitButtonLabel?: string; submitButtonLabel?: string;
cancelButtonLabel?: string; cancelButtonLabel?: string;
className?: string;
} ): JSX.Element | null { } ): JSX.Element | null {
const [ isOpen, setOpen ] = useState( true ); const [ isOpen, setOpen ] = useState( true );
@ -55,21 +59,23 @@ function FeedbackModal( {
return ( return (
<Modal <Modal
className="woocommerce-feedback-modal" className={ classnames( 'woocommerce-feedback-modal', className ) }
title={ title } title={ title }
onRequestClose={ closeModal } onRequestClose={ closeModal }
shouldCloseOnClickOutside={ false } shouldCloseOnClickOutside={ false }
> >
<Text { description && (
variant="body" <Text
as="p" variant="body"
className="woocommerce-feedback-modal__description" as="p"
size={ 14 } className="woocommerce-feedback-modal__description"
lineHeight="20px" size={ 14 }
marginBottom="1.5em" lineHeight="20px"
> marginBottom="1.5em"
{ description } >
</Text> { description }
</Text>
) }
{ children } { children }
<div className="woocommerce-feedback-modal__buttons"> <div className="woocommerce-feedback-modal__buttons">
<Button isTertiary onClick={ closeModal } name="cancel"> <Button isTertiary onClick={ closeModal } name="cancel">

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Update product CES modal design and fields

View File

@ -41,11 +41,16 @@ export const ProductMVPFeedbackModalContainer: React.FC< {
`post-new.php?post_type=product&product_block_editor=0&_feature_nonce=${ _feature_nonce }` `post-new.php?post_type=product&product_block_editor=0&_feature_nonce=${ _feature_nonce }`
); );
const recordScore = ( checked: string[], comments: string ) => { const recordScore = (
checked: string[],
comments: string,
email: string
) => {
recordEvent( 'product_mvp_feedback', { recordEvent( 'product_mvp_feedback', {
action: 'disable', action: 'disable',
checked, checked,
comments: comments || '', comments: comments || '',
email,
} ); } );
hideProductMVPFeedbackModal(); hideProductMVPFeedbackModal();
window.location.href = `${ classicEditorUrl }&new-product-experience-disabled=true`; window.location.href = `${ classicEditorUrl }&new-product-experience-disabled=true`;

View File

@ -1,9 +1,18 @@
/** /**
* External dependencies * External dependencies
*/ */
import { createElement, Fragment, useState } from '@wordpress/element'; import {
createElement,
createInterpolateElement,
Fragment,
useState,
} from '@wordpress/element';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CheckboxControl, TextareaControl } from '@wordpress/components'; import {
CheckboxControl,
TextareaControl,
TextControl,
} from '@wordpress/components';
import { FeedbackModal } from '@woocommerce/customer-effort-score'; import { FeedbackModal } from '@woocommerce/customer-effort-score';
import { Text } from '@woocommerce/experimental'; import { Text } from '@woocommerce/experimental';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
@ -20,7 +29,11 @@ function ProductMVPFeedbackModal( {
recordScoreCallback, recordScoreCallback,
onCloseModal, onCloseModal,
}: { }: {
recordScoreCallback: ( checked: string[], comments: string ) => void; recordScoreCallback: (
checked: string[],
comments: string,
email: string
) => void;
onCloseModal?: () => void; onCloseModal?: () => void;
} ): JSX.Element | null { } ): JSX.Element | null {
const [ missingFeatures, setMissingFeatures ] = useState( false ); const [ missingFeatures, setMissingFeatures ] = useState( false );
@ -61,37 +74,33 @@ function ProductMVPFeedbackModal( {
}, },
]; ];
const [ comments, setComments ] = useState( '' ); const [ comments, setComments ] = useState( '' );
const [ email, setEmail ] = useState( '' );
const checked = checkboxes
.filter( ( checkbox ) => checkbox.checked )
.map( ( checkbox ) => checkbox.key );
const onSendFeedback = () => { const onSendFeedback = () => {
const checked = checkboxes recordScoreCallback( checked, comments, email );
.filter( ( checkbox ) => checkbox.checked )
.map( ( checkbox ) => checkbox.key );
recordScoreCallback( checked, comments );
}; };
const isSendButtonDisabled = const optionalElement = (
! comments && <span className="woocommerce-product-mvp-feedback-modal__optional">
! missingFeatures && { __( '(optional)', 'woocommerce' ) }
! missingPlugins && </span>
! difficultToUse && );
! slowBuggyOrBroken &&
! other;
return ( return (
<FeedbackModal <FeedbackModal
title={ __( title={ __(
'Thanks for trying out the new product editor!', 'Thanks for trying out the new product form!',
'woocommerce'
) }
description={ __(
'Were working on making it better, and your feedback will help improve the experience for thousands of merchants like you.',
'woocommerce' 'woocommerce'
) } ) }
onSubmit={ onSendFeedback } onSubmit={ onSendFeedback }
onModalClose={ onCloseModal } onModalClose={ onCloseModal }
isSubmitButtonDisabled={ isSendButtonDisabled } isSubmitButtonDisabled={ ! checked.length }
submitButtonLabel={ __( 'Send feedback', 'woocommerce' ) } submitButtonLabel={ __( 'Send feedback', 'woocommerce' ) }
cancelButtonLabel={ __( 'Skip', 'woocommerce' ) } cancelButtonLabel={ __( 'Skip', 'woocommerce' ) }
className="woocommerce-product-mvp-feedback-modal"
> >
<> <>
<Text <Text
@ -100,45 +109,63 @@ function ProductMVPFeedbackModal( {
weight="600" weight="600"
size="14" size="14"
lineHeight="20px" lineHeight="20px"
> ></Text>
{ __( <fieldset className="woocommerce-product-mvp-feedback-modal__reason">
'What made you switch back to the classic product editor?', <legend>
'woocommerce' { __(
) } 'What made you turn off the new product form?',
</Text>
<Text
weight="400"
size="12"
as="p"
lineHeight="16px"
color="#757575"
className="woocommerce-product-mvp-feedback-modal__subtitle"
>
{ __( '(Check all that apply)', 'woocommerce' ) }
</Text>
<div className="woocommerce-product-mvp-feedback-modal__checkboxes">
{ checkboxes.map( ( checkbox, index ) => (
<CheckboxControl
key={ index }
label={ checkbox.label }
name={ checkbox.key }
checked={ checkbox.checked }
onChange={ checkbox.onChange }
/>
) ) }
</div>
<div className="woocommerce-product-mvp-feedback-modal__comments">
<TextareaControl
label={ __( 'Additional comments', 'woocommerce' ) }
value={ comments }
placeholder={ __(
'Optional, but much apprecated. We love reading your feedback!',
'woocommerce' 'woocommerce'
) } ) }
</legend>
<div className="woocommerce-product-mvp-feedback-modal__checkboxes">
{ checkboxes.map( ( checkbox, index ) => (
<CheckboxControl
key={ index }
label={ checkbox.label }
name={ checkbox.key }
checked={ checkbox.checked }
onChange={ checkbox.onChange }
/>
) ) }
</div>
</fieldset>
<div className="woocommerce-product-mvp-feedback-modal__comments">
<TextareaControl
label={ createInterpolateElement(
__(
'Additional thoughts <optional/>',
'woocommerce'
),
{
optional: optionalElement,
}
) }
value={ comments }
onChange={ ( value: string ) => setComments( value ) } onChange={ ( value: string ) => setComments( value ) }
rows={ 5 } rows={ 5 }
/> />
</div> </div>
<div className="woocommerce-product-mvp-feedback-modal__email">
<TextControl
label={ createInterpolateElement(
__(
'Your email address <optional/>',
'woocommerce'
),
{
optional: optionalElement,
}
) }
value={ email }
onChange={ ( value: string ) => setEmail( value ) }
rows={ 5 }
help={ __(
'In case you want to participate in further discussion and future user research.',
'woocommerce'
) }
/>
</div>
</> </>
</FeedbackModal> </FeedbackModal>
); );

View File

@ -1,23 +1,62 @@
$modal-header-height: 84px;
.woocommerce-product-mvp-feedback-modal { .woocommerce-product-mvp-feedback-modal {
width: 600px;
.components-modal__header {
height: $modal-header-height;
&-heading {
font-weight: 500;
font-size: 20px;
}
}
.components-modal__content {
margin-top: $modal-header-height;
padding-bottom: 32px;
}
legend,
label {
color: $gray-900;
}
.woocommerce-product-mvp-feedback-modal__optional {
color: $gray-700;
}
legend {
font-size: 11px;
font-weight: 500;
line-height: 1.4;
text-transform: uppercase;
display: inline-block;
margin-bottom: $gap-small;
padding: 0;
}
&__subtitle { &__subtitle {
margin-top: $gap-smaller !important; margin-top: $gap-smaller !important;
} }
&__checkboxes { &__checkboxes {
margin: $gap-small 0; display: grid;
grid-template-columns: 1fr 1fr;
} }
&__comments { &__comments {
margin-top: 2em; margin-top: 2em;
margin-bottom: 1.5em; margin-bottom: 1.5em;
label {
display: block;
font-weight: bold;
text-transform: none;
font-size: 14px;
}
textarea { textarea {
width: 100%; width: 100%;
} }
} }
&__reason,
&__comments,
&__email {
margin-bottom: $gap-large;
}
} }

View File

@ -48,6 +48,7 @@ describe( 'ProductMVPFeedbackModal', () => {
fireEvent.click( screen.getByRole( 'checkbox', { name: /other/i } ) ); fireEvent.click( screen.getByRole( 'checkbox', { name: /other/i } ) );
expect( mockRecordScoreCallback ).toHaveBeenCalledWith( expect( mockRecordScoreCallback ).toHaveBeenCalledWith(
[ 'other' ], [ 'other' ],
'',
'' ''
); );
} ); } );