From 2cc0ae0e2b4d9577a7296b5895d8d2b88d20f8c0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 9 Nov 2023 16:25:28 +0000 Subject: [PATCH] Fix address block invalidations in the editor and address card display in Firefox (#11714) * Add checks to see if getCartData finished before rendering address * Prevent block error due to excessive updates of customValidation * Do not condense address in admin and handle phone field * Add missing showPhoneField for billing --------- Co-authored-by: Thomas Roberts --- .../address-form/address-form.tsx | 9 ++++--- .../cart-checkout/address-form/types.ts | 2 -- .../js/blocks/checkout/address-card/index.tsx | 4 ++- .../checkout-billing-address-block/block.tsx | 22 +++++++++++----- .../customer-address.tsx | 3 ++- .../checkout-shipping-address-block/block.tsx | 21 ++++++++++++---- .../customer-address.tsx | 14 ++++++++--- .../checkout/components/text-input/types.ts | 2 ++ .../text-input/validated-text-input.tsx | 25 +++++++++++-------- 9 files changed, 69 insertions(+), 33 deletions(-) diff --git a/assets/js/base/components/cart-checkout/address-form/address-form.tsx b/assets/js/base/components/cart-checkout/address-form/address-form.tsx index 3f1a0b3b66c..481b6b1a46e 100644 --- a/assets/js/base/components/cart-checkout/address-form/address-form.tsx +++ b/assets/js/base/components/cart-checkout/address-form/address-form.tsx @@ -15,7 +15,7 @@ import { ShippingStateInput, } from '@woocommerce/base-components/state-input'; import { useEffect, useMemo, useRef } from '@wordpress/element'; -import { withInstanceId } from '@wordpress/compose'; +import { useInstanceId } from '@wordpress/compose'; import { useShallowEqual } from '@woocommerce/base-hooks'; import { defaultAddressFields } from '@woocommerce/settings'; import isShallowEqual from '@wordpress/is-shallow-equal'; @@ -44,11 +44,12 @@ const AddressForm = ( { id = '', fields = defaultFields, fieldConfig = {} as FieldConfig, - instanceId, onChange, type = 'shipping', values, }: AddressFormProps ): JSX.Element => { + const instanceId = useInstanceId( AddressForm ); + // Track incoming props. const currentFields = useShallowEqual( fields ); const currentFieldConfig = useShallowEqual( fieldConfig ); @@ -99,7 +100,7 @@ const AddressForm = ( { fieldsRef.current?.postcode?.revalidate(); }, [ currentCountry ] ); - id = id || instanceId; + id = id || `${ instanceId }`; return (
@@ -206,4 +207,4 @@ const AddressForm = ( { ); }; -export default withInstanceId( AddressForm ); +export default AddressForm; diff --git a/assets/js/base/components/cart-checkout/address-form/types.ts b/assets/js/base/components/cart-checkout/address-form/types.ts index ece3ef7e79f..b9187efba05 100644 --- a/assets/js/base/components/cart-checkout/address-form/types.ts +++ b/assets/js/base/components/cart-checkout/address-form/types.ts @@ -26,8 +26,6 @@ export type AddressFormFields = { export interface AddressFormProps { // Id for component. id?: string; - // Unique id for form. - instanceId: string; // Type of form (billing or shipping). type?: AddressType; // Array of fields in form. diff --git a/assets/js/blocks/checkout/address-card/index.tsx b/assets/js/blocks/checkout/address-card/index.tsx index 8c7621de93f..363b4f2c5c0 100644 --- a/assets/js/blocks/checkout/address-card/index.tsx +++ b/assets/js/blocks/checkout/address-card/index.tsx @@ -17,10 +17,12 @@ const AddressCard = ( { address, onEdit, target, + showPhoneField, }: { address: CartShippingAddress | CartBillingAddress; onEdit: () => void; target: string; + showPhoneField: boolean; } ): JSX.Element | null => { return (
@@ -44,7 +46,7 @@ const AddressCard = ( { { field } ) ) }
- { address.phone ? ( + { address.phone && showPhoneField ? (
{ + const store = select( CART_STORE_KEY ); + return { + cartDataLoaded: store.hasFinishedResolution( 'getCartData' ), + }; + } ); return ( <> - + { cartDataLoaded ? ( + + ) : null } ); diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx index e4dafcf40a1..4916b90bc6f 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx @@ -97,9 +97,10 @@ const CustomerAddress = ( { onEdit={ () => { setEditing( true ); } } + showPhoneField={ showPhoneField } /> ), - [ billingAddress ] + [ billingAddress, showPhoneField ] ); const renderAddressFormComponent = useCallback( diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx index 3b8dc12871f..1ee5785fba5 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx @@ -19,6 +19,8 @@ import type { AddressField, AddressFields, } from '@woocommerce/settings'; +import { useSelect } from '@wordpress/data'; +import { CART_STORE_KEY } from '@woocommerce/block-data'; /** * Internal dependencies @@ -96,15 +98,24 @@ const Block = ( { ( shippingAddress.first_name || shippingAddress.last_name ) ); + const { cartDataLoaded } = useSelect( ( select ) => { + const store = select( CART_STORE_KEY ); + return { + cartDataLoaded: store.hasFinishedResolution( 'getCartData' ), + }; + } ); + return ( <> - + { cartDataLoaded ? ( + + ) : null } { hasAddress && ( { @@ -103,9 +108,10 @@ const CustomerAddress = ( { onEdit={ () => { setEditing( true ); } } + showPhoneField={ showPhoneField } /> ), - [ shippingAddress ] + [ shippingAddress, showPhoneField ] ); const renderAddressFormComponent = useCallback( @@ -147,9 +153,11 @@ const CustomerAddress = ( { dispatchCheckoutEvent, onChangeAddress, requirePhoneField, + setBillingPhone, setShippingPhone, shippingAddress, showPhoneField, + useShippingAsBilling, ] ); diff --git a/packages/checkout/components/text-input/types.ts b/packages/checkout/components/text-input/types.ts index 263ff18cd4d..24e5d5587d1 100644 --- a/packages/checkout/components/text-input/types.ts +++ b/packages/checkout/components/text-input/types.ts @@ -26,6 +26,8 @@ export interface ValidatedTextInputProps label?: string | undefined; // Field value. value: string; + // Other sibling fields that should be validated together. + values: string[]; // If true, validation errors will be shown. showError?: boolean; // Error message to display alongside the field regardless of validation. diff --git a/packages/checkout/components/text-input/validated-text-input.tsx b/packages/checkout/components/text-input/validated-text-input.tsx index d1d4fd0d575..29295adaaa0 100644 --- a/packages/checkout/components/text-input/validated-text-input.tsx +++ b/packages/checkout/components/text-input/validated-text-input.tsx @@ -51,7 +51,7 @@ const ValidatedTextInput = forwardRef< customFormatter = ( newValue: string ) => newValue, label, validateOnMount = true, - instanceId: preferredInstanceId, + instanceId: preferredInstanceId = '', ...rest }, forwardedRef @@ -80,6 +80,14 @@ const ValidatedTextInput = forwardRef< clearValidationError, } = useDispatch( VALIDATION_STORE_KEY ); + // Ref for validation callback. + const customValidationRef = useRef( customValidation ); + + // Update ref when validation callback changes. + useEffect( () => { + customValidationRef.current = customValidation; + }, [ customValidation ] ); + const { validationError, validationErrorId } = useSelect( ( select ) => { const store = select( VALIDATION_STORE_KEY ); @@ -105,7 +113,7 @@ const ValidatedTextInput = forwardRef< if ( inputObject.checkValidity() && - customValidation( inputObject ) + customValidationRef.current( inputObject ) ) { clearValidationError( errorIdString ); return; @@ -120,13 +128,7 @@ const ValidatedTextInput = forwardRef< }, } ); }, - [ - clearValidationError, - customValidation, - errorIdString, - setValidationErrors, - label, - ] + [ clearValidationError, errorIdString, setValidationErrors, label ] ); // Allows parent to trigger revalidation. @@ -184,6 +186,9 @@ const ValidatedTextInput = forwardRef< if ( ! isPristine ) { return; } + + setIsPristine( false ); + if ( focusOnMount ) { inputRef.current?.focus(); } @@ -192,8 +197,6 @@ const ValidatedTextInput = forwardRef< if ( validateOnMount || ! focusOnMount ) { validateInput( true ); } - - setIsPristine( false ); }, [ validateOnMount, focusOnMount,