Skip to content

Commit

Permalink
Fix validation behavior (woocommerce#38194)
Browse files Browse the repository at this point in the history
* Create ValidationContext

* Add ValidationProvider to the product editor

* Add new validation to the name block

* Add new validation to the Save button

* Store validations within a useRef instead of useState to improves re-renders

* Create find first visible and invalid element function

* Add focus first invalid element feat when submitting

* Integrate autofocus with name block

* Migrate sale-price block to the new validation system

* Add changelog file

* Migrate regular-price block to the new validation system

* Migrate schedule-sale block to the new validation system

* Migrate inventory-quantity block to the new validation system

* Migrate shipping-dimensions block to the new validation system

* Remove old validation hook

* Add validation to the save-draft button

* Add validation to the preview button

* Expose validation hooks to be used outside of the package

* Make sure the product is in fact saved before calling the onPublishSuccess
  • Loading branch information
mdperez86 committed May 17, 2023
1 parent 3db1851 commit 470ecd0
Show file tree
Hide file tree
Showing 25 changed files with 484 additions and 347 deletions.
4 changes: 4 additions & 0 deletions packages/js/product-editor/changelog/add-37984
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Fix validation behavior#37984
23 changes: 16 additions & 7 deletions packages/js/product-editor/src/blocks/inventory-quantity/edit.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
import { Product } from '@woocommerce/data';
import { BlockEditProps } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { useInstanceId } from '@wordpress/compose';
Expand All @@ -17,10 +18,11 @@ import {
* Internal dependencies
*/
import { TrackInventoryBlockAttributes } from './types';
import { useValidation } from '../../contexts/validation-context';

import { useValidation } from '../../hooks/use-validation';

export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) {
export function Edit( {
clientId,
}: BlockEditProps< TrackInventoryBlockAttributes > ) {
const blockProps = useBlockProps();

const [ manageStock ] = useEntityProp< boolean >(
Expand All @@ -40,16 +42,21 @@ export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) {
'product_stock_quantity'
) as string;

const stockQuantityValidationError = useValidation(
'product/stock_quantity',
function stockQuantityValidator() {
const {
ref: stockQuantityRef,
error: stockQuantityValidationError,
validate: validateStockQuantity,
} = useValidation< Product >(
`stock_quantity-${ clientId }`,
async function stockQuantityValidator() {
if ( manageStock && stockQuantity && stockQuantity < 0 ) {
return __(
'Stock quantity must be a positive number.',
'woocommerce'
);
}
}
},
[ manageStock, stockQuantity ]
);

useEffect( () => {
Expand All @@ -72,9 +79,11 @@ export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) {
<InputControl
id={ stockQuantityId }
name="stock_quantity"
ref={ stockQuantityRef }
label={ __( 'Available quantity', 'woocommerce' ) }
value={ stockQuantity }
onChange={ setStockQuantity }
onBlur={ validateStockQuantity }
type="number"
min={ 0 }
/>
Expand Down
21 changes: 15 additions & 6 deletions packages/js/product-editor/src/blocks/name/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { useEntityProp, useEntityId } from '@wordpress/core-data';
*/
import { AUTO_DRAFT_NAME } from '../../utils';
import { EditProductLinkModal } from '../../components/edit-product-link-modal';
import { useValidation } from '../../hooks/use-validation';
import { useValidation } from '../../contexts/validation-context';

export function Edit() {
const blockProps = useBlockProps();
Expand Down Expand Up @@ -77,9 +77,13 @@ export function Edit() {
}
);

const nameValidationError = useValidation(
'product/name',
function nameValidator() {
const {
ref: nameRef,
error: nameValidationError,
validate: validateName,
} = useValidation< Product >(
'name',
async function nameValidator() {
if ( ! name || name === AUTO_DRAFT_NAME ) {
return __( 'This field is required.', 'woocommerce' );
}
Expand All @@ -90,7 +94,8 @@ export function Edit() {
'woocommerce'
);
}
}
},
[ name ]
);

const setSkuIfEmpty = () => {
Expand Down Expand Up @@ -153,14 +158,18 @@ export function Edit() {
>
<InputControl
id={ nameControlId }
ref={ nameRef }
name="name"
placeholder={ __(
'e.g. 12 oz Coffee Mug',
'woocommerce'
) }
onChange={ setName }
value={ name && name !== AUTO_DRAFT_NAME ? name : '' }
onBlur={ setSkuIfEmpty }
onBlur={ () => {
setSkuIfEmpty();
validateName();
} }
/>
</BaseControl>

Expand Down
19 changes: 14 additions & 5 deletions packages/js/product-editor/src/blocks/regular-price/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import classNames from 'classnames';
import { Link } from '@woocommerce/components';
import { CurrencyContext } from '@woocommerce/currency';
import { Product } from '@woocommerce/data';
import { getNewPath } from '@woocommerce/navigation';
import { recordEvent } from '@woocommerce/tracks';
import { useBlockProps } from '@wordpress/block-editor';
Expand All @@ -28,10 +29,11 @@ import {
import { useCurrencyInputProps } from '../../hooks/use-currency-input-props';
import { formatCurrencyDisplayValue } from '../../utils';
import { SalePriceBlockAttributes } from './types';
import { useValidation } from '../../hooks/use-validation';
import { useValidation } from '../../contexts/validation-context';

export function Edit( {
attributes,
clientId,
}: BlockEditProps< SalePriceBlockAttributes > ) {
const blockProps = useBlockProps();
const { label, help } = attributes;
Expand Down Expand Up @@ -71,9 +73,13 @@ export function Edit( {
'wp-block-woocommerce-product-regular-price-field'
) as string;

const regularPriceValidationError = useValidation(
'product/regular_price',
function regularPriceValidator() {
const {
ref: regularPriceRef,
error: regularPriceValidationError,
validate: validateRegularPrice,
} = useValidation< Product >(
`regular_price-${ clientId }`,
async function regularPriceValidator() {
const listPrice = Number.parseFloat( regularPrice );
if ( listPrice ) {
if ( listPrice < 0 ) {
Expand All @@ -92,7 +98,8 @@ export function Edit( {
);
}
}
}
},
[ regularPrice, salePrice ]
);

return (
Expand All @@ -112,13 +119,15 @@ export function Edit( {
{ ...inputProps }
id={ regularPriceId }
name={ 'regular_price' }
ref={ regularPriceRef }
label={ label }
value={ formatCurrencyDisplayValue(
String( regularPrice ),
currencyConfig,
formatAmount
) }
onChange={ setRegularPrice }
onBlur={ validateRegularPrice }
/>
</BaseControl>
</div>
Expand Down
19 changes: 14 additions & 5 deletions packages/js/product-editor/src/blocks/sale-price/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import classNames from 'classnames';
import { CurrencyContext } from '@woocommerce/currency';
import { Product } from '@woocommerce/data';
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps } from '@wordpress/blocks';
import { useInstanceId } from '@wordpress/compose';
Expand All @@ -21,10 +22,11 @@ import {
import { useCurrencyInputProps } from '../../hooks/use-currency-input-props';
import { formatCurrencyDisplayValue } from '../../utils';
import { SalePriceBlockAttributes } from './types';
import { useValidation } from '../../hooks/use-validation';
import { useValidation } from '../../contexts/validation-context';

export function Edit( {
attributes,
clientId,
}: BlockEditProps< SalePriceBlockAttributes > ) {
const blockProps = useBlockProps();
const { label, help } = attributes;
Expand All @@ -51,9 +53,13 @@ export function Edit( {
'wp-block-woocommerce-product-sale-price-field'
) as string;

const salePriceValidationError = useValidation(
'product/sale_price',
function salePriceValidator() {
const {
ref: salePriceRef,
error: salePriceValidationError,
validate: validateSalePrice,
} = useValidation< Product >(
`sale-price-${ clientId }`,
async function salePriceValidator() {
if ( salePrice ) {
if ( Number.parseFloat( salePrice ) < 0 ) {
return __(
Expand All @@ -72,7 +78,8 @@ export function Edit( {
);
}
}
}
},
[ regularPrice, salePrice ]
);

return (
Expand All @@ -90,13 +97,15 @@ export function Edit( {
{ ...inputProps }
id={ salePriceId }
name={ 'sale_price' }
ref={ salePriceRef }
onChange={ setSalePrice }
label={ label }
value={ formatCurrencyDisplayValue(
String( salePrice ),
currencyConfig,
formatAmount
) }
onBlur={ validateSalePrice }
/>
</BaseControl>
</div>
Expand Down
37 changes: 27 additions & 10 deletions packages/js/product-editor/src/blocks/schedule-sale/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import { DateTimePickerControl } from '@woocommerce/components';
import { Product } from '@woocommerce/data';
import { recordEvent } from '@woocommerce/tracks';
import { useBlockProps } from '@wordpress/block-editor';
import { BlockEditProps } from '@wordpress/blocks';
Expand All @@ -19,9 +20,11 @@ import { getSettings } from '@wordpress/date';
* Internal dependencies
*/
import { ScheduleSalePricingBlockAttributes } from './types';
import { useValidation } from '../../hooks/use-validation';
import { useValidation } from '../../contexts/validation-context';

export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) {
export function Edit( {
clientId,
}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) {
const blockProps = useBlockProps();

const dateTimeFormat = getSettings().formats.datetime;
Expand Down Expand Up @@ -84,9 +87,13 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
const _dateOnSaleFrom = moment( dateOnSaleFromGmt, moment.ISO_8601, true );
const _dateOnSaleTo = moment( dateOnSaleToGmt, moment.ISO_8601, true );

const dateOnSaleFromGmtValidationError = useValidation(
'product/date_on_sale_from_gmt',
function dateOnSaleFromValidator() {
const {
// ref: dateOnSaleFromGmtRef,
error: dateOnSaleFromGmtValidationError,
validate: validateDateOnSaleFromGmt,
} = useValidation< Product >(
`date_on_sale_from_gmt-${ clientId }`,
async function dateOnSaleFromValidator() {
if ( showScheduleSale && dateOnSaleFromGmt ) {
if ( ! _dateOnSaleFrom.isValid() ) {
return __( 'Please enter a valid date.', 'woocommerce' );
Expand All @@ -99,12 +106,17 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
);
}
}
}
},
[ showScheduleSale, dateOnSaleFromGmt, _dateOnSaleFrom, _dateOnSaleTo ]
);

const dateOnSaleToGmtValidationError = useValidation(
'product/date_on_sale_to_gmt',
function dateOnSaleToValidator() {
const {
// ref: dateOnSaleToGmtRef,
error: dateOnSaleToGmtValidationError,
validate: validateDateOnSaleToGmt,
} = useValidation< Product >(
`date_on_sale_to_gmt-${ clientId }`,
async function dateOnSaleToValidator() {
if ( showScheduleSale && dateOnSaleToGmt ) {
if ( ! _dateOnSaleTo.isValid() ) {
return __( 'Please enter a valid date.', 'woocommerce' );
Expand All @@ -117,7 +129,8 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
);
}
}
}
},
[ showScheduleSale, dateOnSaleFromGmt, _dateOnSaleFrom, _dateOnSaleTo ]
);

return (
Expand All @@ -133,6 +146,7 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
<div className="wp-block-columns">
<div className="wp-block-column">
<DateTimePickerControl
// ref={ dateOnSaleFromGmtRef }
label={ __( 'From', 'woocommerce' ) }
placeholder={ __(
'Sale start date and time (optional)',
Expand All @@ -145,11 +159,13 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
dateOnSaleFromGmtValidationError && 'has-error'
}
help={ dateOnSaleFromGmtValidationError as string }
onBlur={ validateDateOnSaleFromGmt }
/>
</div>

<div className="wp-block-column">
<DateTimePickerControl
// ref={ dateOnSaleToGmtRef }
label={ __( 'To', 'woocommerce' ) }
placeholder={ __(
'Sale end date and time (optional)',
Expand All @@ -164,6 +180,7 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
.toISOString()
)
}
onBlur={ validateDateOnSaleToGmt }
className={
dateOnSaleToGmtValidationError && 'has-error'
}
Expand Down
Loading

0 comments on commit 470ecd0

Please sign in to comment.