diff --git a/assets/js/blocks/checkout/address-wrapper/index.tsx b/assets/js/blocks/checkout/address-wrapper/index.tsx
index b6182fa0141..bd9f5349c79 100644
--- a/assets/js/blocks/checkout/address-wrapper/index.tsx
+++ b/assets/js/blocks/checkout/address-wrapper/index.tsx
@@ -1,12 +1,17 @@
/**
* External dependencies
*/
-import { CSSTransition, TransitionGroup } from 'react-transition-group';
+import classnames from 'classnames';
+
/**
* Internal dependencies
*/
import './style.scss';
+/**
+ * Wrapper for address fields which handles the edit/preview transition. Form fields are always rendered so that
+ * validation can occur.
+ */
export const AddressWrapper = ( {
isEditing = false,
addressCard,
@@ -16,25 +21,22 @@ export const AddressWrapper = ( {
addressCard: () => JSX.Element;
addressForm: () => JSX.Element;
} ): JSX.Element | null => {
+ const wrapperClasses = classnames(
+ 'wc-block-components-address-address-wrapper',
+ {
+ 'is-editing': isEditing,
+ }
+ );
+
return (
-
- { ! isEditing && (
-
- { addressCard() }
-
- ) }
- { isEditing && (
-
- { addressForm() }
-
- ) }
-
+
+
+ { addressCard() }
+
+
+ { addressForm() }
+
+
);
};
diff --git a/assets/js/blocks/checkout/address-wrapper/style.scss b/assets/js/blocks/checkout/address-wrapper/style.scss
index 6215de67970..3f18307a240 100644
--- a/assets/js/blocks/checkout/address-wrapper/style.scss
+++ b/assets/js/blocks/checkout/address-wrapper/style.scss
@@ -1,25 +1,32 @@
-.address-fade-transition-wrapper {
+.wc-block-components-address-address-wrapper {
position: relative;
-}
-.address-fade-transition-enter {
- opacity: 0;
-}
-.address-fade-transition-enter-active {
- opacity: 1;
- transition: opacity 300ms ease-in;
-}
-.address-fade-transition-enter-done {
- opacity: 1;
-}
-.address-fade-transition-exit {
- opacity: 1;
- position: absolute;
- top: 0;
-}
-.address-fade-transition-exit-active {
- opacity: 0;
- transition: opacity 300ms ease-out;
-}
-.address-fade-transition-done {
- opacity: 0;
+
+ .wc-block-components-address-card-wrapper,
+ .wc-block-components-address-form-wrapper {
+ transition: all 300ms ease-in-out;
+ width: 100%;
+ }
+
+ &.is-editing {
+ .wc-block-components-address-form-wrapper {
+ opacity: 1;
+ }
+ .wc-block-components-address-card-wrapper {
+ opacity: 0;
+ visibility: hidden;
+ position: absolute;
+ top: 0;
+ }
+ }
+
+ &:not(.is-editing) {
+ .wc-block-components-address-form-wrapper {
+ opacity: 0;
+ visibility: hidden;
+ height: 0;
+ }
+ .wc-block-components-address-card-wrapper {
+ opacity: 1;
+ }
+ }
}
diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
index b1f1dcdc2f4..ae722ab9228 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx
@@ -80,10 +80,6 @@ const Block = ( {
const noticeContext = useBillingAsShipping
? [ noticeContexts.BILLING_ADDRESS, noticeContexts.SHIPPING_ADDRESS ]
: [ noticeContexts.BILLING_ADDRESS ];
- const hasAddress = !! (
- billingAddress.address_1 &&
- ( billingAddress.first_name || billingAddress.last_name )
- );
return (
<>
@@ -93,7 +89,6 @@ const Block = ( {
addressFieldsConfig={ addressFieldsConfig }
showPhoneField={ showPhoneField }
requirePhoneField={ requirePhoneField }
- hasAddress={ hasAddress }
forceEditing={ forceEditing }
/>
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 45bc172b382..e4dafcf40a1 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
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { useState, useCallback } from '@wordpress/element';
+import { useState, useCallback, useEffect } from '@wordpress/element';
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
import type {
@@ -9,6 +9,8 @@ import type {
AddressField,
AddressFields,
} from '@woocommerce/settings';
+import { useSelect } from '@wordpress/data';
+import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
/**
* Internal dependencies
@@ -21,13 +23,11 @@ const CustomerAddress = ( {
addressFieldsConfig,
showPhoneField,
requirePhoneField,
- hasAddress,
forceEditing = false,
}: {
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
showPhoneField: boolean;
requirePhoneField: boolean;
- hasAddress: boolean;
forceEditing?: boolean;
} ) => {
const {
@@ -40,8 +40,34 @@ const CustomerAddress = ( {
useBillingAsShipping,
} = useCheckoutAddress();
const { dispatchCheckoutEvent } = useStoreEvents();
-
+ const hasAddress = !! (
+ billingAddress.address_1 &&
+ ( billingAddress.first_name || billingAddress.last_name )
+ );
const [ editing, setEditing ] = useState( ! hasAddress || forceEditing );
+
+ // Forces editing state if store has errors.
+ const { hasValidationErrors, invalidProps } = useSelect( ( select ) => {
+ const store = select( VALIDATION_STORE_KEY );
+ return {
+ hasValidationErrors: store.hasValidationErrors(),
+ invalidProps: Object.keys( billingAddress )
+ .filter( ( key ) => {
+ return (
+ store.getValidationError( 'billing_' + key ) !==
+ undefined
+ );
+ } )
+ .filter( Boolean ),
+ };
+ } );
+
+ useEffect( () => {
+ if ( invalidProps.length > 0 && editing === false ) {
+ setEditing( true );
+ }
+ }, [ editing, hasValidationErrors, invalidProps.length ] );
+
const addressFieldKeys = Object.keys(
defaultAddressFields
) as ( keyof AddressFields )[];
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 077133c5c23..3b8dc12871f 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
@@ -104,7 +104,6 @@ const Block = ( {
addressFieldsConfig={ addressFieldsConfig }
showPhoneField={ showPhoneField }
requirePhoneField={ requirePhoneField }
- hasAddress={ hasAddress }
/>
{ hasAddress && (
diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx
index 72431db75fa..f041c54d369 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { useState, useCallback } from '@wordpress/element';
+import { useState, useCallback, useEffect } from '@wordpress/element';
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
import type {
@@ -9,6 +9,8 @@ import type {
AddressField,
AddressFields,
} from '@woocommerce/settings';
+import { useSelect } from '@wordpress/data';
+import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
/**
* Internal dependencies
@@ -21,12 +23,10 @@ const CustomerAddress = ( {
addressFieldsConfig,
showPhoneField,
requirePhoneField,
- hasAddress,
}: {
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
showPhoneField: boolean;
requirePhoneField: boolean;
- hasAddress: boolean;
} ) => {
const {
defaultAddressFields,
@@ -37,8 +37,34 @@ const CustomerAddress = ( {
useShippingAsBilling,
} = useCheckoutAddress();
const { dispatchCheckoutEvent } = useStoreEvents();
-
+ const hasAddress = !! (
+ shippingAddress.address_1 &&
+ ( shippingAddress.first_name || shippingAddress.last_name )
+ );
const [ editing, setEditing ] = useState( ! hasAddress );
+
+ // Forces editing state if store has errors.
+ const { hasValidationErrors, invalidProps } = useSelect( ( select ) => {
+ const store = select( VALIDATION_STORE_KEY );
+ return {
+ hasValidationErrors: store.hasValidationErrors(),
+ invalidProps: Object.keys( shippingAddress )
+ .filter( ( key ) => {
+ return (
+ store.getValidationError( 'shipping_' + key ) !==
+ undefined
+ );
+ } )
+ .filter( Boolean ),
+ };
+ } );
+
+ useEffect( () => {
+ if ( invalidProps.length > 0 && editing === false ) {
+ setEditing( true );
+ }
+ }, [ editing, hasValidationErrors, invalidProps.length ] );
+
const addressFieldKeys = Object.keys(
defaultAddressFields
) as ( keyof AddressFields )[];
diff --git a/tests/e2e/tests/checkout/checkout.page.ts b/tests/e2e/tests/checkout/checkout.page.ts
index eaa337e9719..52990872a41 100644
--- a/tests/e2e/tests/checkout/checkout.page.ts
+++ b/tests/e2e/tests/checkout/checkout.page.ts
@@ -126,34 +126,22 @@ export class CheckoutPage {
}
async editBillingDetails() {
- const editButton = await this.page
- .locator(
- '.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
- )
- .isVisible();
+ const editButton = this.page.locator(
+ '.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
+ );
- if ( editButton ) {
- await this.page
- .locator(
- '.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
- )
- .click();
+ if ( await editButton.isVisible() ) {
+ await editButton.click();
}
}
async editShippingDetails() {
- const editButton = await this.page
- .locator(
- '.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
- )
- .isVisible();
+ const editButton = this.page.locator(
+ '.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
+ );
- if ( editButton ) {
- await this.page
- .locator(
- '.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
- )
- .click();
+ if ( await editButton.isVisible() ) {
+ await editButton.click();
}
}