Ensure validation of fields occurs when collapsing fields (https://github.com/woocommerce/woocommerce-blocks/pull/11199)

* Ensure validation of fields occurs when collapsing fields

* update click for edit button

* turn off pointer events when hidden

* Add visibility rule
This commit is contained in:
Mike Jolley 2023-10-10 22:07:58 +01:00 committed by GitHub
parent 41724e9400
commit 75bac91787
7 changed files with 121 additions and 78 deletions

View File

@ -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 (
<TransitionGroup className="address-fade-transition-wrapper">
{ ! isEditing && (
<CSSTransition
timeout={ 300 }
classNames="address-fade-transition"
>
<div className={ wrapperClasses }>
<div className="wc-block-components-address-card-wrapper">
{ addressCard() }
</CSSTransition>
) }
{ isEditing && (
<CSSTransition
timeout={ 300 }
classNames="address-fade-transition"
>
</div>
<div className="wc-block-components-address-form-wrapper">
{ addressForm() }
</CSSTransition>
) }
</TransitionGroup>
</div>
</div>
);
};

View File

@ -1,25 +1,32 @@
.address-fade-transition-wrapper {
.wc-block-components-address-address-wrapper {
position: relative;
}
.address-fade-transition-enter {
.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;
}
.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;
visibility: hidden;
position: absolute;
top: 0;
}
.address-fade-transition-exit-active {
opacity: 0;
transition: opacity 300ms ease-out;
}
.address-fade-transition-done {
}
}
&:not(.is-editing) {
.wc-block-components-address-form-wrapper {
opacity: 0;
visibility: hidden;
height: 0;
}
.wc-block-components-address-card-wrapper {
opacity: 1;
}
}
}

View File

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

View File

@ -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 )[];

View File

@ -104,7 +104,6 @@ const Block = ( {
addressFieldsConfig={ addressFieldsConfig }
showPhoneField={ showPhoneField }
requirePhoneField={ requirePhoneField }
hasAddress={ hasAddress }
/>
</WrapperComponent>
{ hasAddress && (

View File

@ -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 )[];

View File

@ -126,34 +126,22 @@ export class CheckoutPage {
}
async editBillingDetails() {
const editButton = await this.page
.locator(
const editButton = this.page.locator(
'.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
)
.isVisible();
);
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(
const editButton = this.page.locator(
'.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
)
.isVisible();
);
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();
}
}