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:
Joshua T Flowers 2019-08-08 13:25:55 +08:00 committed by GitHub
parent ea6da8858d
commit 1e042d355c
3 changed files with 131 additions and 133 deletions

View File

@ -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>
); );
} }
} }

View File

@ -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 }

View File

@ -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,