From 66523872f3aecd1ffb9685e7f82b2a5c128e69fa Mon Sep 17 00:00:00 2001 From: Seghir Nadir Date: Tue, 17 Sep 2024 12:56:15 +0200 Subject: [PATCH] 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 --- .../context/hooks/use-checkout-address.ts | 36 ++++++++--- .../checkout-billing-address-block/block.tsx | 25 +------- .../customer-address.tsx | 11 ++-- .../checkout-shipping-address-block/block.tsx | 10 +-- .../customer-address.tsx | 11 ++-- .../assets/js/data/checkout/action-types.ts | 2 + .../assets/js/data/checkout/actions.ts | 26 ++++++++ .../assets/js/data/checkout/default-state.ts | 27 ++++++-- .../assets/js/data/checkout/reducers.ts | 14 +++++ .../assets/js/data/checkout/selectors.ts | 8 +++ .../extensibility/data-store/checkout.md | 63 ++++++++++++++++++- .../packages/components/panel/style.scss | 1 - ...6-add-control-address-edit-from-data-store | 4 ++ 13 files changed, 180 insertions(+), 58 deletions(-) create mode 100644 plugins/woocommerce/changelog/51386-add-control-address-edit-from-data-store diff --git a/plugins/woocommerce-blocks/assets/js/base/context/hooks/use-checkout-address.ts b/plugins/woocommerce-blocks/assets/js/base/context/hooks/use-checkout-address.ts index 308318d73d3..a69c2d22160 100644 --- a/plugins/woocommerce-blocks/assets/js/base/context/hooks/use-checkout-address.ts +++ b/plugins/woocommerce-blocks/assets/js/base/context/hooks/use-checkout-address.ts @@ -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, diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx index bcdcbac3e69..d56938d94df 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/block.tsx @@ -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 ( <> @@ -121,7 +101,6 @@ const Block = ( { { cartDataLoaded ? ( ) : null } diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx index b36708f4314..668fe20cf61 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-billing-address-block/customer-address.tsx @@ -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( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx index 2ddbb0d7744..e749fa33afe 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/block.tsx @@ -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 ( <> @@ -138,7 +132,6 @@ const Block = ( { { cartDataLoaded ? ( ) : null } @@ -151,6 +144,7 @@ const Block = ( { if ( checked ) { syncBillingWithShipping(); } else { + setEditingBillingAddress( true ); clearBillingAddress( billingAddress ); } } } diff --git a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx index 1c21625c188..fed4e47c7ba 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-address-block/customer-address.tsx @@ -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( diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/action-types.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/action-types.ts index 327cd10bca1..3b1b82a5254 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/action-types.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/action-types.ts @@ -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; diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts index 8cc1f724274..eee54051f65 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts @@ -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 diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/default-state.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/default-state.ts index 7891c255565..1a82d4d5056 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/default-state.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/default-state.ts @@ -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, }; diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/reducers.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/reducers.ts index 1a01c5772df..01bf428d245 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/reducers.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/reducers.ts @@ -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 && diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/selectors.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/selectors.ts index c472bc25bc8..759049db2ba 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/selectors.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/selectors.ts @@ -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; }; diff --git a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/checkout.md b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/checkout.md index 5badd9d879e..b4a99cc4ecb 100644 --- a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/checkout.md +++ b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/checkout.md @@ -1,5 +1,7 @@ # Checkout Store (`wc/store/checkout`) + + > 💡 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_ + +- `boolean`: True if the billing address is being edited. + +#### _Example_ + +```js +const store = select( CHECKOUT_STORE_KEY ); +const editingBillingAddress = store.getEditingBillingAddress(); +``` + +### getEditingShippingAddress + +Returns true if the shipping address is being edited. + +#### _Returns_ + +- `boolean`: True if the shipping address is being edited. + +#### _Example_ + +```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_ + +- _isEditing_ `boolean`: True to set the billing address to editing state, false to set it to collapsed state. + +#### _Example_ + +```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_ + +- _isEditing_ `boolean`: True to set the shipping address to editing state, false to set it to collapsed state. + +#### _Example_ + +```js +const store = dispatch( CHECKOUT_STORE_KEY ); +store.setEditingShippingAddress( true ); +``` + --- diff --git a/plugins/woocommerce-blocks/packages/components/panel/style.scss b/plugins/woocommerce-blocks/packages/components/panel/style.scss index 8df33917d6a..d4651f9b918 100644 --- a/plugins/woocommerce-blocks/packages/components/panel/style.scss +++ b/plugins/woocommerce-blocks/packages/components/panel/style.scss @@ -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); diff --git a/plugins/woocommerce/changelog/51386-add-control-address-edit-from-data-store b/plugins/woocommerce/changelog/51386-add-control-address-edit-from-data-store new file mode 100644 index 00000000000..38a81b13fff --- /dev/null +++ b/plugins/woocommerce/changelog/51386-add-control-address-edit-from-data-store @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Move address card state management to data stores in Checkout block. \ No newline at end of file