Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Ensure validation of fields occurs when collapsing fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejolley committed Oct 10, 2023
1 parent ffaf21f commit 98a5d96
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 56 deletions.
40 changes: 21 additions & 19 deletions assets/js/blocks/checkout/address-wrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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"
>
{ addressCard() }
</CSSTransition>
) }
{ isEditing && (
<CSSTransition
timeout={ 300 }
classNames="address-fade-transition"
>
{ addressForm() }
</CSSTransition>
) }
</TransitionGroup>
<div className={ wrapperClasses }>
<div className="wc-block-components-address-card-wrapper">
{ addressCard() }
</div>
<div className="wc-block-components-address-form-wrapper">
{ addressForm() }
</div>
</div>
);
};

Expand Down
51 changes: 28 additions & 23 deletions assets/js/blocks/checkout/address-wrapper/style.scss
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
.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: opacity 300ms ease-in-out, height 300ms ease-in-out;
width: 100%;
}

&.is-editing {
.wc-block-components-address-form-wrapper {
opacity: 1;
}
.wc-block-components-address-card-wrapper {
opacity: 0;
position: absolute;
top: 0;
}
}

&:not(.is-editing) {
.wc-block-components-address-form-wrapper {
opacity: 0;
height: 0;
}
.wc-block-components-address-card-wrapper {
opacity: 1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
Expand All @@ -93,7 +89,6 @@ const Block = ( {
addressFieldsConfig={ addressFieldsConfig }
showPhoneField={ showPhoneField }
requirePhoneField={ requirePhoneField }
hasAddress={ hasAddress }
forceEditing={ forceEditing }
/>
</WrapperComponent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/**
* 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 {
BillingAddress,
AddressField,
AddressFields,
} from '@woocommerce/settings';
import { useSelect } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand All @@ -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 {
Expand All @@ -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 )[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ const Block = ( {
addressFieldsConfig={ addressFieldsConfig }
showPhoneField={ showPhoneField }
requirePhoneField={ requirePhoneField }
hasAddress={ hasAddress }
/>
</WrapperComponent>
{ hasAddress && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/**
* 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 {
ShippingAddress,
AddressField,
AddressFields,
} from '@woocommerce/settings';
import { useSelect } from '@wordpress/data';
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand All @@ -21,12 +23,10 @@ const CustomerAddress = ( {
addressFieldsConfig,
showPhoneField,
requirePhoneField,
hasAddress,
}: {
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
showPhoneField: boolean;
requirePhoneField: boolean;
hasAddress: boolean;
} ) => {
const {
defaultAddressFields,
Expand All @@ -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 )[];
Expand Down

0 comments on commit 98a5d96

Please sign in to comment.