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:
parent
41724e9400
commit
75bac91787
|
@ -1,12 +1,17 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import './style.scss';
|
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 = ( {
|
export const AddressWrapper = ( {
|
||||||
isEditing = false,
|
isEditing = false,
|
||||||
addressCard,
|
addressCard,
|
||||||
|
@ -16,25 +21,22 @@ export const AddressWrapper = ( {
|
||||||
addressCard: () => JSX.Element;
|
addressCard: () => JSX.Element;
|
||||||
addressForm: () => JSX.Element;
|
addressForm: () => JSX.Element;
|
||||||
} ): JSX.Element | null => {
|
} ): JSX.Element | null => {
|
||||||
|
const wrapperClasses = classnames(
|
||||||
|
'wc-block-components-address-address-wrapper',
|
||||||
|
{
|
||||||
|
'is-editing': isEditing,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TransitionGroup className="address-fade-transition-wrapper">
|
<div className={ wrapperClasses }>
|
||||||
{ ! isEditing && (
|
<div className="wc-block-components-address-card-wrapper">
|
||||||
<CSSTransition
|
{ addressCard() }
|
||||||
timeout={ 300 }
|
</div>
|
||||||
classNames="address-fade-transition"
|
<div className="wc-block-components-address-form-wrapper">
|
||||||
>
|
{ addressForm() }
|
||||||
{ addressCard() }
|
</div>
|
||||||
</CSSTransition>
|
</div>
|
||||||
) }
|
|
||||||
{ isEditing && (
|
|
||||||
<CSSTransition
|
|
||||||
timeout={ 300 }
|
|
||||||
classNames="address-fade-transition"
|
|
||||||
>
|
|
||||||
{ addressForm() }
|
|
||||||
</CSSTransition>
|
|
||||||
) }
|
|
||||||
</TransitionGroup>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
.address-fade-transition-wrapper {
|
.wc-block-components-address-address-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
.address-fade-transition-enter {
|
.wc-block-components-address-card-wrapper,
|
||||||
opacity: 0;
|
.wc-block-components-address-form-wrapper {
|
||||||
}
|
transition: all 300ms ease-in-out;
|
||||||
.address-fade-transition-enter-active {
|
width: 100%;
|
||||||
opacity: 1;
|
}
|
||||||
transition: opacity 300ms ease-in;
|
|
||||||
}
|
&.is-editing {
|
||||||
.address-fade-transition-enter-done {
|
.wc-block-components-address-form-wrapper {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.address-fade-transition-exit {
|
.wc-block-components-address-card-wrapper {
|
||||||
opacity: 1;
|
opacity: 0;
|
||||||
position: absolute;
|
visibility: hidden;
|
||||||
top: 0;
|
position: absolute;
|
||||||
}
|
top: 0;
|
||||||
.address-fade-transition-exit-active {
|
}
|
||||||
opacity: 0;
|
}
|
||||||
transition: opacity 300ms ease-out;
|
|
||||||
}
|
&:not(.is-editing) {
|
||||||
.address-fade-transition-done {
|
.wc-block-components-address-form-wrapper {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.wc-block-components-address-card-wrapper {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,10 +80,6 @@ const Block = ( {
|
||||||
const noticeContext = useBillingAsShipping
|
const noticeContext = useBillingAsShipping
|
||||||
? [ noticeContexts.BILLING_ADDRESS, noticeContexts.SHIPPING_ADDRESS ]
|
? [ noticeContexts.BILLING_ADDRESS, noticeContexts.SHIPPING_ADDRESS ]
|
||||||
: [ noticeContexts.BILLING_ADDRESS ];
|
: [ noticeContexts.BILLING_ADDRESS ];
|
||||||
const hasAddress = !! (
|
|
||||||
billingAddress.address_1 &&
|
|
||||||
( billingAddress.first_name || billingAddress.last_name )
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -93,7 +89,6 @@ const Block = ( {
|
||||||
addressFieldsConfig={ addressFieldsConfig }
|
addressFieldsConfig={ addressFieldsConfig }
|
||||||
showPhoneField={ showPhoneField }
|
showPhoneField={ showPhoneField }
|
||||||
requirePhoneField={ requirePhoneField }
|
requirePhoneField={ requirePhoneField }
|
||||||
hasAddress={ hasAddress }
|
|
||||||
forceEditing={ forceEditing }
|
forceEditing={ forceEditing }
|
||||||
/>
|
/>
|
||||||
</WrapperComponent>
|
</WrapperComponent>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { useState, useCallback } from '@wordpress/element';
|
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||||
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
||||||
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
||||||
import type {
|
import type {
|
||||||
|
@ -9,6 +9,8 @@ import type {
|
||||||
AddressField,
|
AddressField,
|
||||||
AddressFields,
|
AddressFields,
|
||||||
} from '@woocommerce/settings';
|
} from '@woocommerce/settings';
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,13 +23,11 @@ const CustomerAddress = ( {
|
||||||
addressFieldsConfig,
|
addressFieldsConfig,
|
||||||
showPhoneField,
|
showPhoneField,
|
||||||
requirePhoneField,
|
requirePhoneField,
|
||||||
hasAddress,
|
|
||||||
forceEditing = false,
|
forceEditing = false,
|
||||||
}: {
|
}: {
|
||||||
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
|
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
|
||||||
showPhoneField: boolean;
|
showPhoneField: boolean;
|
||||||
requirePhoneField: boolean;
|
requirePhoneField: boolean;
|
||||||
hasAddress: boolean;
|
|
||||||
forceEditing?: boolean;
|
forceEditing?: boolean;
|
||||||
} ) => {
|
} ) => {
|
||||||
const {
|
const {
|
||||||
|
@ -40,8 +40,34 @@ const CustomerAddress = ( {
|
||||||
useBillingAsShipping,
|
useBillingAsShipping,
|
||||||
} = useCheckoutAddress();
|
} = useCheckoutAddress();
|
||||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||||
|
const hasAddress = !! (
|
||||||
|
billingAddress.address_1 &&
|
||||||
|
( billingAddress.first_name || billingAddress.last_name )
|
||||||
|
);
|
||||||
const [ editing, setEditing ] = useState( ! hasAddress || forceEditing );
|
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(
|
const addressFieldKeys = Object.keys(
|
||||||
defaultAddressFields
|
defaultAddressFields
|
||||||
) as ( keyof AddressFields )[];
|
) as ( keyof AddressFields )[];
|
||||||
|
|
|
@ -104,7 +104,6 @@ const Block = ( {
|
||||||
addressFieldsConfig={ addressFieldsConfig }
|
addressFieldsConfig={ addressFieldsConfig }
|
||||||
showPhoneField={ showPhoneField }
|
showPhoneField={ showPhoneField }
|
||||||
requirePhoneField={ requirePhoneField }
|
requirePhoneField={ requirePhoneField }
|
||||||
hasAddress={ hasAddress }
|
|
||||||
/>
|
/>
|
||||||
</WrapperComponent>
|
</WrapperComponent>
|
||||||
{ hasAddress && (
|
{ hasAddress && (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { useState, useCallback } from '@wordpress/element';
|
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||||
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
||||||
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
||||||
import type {
|
import type {
|
||||||
|
@ -9,6 +9,8 @@ import type {
|
||||||
AddressField,
|
AddressField,
|
||||||
AddressFields,
|
AddressFields,
|
||||||
} from '@woocommerce/settings';
|
} from '@woocommerce/settings';
|
||||||
|
import { useSelect } from '@wordpress/data';
|
||||||
|
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,12 +23,10 @@ const CustomerAddress = ( {
|
||||||
addressFieldsConfig,
|
addressFieldsConfig,
|
||||||
showPhoneField,
|
showPhoneField,
|
||||||
requirePhoneField,
|
requirePhoneField,
|
||||||
hasAddress,
|
|
||||||
}: {
|
}: {
|
||||||
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
|
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
|
||||||
showPhoneField: boolean;
|
showPhoneField: boolean;
|
||||||
requirePhoneField: boolean;
|
requirePhoneField: boolean;
|
||||||
hasAddress: boolean;
|
|
||||||
} ) => {
|
} ) => {
|
||||||
const {
|
const {
|
||||||
defaultAddressFields,
|
defaultAddressFields,
|
||||||
|
@ -37,8 +37,34 @@ const CustomerAddress = ( {
|
||||||
useShippingAsBilling,
|
useShippingAsBilling,
|
||||||
} = useCheckoutAddress();
|
} = useCheckoutAddress();
|
||||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||||
|
const hasAddress = !! (
|
||||||
|
shippingAddress.address_1 &&
|
||||||
|
( shippingAddress.first_name || shippingAddress.last_name )
|
||||||
|
);
|
||||||
const [ editing, setEditing ] = useState( ! hasAddress );
|
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(
|
const addressFieldKeys = Object.keys(
|
||||||
defaultAddressFields
|
defaultAddressFields
|
||||||
) as ( keyof AddressFields )[];
|
) as ( keyof AddressFields )[];
|
||||||
|
|
|
@ -126,34 +126,22 @@ export class CheckoutPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async editBillingDetails() {
|
async editBillingDetails() {
|
||||||
const editButton = await this.page
|
const editButton = this.page.locator(
|
||||||
.locator(
|
'.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
|
||||||
'.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
|
);
|
||||||
)
|
|
||||||
.isVisible();
|
|
||||||
|
|
||||||
if ( editButton ) {
|
if ( await editButton.isVisible() ) {
|
||||||
await this.page
|
await editButton.click();
|
||||||
.locator(
|
|
||||||
'.wc-block-checkout__billing-fields .wc-block-components-address-card__edit'
|
|
||||||
)
|
|
||||||
.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async editShippingDetails() {
|
async editShippingDetails() {
|
||||||
const editButton = await this.page
|
const editButton = this.page.locator(
|
||||||
.locator(
|
'.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
|
||||||
'.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
|
);
|
||||||
)
|
|
||||||
.isVisible();
|
|
||||||
|
|
||||||
if ( editButton ) {
|
if ( await editButton.isVisible() ) {
|
||||||
await this.page
|
await editButton.click();
|
||||||
.locator(
|
|
||||||
'.wc-block-checkout__shipping-fields .wc-block-components-address-card__edit'
|
|
||||||
)
|
|
||||||
.click();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue