Add monthly pricing toggle to product types (https://github.com/woocommerce/woocommerce-admin/pull/5015)
* Add monthly pricing toggle * Move product type label to its own folder * Add popover to product type label * Add card help text * Add product type component tests * Add tests for product types step * Refactor validation in product types * Export ProductTypes component for testing
This commit is contained in:
parent
dbec08f7dc
commit
94db7f7f76
|
@ -1,18 +0,0 @@
|
|||
.woocommerce-profile-wizard__product-types {
|
||||
.components-base-control__field {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.woocommerce-product-wizard__product-types__label {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__checkbox {
|
||||
border-bottom: 1px solid $gray-100;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__card-actions .components-button.is-primary {
|
||||
margin-top: $gap;
|
||||
}
|
||||
}
|
|
@ -1,60 +1,24 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Component, Fragment } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Component } from '@wordpress/element';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button, CheckboxControl, Tooltip } from '@wordpress/components';
|
||||
import { Button, CheckboxControl, FormToggle } from '@wordpress/components';
|
||||
import { includes, filter, get } from 'lodash';
|
||||
import interpolateComponents from 'interpolate-components';
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { getSetting } from '@woocommerce/wc-admin-settings';
|
||||
import { H, Card, Link, Pill } from '@woocommerce/components';
|
||||
import { H, Card } from '@woocommerce/components';
|
||||
import { ONBOARDING_STORE_NAME } from '@woocommerce/data';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './product-types.scss';
|
||||
import ProductType from './product-type';
|
||||
import './style.scss';
|
||||
|
||||
function getLabel( description, yearlyPrice ) {
|
||||
if ( ! yearlyPrice ) {
|
||||
return description;
|
||||
}
|
||||
|
||||
const monthlyPrice = ( yearlyPrice / 12.0 ).toFixed( 2 );
|
||||
const priceDescription = sprintf(
|
||||
__( '$%f per month, billed annually', 'woocommerce-admin' ),
|
||||
monthlyPrice
|
||||
);
|
||||
/* eslint-disable @wordpress/i18n-no-collapsible-whitespace */
|
||||
const toolTipText = __(
|
||||
"This product type requires a paid extension.\nWe'll add this to a cart so that\nyou can purchase and install it later.",
|
||||
'woocommerce-admin'
|
||||
);
|
||||
/* eslint-enable @wordpress/i18n-no-collapsible-whitespace */
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<span className="woocommerce-product-wizard__product-types__label">
|
||||
{ description }
|
||||
</span>
|
||||
<Tooltip text={ toolTipText } position="bottom center">
|
||||
<span>
|
||||
<Pill>
|
||||
<span className="screen-reader-text">
|
||||
{ toolTipText }
|
||||
</span>
|
||||
{ priceDescription }
|
||||
</Pill>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
class ProductTypes extends Component {
|
||||
export class ProductTypes extends Component {
|
||||
constructor( props ) {
|
||||
super();
|
||||
const profileItems = get( props, 'profileItems', {} );
|
||||
|
@ -66,6 +30,7 @@ class ProductTypes extends Component {
|
|||
|
||||
this.state = {
|
||||
error: null,
|
||||
isMonthlyPricing: true,
|
||||
selected: profileItems.product_types || defaultProductTypes,
|
||||
};
|
||||
|
||||
|
@ -73,7 +38,7 @@ class ProductTypes extends Component {
|
|||
this.onChange = this.onChange.bind( this );
|
||||
}
|
||||
|
||||
async validateField() {
|
||||
validateField() {
|
||||
const error = this.state.selected.length
|
||||
? null
|
||||
: __(
|
||||
|
@ -81,37 +46,31 @@ class ProductTypes extends Component {
|
|||
'woocommerce-admin'
|
||||
);
|
||||
this.setState( { error } );
|
||||
return ! error;
|
||||
}
|
||||
|
||||
async onContinue() {
|
||||
await this.validateField();
|
||||
if ( this.state.error ) {
|
||||
onContinue() {
|
||||
if ( ! this.validateField() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
createNotice,
|
||||
goToNextStep,
|
||||
isError,
|
||||
updateProfileItems,
|
||||
} = this.props;
|
||||
const { createNotice, goToNextStep, updateProfileItems } = this.props;
|
||||
|
||||
recordEvent( 'storeprofiler_store_product_type_continue', {
|
||||
product_type: this.state.selected,
|
||||
} );
|
||||
await updateProfileItems( { product_types: this.state.selected } );
|
||||
|
||||
if ( ! isError ) {
|
||||
goToNextStep();
|
||||
} else {
|
||||
createNotice(
|
||||
'error',
|
||||
__(
|
||||
'There was a problem updating your product types.',
|
||||
'woocommerce-admin'
|
||||
updateProfileItems( { product_types: this.state.selected } )
|
||||
.then( () => goToNextStep() )
|
||||
.catch( () =>
|
||||
createNotice(
|
||||
'error',
|
||||
__(
|
||||
'There was a problem updating your product types.',
|
||||
'woocommerce-admin'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onChange( slug ) {
|
||||
|
@ -135,15 +94,9 @@ class ProductTypes extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
onLearnMore( slug ) {
|
||||
recordEvent( 'storeprofiler_store_product_type_learn_more', {
|
||||
product_type: slug,
|
||||
} );
|
||||
}
|
||||
|
||||
render() {
|
||||
const { productTypes = {} } = getSetting( 'onboarding', {} );
|
||||
const { error, selected } = this.state;
|
||||
const { error, isMonthlyPricing, selected } = this.state;
|
||||
|
||||
return (
|
||||
<div className="woocommerce-profile-wizard__product-types">
|
||||
|
@ -160,51 +113,51 @@ class ProductTypes extends Component {
|
|||
<Card>
|
||||
<div className="woocommerce-profile-wizard__checkbox-group">
|
||||
{ Object.keys( productTypes ).map( ( slug ) => {
|
||||
const label = getLabel(
|
||||
productTypes[ slug ].label,
|
||||
productTypes[ slug ].yearly_price
|
||||
);
|
||||
const moreUrl = productTypes[ slug ].more_url;
|
||||
const helpText =
|
||||
productTypes[ slug ].description &&
|
||||
interpolateComponents( {
|
||||
mixedString:
|
||||
productTypes[ slug ].description +
|
||||
( productTypes[ slug ].more_url
|
||||
? ' {{moreLink/}}'
|
||||
: '' ),
|
||||
components: {
|
||||
moreLink: moreUrl ? (
|
||||
<Link
|
||||
href={ moreUrl }
|
||||
target="_blank"
|
||||
type="external"
|
||||
onClick={ () =>
|
||||
this.onLearnMore( slug )
|
||||
}
|
||||
>
|
||||
{ __(
|
||||
'Learn more',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</Link>
|
||||
) : (
|
||||
''
|
||||
),
|
||||
},
|
||||
} );
|
||||
|
||||
return (
|
||||
<CheckboxControl
|
||||
key={ slug }
|
||||
label={ label }
|
||||
help={ helpText }
|
||||
label={
|
||||
<ProductType
|
||||
description={
|
||||
productTypes[ slug ].description
|
||||
}
|
||||
label={ productTypes[ slug ].label }
|
||||
annualPrice={
|
||||
productTypes[ slug ]
|
||||
.yearly_price
|
||||
}
|
||||
isMonthlyPricing={
|
||||
isMonthlyPricing
|
||||
}
|
||||
moreUrl={
|
||||
productTypes[ slug ].more_url
|
||||
}
|
||||
slug={ slug }
|
||||
/>
|
||||
}
|
||||
onChange={ () => this.onChange( slug ) }
|
||||
checked={ selected.includes( slug ) }
|
||||
className="woocommerce-profile-wizard__checkbox"
|
||||
/>
|
||||
);
|
||||
} ) }
|
||||
<div className="woocommerce-profile-wizard__product-types-pricing-toggle woocommerce-profile-wizard__checkbox">
|
||||
<label htmlFor="woocommerce-product-types__pricing-toggle">
|
||||
{ __(
|
||||
'Display monthly prices',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
<FormToggle
|
||||
id="woocommerce-product-types__pricing-toggle"
|
||||
checked={ isMonthlyPricing }
|
||||
onChange={ () =>
|
||||
this.setState( {
|
||||
isMonthlyPricing: ! isMonthlyPricing,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{ error && (
|
||||
<span className="woocommerce-profile-wizard__error">
|
||||
{ error }
|
||||
|
@ -222,6 +175,12 @@ class ProductTypes extends Component {
|
|||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
<div className="woocommerce-profile-wizard__card-help-text">
|
||||
{ __(
|
||||
'Billing is annual. All purchases are covered by our 30 day money back guarantee and include access to support and updates. Extensions will be added to a cart for you to purchase later.',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Button, Popover, Tooltip } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
import interpolateComponents from 'interpolate-components';
|
||||
import { Link, Pill } from '@woocommerce/components';
|
||||
import { recordEvent } from '@woocommerce/tracks';
|
||||
|
||||
export default function ProductType( {
|
||||
annualPrice,
|
||||
description,
|
||||
isMonthlyPricing,
|
||||
label,
|
||||
moreUrl,
|
||||
slug,
|
||||
} ) {
|
||||
const [ isPopoverVisible, setIsPopoverVisible ] = useState( '' );
|
||||
if ( ! annualPrice ) {
|
||||
return label;
|
||||
}
|
||||
|
||||
/* eslint-disable @wordpress/i18n-no-collapsible-whitespace */
|
||||
const toolTipText = __(
|
||||
"This product type requires a paid extension.\nWe'll add this to a cart so that\nyou can purchase and install it later.",
|
||||
'woocommerce-admin'
|
||||
);
|
||||
/* eslint-enable @wordpress/i18n-no-collapsible-whitespace */
|
||||
|
||||
return (
|
||||
<div className="woocommerce-product-type">
|
||||
<span className="woocommerce-product-type__label">{ label }</span>
|
||||
<Button
|
||||
isTertiary
|
||||
label={ __(
|
||||
'Learn more about recommended free business features',
|
||||
'woocommerce-admin'
|
||||
) }
|
||||
onClick={ () => {
|
||||
setIsPopoverVisible( true );
|
||||
} }
|
||||
>
|
||||
<i className="material-icons-outlined" aria-hidden="true">
|
||||
info
|
||||
</i>
|
||||
</Button>
|
||||
{ isPopoverVisible && (
|
||||
<Popover
|
||||
focusOnMount="container"
|
||||
position="top center"
|
||||
onClose={ () => setIsPopoverVisible( false ) }
|
||||
>
|
||||
{ interpolateComponents( {
|
||||
mixedString:
|
||||
description + ( moreUrl ? ' {{moreLink/}}' : '' ),
|
||||
components: {
|
||||
moreLink: moreUrl ? (
|
||||
<Link
|
||||
href={ moreUrl }
|
||||
target="_blank"
|
||||
type="external"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'storeprofiler_store_product_type_learn_more',
|
||||
{
|
||||
product_type: slug,
|
||||
}
|
||||
)
|
||||
}
|
||||
>
|
||||
{ __( 'Learn more', 'woocommerce-admin' ) }
|
||||
</Link>
|
||||
) : (
|
||||
''
|
||||
),
|
||||
},
|
||||
} ) }
|
||||
</Popover>
|
||||
) }
|
||||
<Tooltip text={ toolTipText } position="bottom center">
|
||||
<Pill>
|
||||
<span className="screen-reader-text">{ toolTipText }</span>
|
||||
{ isMonthlyPricing
|
||||
? sprintf(
|
||||
/* translators: Dollar amount (example: $4.08 ) */
|
||||
__( '$%f per month', 'woocommerce-admin' ),
|
||||
( annualPrice / 12.0 ).toFixed( 2 )
|
||||
)
|
||||
: sprintf(
|
||||
/* translators: Dollar amount (example: $49.00 ) */
|
||||
__( '$%f per year', 'woocommerce-admin' ),
|
||||
annualPrice
|
||||
) }
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
.woocommerce-profile-wizard__product-types {
|
||||
.components-base-control__field {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.woocommerce-product-type {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.woocommerce-product-type__label {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $gray-100;
|
||||
|
||||
.components-button {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.components-base-control__field {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.components-checkbox-control__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.woocommerce-pill {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.components-popover .components-popover__content {
|
||||
min-width: 360px;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__product-types-pricing-toggle.woocommerce-profile-wizard__checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: $gray-600;
|
||||
font-size: 14px;
|
||||
|
||||
label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.components-form-toggle {
|
||||
display: inline-flex;
|
||||
margin-left: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__card-actions .components-button.is-primary {
|
||||
margin-top: $gap;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ProductTypes should render product types 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__product-types"
|
||||
>
|
||||
<h2
|
||||
class="woocommerce-profile-wizard__header-title"
|
||||
>
|
||||
What type of products will be listed?
|
||||
</h2>
|
||||
<h2
|
||||
class="woocommerce-profile-wizard__header-subtitle"
|
||||
>
|
||||
Choose any that apply
|
||||
</h2>
|
||||
<div
|
||||
class="woocommerce-card"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-card__body"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__checkbox-group"
|
||||
>
|
||||
<div
|
||||
class="components-base-control woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field"
|
||||
>
|
||||
<span
|
||||
class="components-checkbox-control__input-container"
|
||||
>
|
||||
<input
|
||||
class="components-checkbox-control__input"
|
||||
id="inspector-checkbox-control-0"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
class="components-checkbox-control__label"
|
||||
for="inspector-checkbox-control-0"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-product-type"
|
||||
>
|
||||
<span
|
||||
class="woocommerce-product-type__label"
|
||||
>
|
||||
Paid product
|
||||
</span>
|
||||
<button
|
||||
aria-label="Learn more about recommended free business features"
|
||||
class="components-button is-tertiary"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="material-icons-outlined"
|
||||
>
|
||||
info
|
||||
</i>
|
||||
</button>
|
||||
|
||||
<span
|
||||
class="woocommerce-pill"
|
||||
>
|
||||
<span
|
||||
class="screen-reader-text"
|
||||
>
|
||||
This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later.
|
||||
</span>
|
||||
$10 per month
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="components-base-control woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field"
|
||||
>
|
||||
<span
|
||||
class="components-checkbox-control__input-container"
|
||||
>
|
||||
<input
|
||||
class="components-checkbox-control__input"
|
||||
id="inspector-checkbox-control-1"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
class="components-checkbox-control__label"
|
||||
for="inspector-checkbox-control-1"
|
||||
>
|
||||
Free product
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__product-types-pricing-toggle woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<label
|
||||
for="woocommerce-product-types__pricing-toggle"
|
||||
>
|
||||
Display monthly prices
|
||||
<span
|
||||
class="components-form-toggle is-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="components-form-toggle__input"
|
||||
id="woocommerce-product-types__pricing-toggle"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="components-form-toggle__track"
|
||||
/>
|
||||
<span
|
||||
class="components-form-toggle__thumb"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__card-actions"
|
||||
>
|
||||
<button
|
||||
class="components-button is-primary"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__card-help-text"
|
||||
>
|
||||
Billing is annual. All purchases are covered by our 30 day money back guarantee and include access to support and updates. Extensions will be added to a cart for you to purchase later.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ProductTypes should show annual prices on toggle 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__product-types"
|
||||
>
|
||||
<h2
|
||||
class="woocommerce-profile-wizard__header-title"
|
||||
>
|
||||
What type of products will be listed?
|
||||
</h2>
|
||||
<h2
|
||||
class="woocommerce-profile-wizard__header-subtitle"
|
||||
>
|
||||
Choose any that apply
|
||||
</h2>
|
||||
<div
|
||||
class="woocommerce-card"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-card__body"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__checkbox-group"
|
||||
>
|
||||
<div
|
||||
class="components-base-control woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field"
|
||||
>
|
||||
<span
|
||||
class="components-checkbox-control__input-container"
|
||||
>
|
||||
<input
|
||||
class="components-checkbox-control__input"
|
||||
id="inspector-checkbox-control-2"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
class="components-checkbox-control__label"
|
||||
for="inspector-checkbox-control-2"
|
||||
>
|
||||
<div
|
||||
class="woocommerce-product-type"
|
||||
>
|
||||
<span
|
||||
class="woocommerce-product-type__label"
|
||||
>
|
||||
Paid product
|
||||
</span>
|
||||
<button
|
||||
aria-label="Learn more about recommended free business features"
|
||||
class="components-button is-tertiary"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="material-icons-outlined"
|
||||
>
|
||||
info
|
||||
</i>
|
||||
</button>
|
||||
|
||||
<span
|
||||
class="woocommerce-pill"
|
||||
>
|
||||
<span
|
||||
class="screen-reader-text"
|
||||
>
|
||||
This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later.
|
||||
</span>
|
||||
$120 per year
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="components-base-control woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<div
|
||||
class="components-base-control__field"
|
||||
>
|
||||
<span
|
||||
class="components-checkbox-control__input-container"
|
||||
>
|
||||
<input
|
||||
class="components-checkbox-control__input"
|
||||
id="inspector-checkbox-control-3"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
/>
|
||||
</span>
|
||||
<label
|
||||
class="components-checkbox-control__label"
|
||||
for="inspector-checkbox-control-3"
|
||||
>
|
||||
Free product
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__product-types-pricing-toggle woocommerce-profile-wizard__checkbox"
|
||||
>
|
||||
<label
|
||||
for="woocommerce-product-types__pricing-toggle"
|
||||
>
|
||||
Display monthly prices
|
||||
<span
|
||||
class="components-form-toggle"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="components-form-toggle__input"
|
||||
id="woocommerce-product-types__pricing-toggle"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span
|
||||
class="components-form-toggle__track"
|
||||
/>
|
||||
<span
|
||||
class="components-form-toggle__thumb"
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__card-actions"
|
||||
>
|
||||
<button
|
||||
class="components-button is-primary"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="woocommerce-profile-wizard__card-help-text"
|
||||
>
|
||||
Billing is annual. All purchases are covered by our 30 day money back guarantee and include access to support and updates. Extensions will be added to a cart for you to purchase later.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,83 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ProductType should render the product type 1`] = `
|
||||
<div
|
||||
className="woocommerce-product-type"
|
||||
>
|
||||
<span
|
||||
className="woocommerce-product-type__label"
|
||||
>
|
||||
Product type label
|
||||
</span>
|
||||
<ForwardRef(Button)
|
||||
isTertiary={true}
|
||||
label="Learn more about recommended free business features"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="material-icons-outlined"
|
||||
>
|
||||
info
|
||||
</i>
|
||||
</ForwardRef(Button)>
|
||||
<Tooltip
|
||||
position="bottom center"
|
||||
text="This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later."
|
||||
>
|
||||
<Pill>
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later.
|
||||
</span>
|
||||
$120 per year
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ProductType should render the product type with monthly prices 1`] = `
|
||||
<div
|
||||
className="woocommerce-product-type"
|
||||
>
|
||||
<span
|
||||
className="woocommerce-product-type__label"
|
||||
>
|
||||
Product type label
|
||||
</span>
|
||||
<ForwardRef(Button)
|
||||
isTertiary={true}
|
||||
label="Learn more about recommended free business features"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="material-icons-outlined"
|
||||
>
|
||||
info
|
||||
</i>
|
||||
</ForwardRef(Button)>
|
||||
<Tooltip
|
||||
position="bottom center"
|
||||
text="This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later."
|
||||
>
|
||||
<Pill>
|
||||
<span
|
||||
className="screen-reader-text"
|
||||
>
|
||||
This product type requires a paid extension.
|
||||
We'll add this to a cart so that
|
||||
you can purchase and install it later.
|
||||
</span>
|
||||
$10 per month
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { setSetting } from '@woocommerce/wc-admin-settings';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ProductTypes } from '../';
|
||||
|
||||
describe( 'ProductTypes', () => {
|
||||
beforeEach( () => {
|
||||
setSetting( 'onboarding', {
|
||||
productTypes: {
|
||||
paidProduct: {
|
||||
description: 'Paid product type',
|
||||
label: 'Paid product',
|
||||
more_url: 'https://woocommerce.com/paid-product',
|
||||
product: 100,
|
||||
slug: 'paid-product',
|
||||
yearly_price: 120,
|
||||
},
|
||||
freeProduct: {
|
||||
label: 'Free product',
|
||||
},
|
||||
},
|
||||
} );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
setSetting( 'onboarding', {} );
|
||||
} );
|
||||
|
||||
test( 'should render product types', () => {
|
||||
const { container } = render( <ProductTypes /> );
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'should show annual prices on toggle', () => {
|
||||
const { container } = render( <ProductTypes /> );
|
||||
|
||||
const toggle = screen.getByLabelText( 'Display monthly prices', {
|
||||
selector: 'input',
|
||||
} );
|
||||
|
||||
userEvent.click( toggle );
|
||||
|
||||
expect( container ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'should validate on continue', async () => {
|
||||
const mockCreateNotice = jest.fn();
|
||||
const mockGoToNextStep = jest.fn();
|
||||
const mockUpdateProfileItems = jest.fn().mockResolvedValue();
|
||||
|
||||
render(
|
||||
<ProductTypes
|
||||
createNotice={ mockCreateNotice }
|
||||
goToNextStep={ mockGoToNextStep }
|
||||
updateProfileItems={ mockUpdateProfileItems }
|
||||
/>
|
||||
);
|
||||
|
||||
const continueButton = screen.getByText( 'Continue', {
|
||||
selector: 'button',
|
||||
} );
|
||||
const productType = screen.getByText( 'Free product', {
|
||||
selector: 'label',
|
||||
} );
|
||||
|
||||
// Validation should fail since no product types are selected.
|
||||
userEvent.click( continueButton );
|
||||
await waitFor( () => {
|
||||
expect( mockGoToNextStep ).not.toHaveBeenCalled();
|
||||
expect( mockUpdateProfileItems ).not.toHaveBeenCalled();
|
||||
} );
|
||||
|
||||
// Click on a product type to pass validation.
|
||||
userEvent.click( productType );
|
||||
userEvent.click( continueButton );
|
||||
await waitFor( () => {
|
||||
expect( mockUpdateProfileItems ).toHaveBeenCalled();
|
||||
expect( mockGoToNextStep ).toHaveBeenCalled();
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { shallow } from 'enzyme';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import ProductType from '../product-type';
|
||||
|
||||
const defaultProps = {
|
||||
annualPrice: 120,
|
||||
label: 'Product type label',
|
||||
description: 'Product type description',
|
||||
moreUrl: 'https://woocommerce.com/my-product-type',
|
||||
slug: 'my-product-type',
|
||||
};
|
||||
|
||||
describe( 'ProductType', () => {
|
||||
test( 'should render the product type', () => {
|
||||
const productType = shallow(
|
||||
<ProductType { ...defaultProps } isMonthlyPricing={ false } />
|
||||
);
|
||||
expect( productType ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'should render the product type with monthly prices', () => {
|
||||
const productType = shallow(
|
||||
<ProductType { ...defaultProps } isMonthlyPricing={ true } />
|
||||
);
|
||||
expect( productType ).toMatchSnapshot();
|
||||
} );
|
||||
|
||||
test( 'should show Popover on click', () => {
|
||||
const { container } = render(
|
||||
<ProductType { ...defaultProps } isMonthlyPricing={ true } />
|
||||
);
|
||||
|
||||
const infoButton = screen.getByLabelText(
|
||||
'Learn more about recommended free business features',
|
||||
{
|
||||
selector: 'button',
|
||||
}
|
||||
);
|
||||
|
||||
userEvent.click( infoButton );
|
||||
|
||||
const popover = container.querySelector( '.components-popover' );
|
||||
const learnMoreLink = popover.querySelector( 'a' );
|
||||
expect( popover ).not.toBeNull();
|
||||
expect( popover.textContent ).toBe(
|
||||
defaultProps.description + ' Learn more'
|
||||
);
|
||||
expect( learnMoreLink.href ).toBe( defaultProps.moreUrl );
|
||||
} );
|
||||
} );
|
|
@ -35,6 +35,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.woocommerce-card + .woocommerce-profile-wizard__card-help-text {
|
||||
font-size: 14px;
|
||||
color: $gray-600;
|
||||
text-align: center;
|
||||
margin-top: $gap;
|
||||
}
|
||||
|
||||
.woocommerce-profile-wizard__header {
|
||||
height: 56px;
|
||||
border-bottom: 1px solid $studio-gray-5;
|
||||
|
|
Loading…
Reference in New Issue