Support controlling address card state from data store in Checkout block (#51386)
* fix line height * Support controlling address card from data store * expand billing address on unsync * Add changefile(s) from automation for the following project(s): woocommerce-blocks * disable linter rule * remove empty section * remove used vars --------- Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
parent
e354295387
commit
66523872f3
|
@ -25,7 +25,11 @@ interface CheckoutAddress {
|
|||
setBillingAddress: ( data: Partial< BillingAddress > ) => void;
|
||||
setEmail: ( value: string ) => void;
|
||||
useShippingAsBilling: boolean;
|
||||
editingBillingAddress: boolean;
|
||||
editingShippingAddress: boolean;
|
||||
setUseShippingAsBilling: ( useShippingAsBilling: boolean ) => void;
|
||||
setEditingBillingAddress: ( isEditing: boolean ) => void;
|
||||
setEditingShippingAddress: ( isEditing: boolean ) => void;
|
||||
defaultFields: AddressFields;
|
||||
showShippingFields: boolean;
|
||||
showBillingFields: boolean;
|
||||
|
@ -40,15 +44,25 @@ interface CheckoutAddress {
|
|||
*/
|
||||
export const useCheckoutAddress = (): CheckoutAddress => {
|
||||
const { needsShipping } = useShippingData();
|
||||
const { useShippingAsBilling, prefersCollection } = useSelect(
|
||||
( select ) => ( {
|
||||
useShippingAsBilling:
|
||||
select( CHECKOUT_STORE_KEY ).getUseShippingAsBilling(),
|
||||
prefersCollection: select( CHECKOUT_STORE_KEY ).prefersCollection(),
|
||||
} )
|
||||
);
|
||||
const { __internalSetUseShippingAsBilling } =
|
||||
useDispatch( CHECKOUT_STORE_KEY );
|
||||
const {
|
||||
useShippingAsBilling,
|
||||
prefersCollection,
|
||||
editingBillingAddress,
|
||||
editingShippingAddress,
|
||||
} = useSelect( ( select ) => ( {
|
||||
useShippingAsBilling:
|
||||
select( CHECKOUT_STORE_KEY ).getUseShippingAsBilling(),
|
||||
prefersCollection: select( CHECKOUT_STORE_KEY ).prefersCollection(),
|
||||
editingBillingAddress:
|
||||
select( CHECKOUT_STORE_KEY ).getEditingBillingAddress(),
|
||||
editingShippingAddress:
|
||||
select( CHECKOUT_STORE_KEY ).getEditingShippingAddress(),
|
||||
} ) );
|
||||
const {
|
||||
__internalSetUseShippingAsBilling,
|
||||
setEditingBillingAddress,
|
||||
setEditingShippingAddress,
|
||||
} = useDispatch( CHECKOUT_STORE_KEY );
|
||||
const {
|
||||
billingAddress,
|
||||
setBillingAddress,
|
||||
|
@ -77,6 +91,10 @@ export const useCheckoutAddress = (): CheckoutAddress => {
|
|||
defaultFields,
|
||||
useShippingAsBilling,
|
||||
setUseShippingAsBilling: __internalSetUseShippingAsBilling,
|
||||
editingBillingAddress,
|
||||
editingShippingAddress,
|
||||
setEditingBillingAddress,
|
||||
setEditingShippingAddress,
|
||||
needsShipping,
|
||||
showShippingFields:
|
||||
! forcedBillingAddress && needsShipping && ! prefersCollection,
|
||||
|
|
|
@ -7,14 +7,12 @@ import {
|
|||
useCheckoutAddress,
|
||||
useEditorContext,
|
||||
noticeContexts,
|
||||
useShippingData,
|
||||
} from '@woocommerce/base-context';
|
||||
import Noninteractive from '@woocommerce/base-components/noninteractive';
|
||||
import type { ShippingAddress, FormFieldsConfig } from '@woocommerce/settings';
|
||||
import { StoreNoticesContainer } from '@woocommerce/blocks-components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { CART_STORE_KEY } from '@woocommerce/block-data';
|
||||
import isShallowEqual from '@wordpress/is-shallow-equal';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -36,14 +34,9 @@ const Block = ( {
|
|||
showPhoneField: boolean;
|
||||
requirePhoneField: boolean;
|
||||
} ): JSX.Element => {
|
||||
const {
|
||||
shippingAddress,
|
||||
billingAddress,
|
||||
setShippingAddress,
|
||||
useBillingAsShipping,
|
||||
} = useCheckoutAddress();
|
||||
const { billingAddress, setShippingAddress, useBillingAsShipping } =
|
||||
useCheckoutAddress();
|
||||
const { isEditor } = useEditorContext();
|
||||
const { needsShipping } = useShippingData();
|
||||
|
||||
// Syncs shipping address with billing address if "Force shipping to the customer billing address" is enabled.
|
||||
useEffectOnce( () => {
|
||||
|
@ -101,19 +94,6 @@ const Block = ( {
|
|||
};
|
||||
} );
|
||||
|
||||
// Default editing state for CustomerAddress component comes from the current address and whether or not we're in the editor.
|
||||
const hasAddress = !! (
|
||||
billingAddress.address_1 &&
|
||||
( billingAddress.first_name || billingAddress.last_name )
|
||||
);
|
||||
const { email, ...billingAddressWithoutEmail } = billingAddress;
|
||||
const billingMatchesShipping = isShallowEqual(
|
||||
billingAddressWithoutEmail,
|
||||
shippingAddress
|
||||
);
|
||||
const defaultEditingAddress =
|
||||
isEditor || ! hasAddress || ( needsShipping && billingMatchesShipping );
|
||||
|
||||
return (
|
||||
<>
|
||||
<StoreNoticesContainer context={ noticeContext } />
|
||||
|
@ -121,7 +101,6 @@ const Block = ( {
|
|||
{ cartDataLoaded ? (
|
||||
<CustomerAddress
|
||||
addressFieldsConfig={ addressFieldsConfig }
|
||||
defaultEditing={ defaultEditingAddress }
|
||||
/>
|
||||
) : null }
|
||||
</WrapperComponent>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||
import { useCallback, useEffect } from '@wordpress/element';
|
||||
import { Form } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
||||
import type {
|
||||
|
@ -20,19 +20,18 @@ import AddressCard from '../../address-card';
|
|||
|
||||
const CustomerAddress = ( {
|
||||
addressFieldsConfig,
|
||||
defaultEditing = false,
|
||||
}: {
|
||||
addressFieldsConfig: FormFieldsConfig;
|
||||
defaultEditing?: boolean;
|
||||
} ) => {
|
||||
const {
|
||||
billingAddress,
|
||||
setShippingAddress,
|
||||
setBillingAddress,
|
||||
useBillingAsShipping,
|
||||
editingBillingAddress: editing,
|
||||
setEditingBillingAddress: setEditing,
|
||||
} = useCheckoutAddress();
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
const [ editing, setEditing ] = useState( defaultEditing );
|
||||
|
||||
// Forces editing state if store has errors.
|
||||
const { hasValidationErrors, invalidProps } = useSelect( ( select ) => {
|
||||
|
@ -55,7 +54,7 @@ const CustomerAddress = ( {
|
|||
if ( invalidProps.length > 0 && editing === false ) {
|
||||
setEditing( true );
|
||||
}
|
||||
}, [ editing, hasValidationErrors, invalidProps.length ] );
|
||||
}, [ editing, hasValidationErrors, invalidProps.length, setEditing ] );
|
||||
|
||||
const onChangeAddress = useCallback(
|
||||
( values: AddressFormValues ) => {
|
||||
|
@ -86,7 +85,7 @@ const CustomerAddress = ( {
|
|||
isExpanded={ editing }
|
||||
/>
|
||||
),
|
||||
[ billingAddress, addressFieldsConfig, editing ]
|
||||
[ billingAddress, addressFieldsConfig, editing, setEditing ]
|
||||
);
|
||||
|
||||
const renderAddressFormComponent = useCallback(
|
||||
|
|
|
@ -47,6 +47,7 @@ const Block = ( {
|
|||
billingAddress,
|
||||
useShippingAsBilling,
|
||||
setUseShippingAsBilling,
|
||||
setEditingBillingAddress,
|
||||
} = useCheckoutAddress();
|
||||
const { isEditor } = useEditorContext();
|
||||
const isGuest = getSetting( 'currentUserId' ) === 0;
|
||||
|
@ -116,10 +117,6 @@ const Block = ( {
|
|||
const noticeContext = useShippingAsBilling
|
||||
? [ noticeContexts.SHIPPING_ADDRESS, noticeContexts.BILLING_ADDRESS ]
|
||||
: [ noticeContexts.SHIPPING_ADDRESS ];
|
||||
const hasAddress = !! (
|
||||
shippingAddress.address_1 &&
|
||||
( shippingAddress.first_name || shippingAddress.last_name )
|
||||
);
|
||||
|
||||
const { cartDataLoaded } = useSelect( ( select ) => {
|
||||
const store = select( CART_STORE_KEY );
|
||||
|
@ -128,9 +125,6 @@ const Block = ( {
|
|||
};
|
||||
} );
|
||||
|
||||
// Default editing state for CustomerAddress component comes from the current address and whether or not we're in the editor.
|
||||
const defaultEditingAddress = isEditor || ! hasAddress;
|
||||
|
||||
return (
|
||||
<>
|
||||
<StoreNoticesContainer context={ noticeContext } />
|
||||
|
@ -138,7 +132,6 @@ const Block = ( {
|
|||
{ cartDataLoaded ? (
|
||||
<CustomerAddress
|
||||
addressFieldsConfig={ addressFieldsConfig }
|
||||
defaultEditing={ defaultEditingAddress }
|
||||
/>
|
||||
) : null }
|
||||
</WrapperComponent>
|
||||
|
@ -151,6 +144,7 @@ const Block = ( {
|
|||
if ( checked ) {
|
||||
syncBillingWithShipping();
|
||||
} else {
|
||||
setEditingBillingAddress( true );
|
||||
clearBillingAddress( billingAddress );
|
||||
}
|
||||
} }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||
import { useCallback, useEffect } from '@wordpress/element';
|
||||
import { Form } from '@woocommerce/base-components/cart-checkout';
|
||||
import { useCheckoutAddress, useStoreEvents } from '@woocommerce/base-context';
|
||||
import type {
|
||||
|
@ -20,19 +20,18 @@ import AddressCard from '../../address-card';
|
|||
|
||||
const CustomerAddress = ( {
|
||||
addressFieldsConfig,
|
||||
defaultEditing = false,
|
||||
}: {
|
||||
addressFieldsConfig: FormFieldsConfig;
|
||||
defaultEditing?: boolean;
|
||||
} ) => {
|
||||
const {
|
||||
shippingAddress,
|
||||
setShippingAddress,
|
||||
setBillingAddress,
|
||||
useShippingAsBilling,
|
||||
editingShippingAddress: editing,
|
||||
setEditingShippingAddress: setEditing,
|
||||
} = useCheckoutAddress();
|
||||
const { dispatchCheckoutEvent } = useStoreEvents();
|
||||
const [ editing, setEditing ] = useState( defaultEditing );
|
||||
|
||||
// Forces editing state if store has errors.
|
||||
const { hasValidationErrors, invalidProps } = useSelect( ( select ) => {
|
||||
|
@ -54,7 +53,7 @@ const CustomerAddress = ( {
|
|||
if ( invalidProps.length > 0 && editing === false ) {
|
||||
setEditing( true );
|
||||
}
|
||||
}, [ editing, hasValidationErrors, invalidProps.length ] );
|
||||
}, [ editing, hasValidationErrors, invalidProps.length, setEditing ] );
|
||||
|
||||
const onChangeAddress = useCallback(
|
||||
( values: AddressFormValues ) => {
|
||||
|
@ -85,7 +84,7 @@ const CustomerAddress = ( {
|
|||
isExpanded={ editing }
|
||||
/>
|
||||
),
|
||||
[ shippingAddress, addressFieldsConfig, editing ]
|
||||
[ shippingAddress, addressFieldsConfig, editing, setEditing ]
|
||||
);
|
||||
|
||||
const renderAddressFormComponent = useCallback(
|
||||
|
|
|
@ -17,4 +17,6 @@ export const ACTION_TYPES = {
|
|||
SET_REDIRECT_URL: 'SET_REDIRECT_URL',
|
||||
SET_SHOULD_CREATE_ACCOUNT: 'SET_SHOULD_CREATE_ACCOUNT',
|
||||
SET_USE_SHIPPING_AS_BILLING: 'SET_USE_SHIPPING_AS_BILLING',
|
||||
SET_EDITING_BILLING_ADDRESS: 'SET_EDITING_BILLING_ADDRESS',
|
||||
SET_EDITING_SHIPPING_ADDRESS: 'SET_EDITING_SHIPPING_ADDRESS',
|
||||
} as const;
|
||||
|
|
|
@ -118,6 +118,30 @@ export const __internalSetUseShippingAsBilling = (
|
|||
useShippingAsBilling,
|
||||
} );
|
||||
|
||||
/**
|
||||
* Set whether the billing address is being edited
|
||||
*
|
||||
* @param isEditing True if the billing address is being edited, false otherwise
|
||||
*/
|
||||
export const setEditingBillingAddress = ( isEditing: boolean ) => {
|
||||
return {
|
||||
type: types.SET_EDITING_BILLING_ADDRESS,
|
||||
isEditing,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the shipping address is being edited
|
||||
*
|
||||
* @param isEditing True if the shipping address is being edited, false otherwise
|
||||
*/
|
||||
export const setEditingShippingAddress = ( isEditing: boolean ) => {
|
||||
return {
|
||||
type: types.SET_EDITING_SHIPPING_ADDRESS,
|
||||
isEditing,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether an account should be created for the user while checking out
|
||||
*
|
||||
|
@ -182,6 +206,8 @@ export type CheckoutAction =
|
|||
| typeof __internalSetCustomerId
|
||||
| typeof __internalSetCustomerPassword
|
||||
| typeof __internalSetUseShippingAsBilling
|
||||
| typeof setEditingBillingAddress
|
||||
| typeof setEditingShippingAddress
|
||||
| typeof __internalSetShouldCreateAccount
|
||||
| typeof __internalSetOrderNotes
|
||||
| typeof setPrefersCollection
|
||||
|
|
|
@ -23,8 +23,28 @@ export type CheckoutState = {
|
|||
shouldCreateAccount: boolean; // Should a user account be created?
|
||||
status: STATUS; // Status of the checkout
|
||||
useShippingAsBilling: boolean; // Should the billing form be hidden and inherit the shipping address?
|
||||
editingBillingAddress: boolean; // Is the billing address being edited?
|
||||
editingShippingAddress: boolean; // Is the shipping address being edited?
|
||||
};
|
||||
|
||||
// Default editing state for CustomerAddress component comes from the current address and whether or not we're in the editor.
|
||||
const hasBillingAddress = !! (
|
||||
checkoutData.billing_address.address_1 &&
|
||||
( checkoutData.billing_address.first_name ||
|
||||
checkoutData.billing_address.last_name )
|
||||
);
|
||||
|
||||
const hasShippingAddress = !! (
|
||||
checkoutData.shipping_address.address_1 &&
|
||||
( checkoutData.shipping_address.first_name ||
|
||||
checkoutData.shipping_address.last_name )
|
||||
);
|
||||
|
||||
const billingMatchesShipping = isSameAddress(
|
||||
checkoutData.billing_address,
|
||||
checkoutData.shipping_address
|
||||
);
|
||||
|
||||
export const defaultState: CheckoutState = {
|
||||
additionalFields: checkoutData.additional_fields || {},
|
||||
calculatingCount: 0,
|
||||
|
@ -38,8 +58,7 @@ export const defaultState: CheckoutState = {
|
|||
redirectUrl: '',
|
||||
shouldCreateAccount: false,
|
||||
status: STATUS.IDLE,
|
||||
useShippingAsBilling: isSameAddress(
|
||||
checkoutData.billing_address,
|
||||
checkoutData.shipping_address
|
||||
),
|
||||
useShippingAsBilling: billingMatchesShipping,
|
||||
editingBillingAddress: ! hasBillingAddress,
|
||||
editingShippingAddress: ! hasShippingAddress,
|
||||
};
|
||||
|
|
|
@ -130,6 +130,20 @@ const reducer = ( state = defaultState, action: CheckoutAction ) => {
|
|||
}
|
||||
break;
|
||||
|
||||
case types.SET_EDITING_BILLING_ADDRESS:
|
||||
newState = {
|
||||
...state,
|
||||
editingBillingAddress: action.isEditing,
|
||||
};
|
||||
break;
|
||||
|
||||
case types.SET_EDITING_SHIPPING_ADDRESS:
|
||||
newState = {
|
||||
...state,
|
||||
editingShippingAddress: action.isEditing,
|
||||
};
|
||||
break;
|
||||
|
||||
case types.SET_SHOULD_CREATE_ACCOUNT:
|
||||
if (
|
||||
action.shouldCreateAccount !== undefined &&
|
||||
|
|
|
@ -36,6 +36,14 @@ export const getUseShippingAsBilling = ( state: CheckoutState ) => {
|
|||
return state.useShippingAsBilling;
|
||||
};
|
||||
|
||||
export const getEditingBillingAddress = ( state: CheckoutState ) => {
|
||||
return state.editingBillingAddress;
|
||||
};
|
||||
|
||||
export const getEditingShippingAddress = ( state: CheckoutState ) => {
|
||||
return state.editingShippingAddress;
|
||||
};
|
||||
|
||||
export const getExtensionData = ( state: CheckoutState ) => {
|
||||
return state.extensionData;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Checkout Store (`wc/store/checkout`) <!-- omit in toc -->
|
||||
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
> 💡 What's the difference between the Cart Store and the Checkout Store?
|
||||
>
|
||||
> The **Cart Store (`wc/store/cart`)** manages and retrieves data about the shopping cart, including items, customer data, and interactions like coupons.
|
||||
|
@ -173,6 +175,36 @@ const store = select( CHECKOUT_STORE_KEY );
|
|||
const useShippingAsBilling = store.getUseShippingAsBilling();
|
||||
```
|
||||
|
||||
### getEditingBillingAddress
|
||||
|
||||
Returns true if the billing address is being edited.
|
||||
|
||||
#### _Returns_ <!-- omit in toc -->
|
||||
|
||||
- `boolean`: True if the billing address is being edited.
|
||||
|
||||
#### _Example_ <!-- omit in toc -->
|
||||
|
||||
```js
|
||||
const store = select( CHECKOUT_STORE_KEY );
|
||||
const editingBillingAddress = store.getEditingBillingAddress();
|
||||
```
|
||||
|
||||
### getEditingShippingAddress
|
||||
|
||||
Returns true if the shipping address is being edited.
|
||||
|
||||
#### _Returns_ <!-- omit in toc -->
|
||||
|
||||
- `boolean`: True if the shipping address is being edited.
|
||||
|
||||
#### _Example_ <!-- omit in toc -->
|
||||
|
||||
```js
|
||||
const store = select( CHECKOUT_STORE_KEY );
|
||||
const editingShippingAddress = store.getEditingShippingAddress();
|
||||
```
|
||||
|
||||
### hasError
|
||||
|
||||
Returns true if an error occurred, and false otherwise.
|
||||
|
@ -293,7 +325,6 @@ const store = select( CHECKOUT_STORE_KEY );
|
|||
const isCalculating = store.isCalculating();
|
||||
```
|
||||
|
||||
|
||||
### prefersCollection
|
||||
|
||||
Returns true if the customer prefers to collect their order, and false otherwise.
|
||||
|
@ -326,6 +357,36 @@ const store = dispatch( CHECKOUT_STORE_KEY );
|
|||
store.setPrefersCollection( true );
|
||||
```
|
||||
|
||||
### setEditingBillingAddress
|
||||
|
||||
Set the billing address to editing state or collapsed state. Note that if the address has invalid fields, it will not be set to collapsed state.
|
||||
|
||||
#### _Parameters_ <!-- omit in toc -->
|
||||
|
||||
- _isEditing_ `boolean`: True to set the billing address to editing state, false to set it to collapsed state.
|
||||
|
||||
#### _Example_ <!-- omit in toc -->
|
||||
|
||||
```js
|
||||
const store = dispatch( CHECKOUT_STORE_KEY );
|
||||
store.setEditingBillingAddress( true );
|
||||
```
|
||||
|
||||
### setEditingShippingAddress
|
||||
|
||||
Set the shipping address to editing state or collapsed state. Note that if the address has invalid fields, it will not be set to collapsed state.
|
||||
|
||||
#### _Parameters_ <!-- omit in toc -->
|
||||
|
||||
- _isEditing_ `boolean`: True to set the shipping address to editing state, false to set it to collapsed state.
|
||||
|
||||
#### _Example_ <!-- omit in toc -->
|
||||
|
||||
```js
|
||||
const store = dispatch( CHECKOUT_STORE_KEY );
|
||||
store.setEditingShippingAddress( true );
|
||||
```
|
||||
|
||||
<!-- FEEDBACK -->
|
||||
|
||||
---
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
.wc-block-components-panel__button {
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
margin-top: em(6px);
|
||||
padding-right: #{24px + $gap-smaller};
|
||||
padding-top: em($gap-small - 6px);
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: enhancement
|
||||
|
||||
Move address card state management to data stores in Checkout block.
|
Loading…
Reference in New Issue