Add event handlers in Form component and update onboarding form (https://github.com/woocommerce/woocommerce-admin/pull/2749)
* Add better checkbox and radio support * Add checkbox, select, and radio examples to form component * Update business details step to use new Form component
This commit is contained in:
parent
ea6da8858d
commit
1e042d355c
|
@ -18,7 +18,7 @@ import { numberFormat } from '@woocommerce/number';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { H, Card, SimpleSelectControl } from '@woocommerce/components';
|
import { H, Card, SimpleSelectControl, Form } from '@woocommerce/components';
|
||||||
import withSelect from 'wc-api/with-select';
|
import withSelect from 'wc-api/with-select';
|
||||||
import { recordEvent } from 'lib/tracks';
|
import { recordEvent } from 'lib/tracks';
|
||||||
|
|
||||||
|
@ -26,38 +26,31 @@ class BusinessDetails extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.state = {
|
this.initialValues = {
|
||||||
errors: {},
|
other_platform: '',
|
||||||
fields: {
|
product_count: '',
|
||||||
other_platform: '',
|
selling_venues: '',
|
||||||
product_count: '',
|
facebook: true,
|
||||||
selling_venues: '',
|
mailchimp: true,
|
||||||
extensions: {
|
|
||||||
facebook: true,
|
|
||||||
mailchimp: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.extensions = [ 'facebook', 'mailchimp' ];
|
||||||
|
|
||||||
this.onContinue = this.onContinue.bind( this );
|
this.onContinue = this.onContinue.bind( this );
|
||||||
|
this.validate = this.validate.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
async onContinue() {
|
async onContinue( values ) {
|
||||||
await this.validateForm();
|
|
||||||
if ( Object.keys( this.state.errors ).length ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
|
const { createNotice, goToNextStep, isError, updateProfileItems } = this.props;
|
||||||
const { extensions, other_platform, product_count, selling_venues } = this.state.fields;
|
const { facebook, mailchimp, other_platform, product_count, selling_venues } = values;
|
||||||
const businessExtensions = keys( pickBy( extensions ) );
|
const businessExtensions = this.getBusinessExtensions( values );
|
||||||
|
|
||||||
recordEvent( 'storeprofiler_store_business_details_continue', {
|
recordEvent( 'storeprofiler_store_business_details_continue', {
|
||||||
product_number: product_count,
|
product_number: product_count,
|
||||||
already_selling: 'no' !== selling_venues,
|
already_selling: 'no' !== selling_venues,
|
||||||
used_platform: other_platform,
|
used_platform: other_platform,
|
||||||
install_facebook: extensions.facebook,
|
install_facebook: facebook,
|
||||||
install_mailchimp: extensions.mailchimp,
|
install_mailchimp: mailchimp,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
await updateProfileItems( {
|
await updateProfileItems( {
|
||||||
|
@ -77,37 +70,27 @@ class BusinessDetails extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validateField( name ) {
|
validate( values ) {
|
||||||
const { errors, fields } = this.state;
|
const errors = {};
|
||||||
|
|
||||||
switch ( name ) {
|
Object.keys( values ).map( name => {
|
||||||
case 'extensions':
|
if ( 'other_platform' === name ) {
|
||||||
break;
|
if (
|
||||||
case 'other_platform':
|
! values.other_platform.length &&
|
||||||
errors.other_platform =
|
[ 'other', 'brick-mortar-other' ].includes( values.selling_venues )
|
||||||
[ 'other', 'brick-mortar-other' ].includes( fields.selling_venues ) &&
|
) {
|
||||||
! fields.other_platform.length
|
errors.other_platform = __( 'This field is required', 'woocommerce-admin' );
|
||||||
? __( 'This field is required', 'woocommerce-admin' )
|
}
|
||||||
: null;
|
} else if ( ! this.extensions.includes( name ) && ! values[ name ].length ) {
|
||||||
break;
|
errors[ name ] = __( 'This field is required', 'woocommerce-admin' );
|
||||||
default:
|
}
|
||||||
errors[ name ] = ! fields[ name ].length
|
} );
|
||||||
? __( 'This field is required', 'woocommerce-admin' )
|
|
||||||
: null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState( { errors: pickBy( errors ) } );
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValue( name, value ) {
|
getBusinessExtensions( values ) {
|
||||||
const fields = { ...this.state.fields, [ name ]: value };
|
return keys( pickBy( values ) ).filter( name => this.extensions.includes( name ) );
|
||||||
this.setState( { fields }, () => this.validateField( name ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
async validateForm() {
|
|
||||||
const { fields } = this.state;
|
|
||||||
Object.keys( fields ).forEach( fieldName => this.validateField( fieldName ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNumberRangeString( min, max = false ) {
|
getNumberRangeString( min, max = false ) {
|
||||||
|
@ -125,33 +108,13 @@ class BusinessDetails extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDefaultValue( key, options ) {
|
renderBusinessExtensionHelpText( values ) {
|
||||||
const { fields } = this.state;
|
const extensions = this.getBusinessExtensions( values );
|
||||||
|
|
||||||
if ( ! fields[ key ].length ) {
|
|
||||||
this.setState( { fields: { ...fields, [ key ]: options[ 0 ].value } }, () =>
|
|
||||||
this.validateField( key )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExtensionChange( extension ) {
|
|
||||||
const { fields } = this.state;
|
|
||||||
const extensions = {
|
|
||||||
...fields.extensions,
|
|
||||||
[ extension ]: ! fields.extensions[ extension ],
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setState( { fields: { ...fields, extensions } } );
|
|
||||||
}
|
|
||||||
|
|
||||||
renderBusinessExtensionHelpText() {
|
|
||||||
const extensionSlugs = {
|
const extensionSlugs = {
|
||||||
facebook: __( 'Facebook for WooCommerce', 'woocommerce-admin' ),
|
facebook: __( 'Facebook for WooCommerce', 'woocommerce-admin' ),
|
||||||
mailchimp: __( 'Mailchimp for WooCommerce', 'woocommerce-admin' ),
|
mailchimp: __( 'Mailchimp for WooCommerce', 'woocommerce-admin' ),
|
||||||
};
|
};
|
||||||
|
|
||||||
const extensions = keys( pickBy( this.state.fields.extensions ) );
|
|
||||||
if ( 0 === extensions.length ) {
|
if ( 0 === extensions.length ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +133,7 @@ class BusinessDetails extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBusinessExtensions() {
|
renderBusinessExtensions( values, getInputProps ) {
|
||||||
const extensionBenefits = [
|
const extensionBenefits = [
|
||||||
{
|
{
|
||||||
slug: 'facebook',
|
slug: 'facebook',
|
||||||
|
@ -191,6 +154,7 @@ class BusinessDetails extends Component {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="woocommerce-profile-wizard__benefits">
|
<div className="woocommerce-profile-wizard__benefits">
|
||||||
{ extensionBenefits.map( benefit => (
|
{ extensionBenefits.map( benefit => (
|
||||||
|
@ -204,9 +168,9 @@ class BusinessDetails extends Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="woocommerce-profile-wizard__benefit-toggle">
|
<div className="woocommerce-profile-wizard__benefit-toggle">
|
||||||
<FormToggle
|
<FormToggle
|
||||||
checked={ this.state.fields.extensions[ benefit.slug ] }
|
checked={ values[ benefit.slug ] }
|
||||||
onChange={ () => this.onExtensionChange( benefit.slug ) }
|
|
||||||
className="woocommerce-profile-wizard__toggle"
|
className="woocommerce-profile-wizard__toggle"
|
||||||
|
{ ...getInputProps( benefit.slug ) }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -216,9 +180,6 @@ class BusinessDetails extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { errors, fields } = this.state;
|
|
||||||
const { other_platform, product_count, selling_venues } = fields;
|
|
||||||
|
|
||||||
const productCountOptions = [
|
const productCountOptions = [
|
||||||
{
|
{
|
||||||
value: '1-10',
|
value: '1-10',
|
||||||
|
@ -283,65 +244,63 @@ class BusinessDetails extends Component {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Show extensions when the currently selling elsewhere checkbox has been answered.
|
|
||||||
const showExtensions = '' !== selling_venues;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Form
|
||||||
<H className="woocommerce-profile-wizard__header-title">
|
initialValues={ this.initialValues }
|
||||||
{ __( 'Business details', 'woocommerce-admin' ) }
|
onSubmitCallback={ this.onContinue }
|
||||||
</H>
|
validate={ this.validate }
|
||||||
<p>{ __( 'Tell us about the business' ) }</p>
|
>
|
||||||
|
{ ( { getInputProps, handleSubmit, values } ) => {
|
||||||
|
// Show extensions when the currently selling elsewhere checkbox has been answered.
|
||||||
|
const showExtensions = '' !== values.selling_venues;
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<H className="woocommerce-profile-wizard__header-title">
|
||||||
|
{ __( 'Business details', 'woocommerce-admin' ) }
|
||||||
|
</H>
|
||||||
|
<p>{ __( 'Tell us about the business', 'woocommerce-admin' ) }</p>
|
||||||
|
<Card>
|
||||||
|
<Fragment>
|
||||||
|
<SimpleSelectControl
|
||||||
|
label={ __( 'How many products will you add?', 'woocommerce-admin' ) }
|
||||||
|
options={ productCountOptions }
|
||||||
|
required
|
||||||
|
{ ...getInputProps( 'product_count' ) }
|
||||||
|
/>
|
||||||
|
|
||||||
<Card>
|
<SimpleSelectControl
|
||||||
<SimpleSelectControl
|
label={ __( 'Currently selling elsewhere?', 'woocommerce-admin' ) }
|
||||||
label={ __( 'How many products will you add?', 'woocommerce-admin' ) }
|
options={ sellingVenueOptions }
|
||||||
onChange={ value => this.updateValue( 'product_count', value ) }
|
required
|
||||||
onFocus={ this.setDefaultValue.bind( this, 'product_count', productCountOptions ) }
|
{ ...getInputProps( 'selling_venues' ) }
|
||||||
options={ productCountOptions }
|
/>
|
||||||
value={ product_count }
|
|
||||||
help={ errors.product_count }
|
|
||||||
className={ errors.product_count ? 'has-error' : null }
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SimpleSelectControl
|
{ [ 'other', 'brick-mortar-other' ].includes( values.selling_venues ) && (
|
||||||
label={ __( 'Currently selling elsewhere?', 'woocommerce-admin' ) }
|
<SimpleSelectControl
|
||||||
onChange={ value => this.updateValue( 'selling_venues', value ) }
|
label={ __( 'Which platform is the store using?', 'woocommerce-admin' ) }
|
||||||
onFocus={ this.setDefaultValue.bind( this, 'selling_venues', sellingVenueOptions ) }
|
options={ otherPlatformOptions }
|
||||||
options={ sellingVenueOptions }
|
required
|
||||||
value={ selling_venues }
|
{ ...getInputProps( 'other_platform' ) }
|
||||||
help={ errors.selling_venues }
|
/>
|
||||||
className={ errors.selling_venues ? 'has-error' : null }
|
) }
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
{ [ 'other', 'brick-mortar-other' ].includes( selling_venues ) && (
|
{ showExtensions && this.renderBusinessExtensions( values, getInputProps ) }
|
||||||
<SimpleSelectControl
|
|
||||||
label={ __( 'Which platform is the store using?', 'woocommerce-admin' ) }
|
|
||||||
onChange={ value => this.updateValue( 'other_platform', value ) }
|
|
||||||
onFocus={ this.setDefaultValue.bind( this, 'other_platform', otherPlatformOptions ) }
|
|
||||||
options={ otherPlatformOptions }
|
|
||||||
value={ other_platform }
|
|
||||||
help={ errors.other_platform }
|
|
||||||
className={ errors.other_platform ? 'has-error' : null }
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
) }
|
|
||||||
|
|
||||||
{ showExtensions && this.renderBusinessExtensions() }
|
<Button
|
||||||
|
isPrimary
|
||||||
|
className="woocommerce-profile-wizard__continue"
|
||||||
|
onClick={ handleSubmit }
|
||||||
|
>
|
||||||
|
{ __( 'Continue', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Button
|
{ showExtensions && this.renderBusinessExtensionHelpText( values ) }
|
||||||
isPrimary
|
</Fragment>
|
||||||
className="woocommerce-profile-wizard__continue"
|
);
|
||||||
onClick={ this.onContinue }
|
} }
|
||||||
>
|
</Form>
|
||||||
{ __( 'Continue', 'woocommerce-admin' ) }
|
|
||||||
</Button>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{ showExtensions && this.renderBusinessExtensionHelpText() }
|
|
||||||
</Fragment>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
```jsx
|
```jsx
|
||||||
import { Button TextControl } from '@wordpress/components';
|
import { Button, CheckboxControl, RadioControl, SelectControl, TextControl } from '@wordpress/components';
|
||||||
import { Form } from '@woocommerce/components';
|
import { Form } from '@woocommerce/components';
|
||||||
|
|
||||||
const validate = ( values ) => {
|
const validate = ( values ) => {
|
||||||
|
@ -14,7 +14,7 @@ const validate = ( values ) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmitCallback = ( values ) => console.log( values );
|
const onSubmitCallback = ( values ) => console.log( values );
|
||||||
const initialValues = { firstName: '', lastName: '' };
|
const initialValues = { firstName: '', lastName: '', select: '3', checkbox: true, radio: '2' };
|
||||||
|
|
||||||
const MyForm = () => (
|
const MyForm = () => (
|
||||||
<Form validate={ validate } onSubmitCallback={ onSubmitCallback } initialValues={ initialValues }>
|
<Form validate={ validate } onSubmitCallback={ onSubmitCallback } initialValues={ initialValues }>
|
||||||
|
@ -34,6 +34,28 @@ const MyForm = () => (
|
||||||
label={ 'Last Name' }
|
label={ 'Last Name' }
|
||||||
{ ...getInputProps( 'lastName' ) }
|
{ ...getInputProps( 'lastName' ) }
|
||||||
/>
|
/>
|
||||||
|
<SelectControl
|
||||||
|
label='Select'
|
||||||
|
options={ [
|
||||||
|
{ label: 'Option 1', value: '1' },
|
||||||
|
{ label: 'Option 2', value: '2' },
|
||||||
|
{ label: 'Option 3', value: '3' },
|
||||||
|
] }
|
||||||
|
{ ...getInputProps( 'select' ) }
|
||||||
|
/>
|
||||||
|
<CheckboxControl
|
||||||
|
label='Checkbox'
|
||||||
|
{ ...getInputProps( 'checkbox' ) }
|
||||||
|
/>
|
||||||
|
<RadioControl
|
||||||
|
label='Radio'
|
||||||
|
options={ [
|
||||||
|
{ label: 'Option 1', value: '1' },
|
||||||
|
{ label: 'Option 2', value: '2' },
|
||||||
|
{ label: 'Option 3', value: '3' },
|
||||||
|
] }
|
||||||
|
{ ...getInputProps( 'radio' ) }
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
isPrimary
|
isPrimary
|
||||||
onClick={ handleSubmit }
|
onClick={ handleSubmit }
|
||||||
|
|
|
@ -44,6 +44,21 @@ class Form extends Component {
|
||||||
} ), this.validate );
|
} ), this.validate );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChange( name, value ) {
|
||||||
|
const { values } = this.state;
|
||||||
|
|
||||||
|
// Handle native events.
|
||||||
|
if ( value.target ) {
|
||||||
|
if ( 'checkbox' === value.target.type ) {
|
||||||
|
this.setValue( name, ! values[ name ] );
|
||||||
|
} else {
|
||||||
|
this.setValue( name, value.target.value );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setValue( name, value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleBlur( name ) {
|
handleBlur( name ) {
|
||||||
this.setState( prevState => ( {
|
this.setState( prevState => ( {
|
||||||
touched: { ...prevState.touched, [ name ]: true },
|
touched: { ...prevState.touched, [ name ]: true },
|
||||||
|
@ -66,7 +81,9 @@ class Form extends Component {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: values[ name ],
|
value: values[ name ],
|
||||||
onChange: ( value ) => this.setValue( name, value ),
|
checked: Boolean( values[ name ] ),
|
||||||
|
selected: values[ name ],
|
||||||
|
onChange: ( value ) => this.handleChange( name, value ),
|
||||||
onBlur: () => this.handleBlur( name ),
|
onBlur: () => this.handleBlur( name ),
|
||||||
className: touched[ name ] && errors[ name ] ? 'has-error' : null,
|
className: touched[ name ] && errors[ name ] ? 'has-error' : null,
|
||||||
help: touched[ name ] ? errors[ name ] : null,
|
help: touched[ name ] ? errors[ name ] : null,
|
||||||
|
|
Loading…
Reference in New Issue