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

Commit

Permalink
Convert checkout context to data store - part 1 (#6232)
Browse files Browse the repository at this point in the history
* Add checkout data store

* wip on checkout data store

* CheckoutContext now uses the checkout store

* Investigated and removed setting the redirectUrl on the default state

* update extension and address hooks to use checkout data store

* use checkout data store in checkout-processor and use-checkout-button

* trim useCheckoutContext from use-payment-method-interface && use-store-cart-item-quantity

* Remove useCheckoutContext from shipping provider

* Remove isCalculating from state

* Removed useCheckoutContext from lots of places

* Remove useCheckoutContext from checkout-payment-block

* Remove useCheckoutContext in checkout-shipping-methods-block and checkout-shipping-address-block

* add isCart selector and action and update the checkoutstate context

* Fixed redirectUrl bug by using thunks

* Remove dispatchActions from checkout-state

* Change SET_HAS_ERROR action to be neater

* Thomas' feedback

* Tidy up

* Oops, deleted things I shouldn't have

* Typescript

* Fix types

* Fix tests

* Remove isCart

* Update docs and remove unecessary getRedirectUrl() selector

* set correct type for preloadedCheckoutData

* Remove duplicate Address type

* Fix missing addresses from type-defs index

* Update docs/block-client-apis/checkout/checkout-api.md

Co-authored-by: Thomas Roberts <[email protected]>

* Update docs/block-client-apis/checkout/checkout-api.md

Co-authored-by: Thomas Roberts <[email protected]>

* Update docs

* Update docs/block-client-apis/checkout/checkout-api.md

Co-authored-by: Thomas Roberts <[email protected]>

* Update docs/block-client-apis/checkout/checkout-api.md

Co-authored-by: Thomas Roberts <[email protected]>

* Revert feedback changes

* REvert feedback formatting

* Update docs formatting

* Delete empty types.ts file

* remove merge conflict from docs

* Correct linting in docs

Co-authored-by: Thomas Roberts <[email protected]>
  • Loading branch information
alexflorisca and opr committed Sep 28, 2022
1 parent 300c9e1 commit 3b91c09
Show file tree
Hide file tree
Showing 47 changed files with 942 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
*/
import TestRenderer, { act } from 'react-test-renderer';
import { createRegistry, RegistryProvider } from '@wordpress/data';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
*/
import * as mockUseStoreCart from '../use-store-cart';
import { useStoreCartItemQuantity } from '../use-store-cart-item-quantity';
import { config as checkoutStoreConfig } from '../../../../../data/checkout';

jest.mock( '../use-store-cart', () => ( {
useStoreCart: jest.fn(),
} ) );

jest.mock( '@woocommerce/block-data', () => ( {
__esModule: true,
CART_STORE_KEY: 'test/store',
CART_STORE_KEY: 'test/cart/store',
CHECKOUT_STORE_KEY: 'test/checkout/store',
} ) );

// Make debounce instantaneous.
Expand All @@ -42,13 +44,15 @@ describe( 'useStoreCartItemQuantity', () => {
let mockRemoveItemFromCart;
let mockChangeCartItemQuantity;
const setupMocks = ( { isPendingDelete, isPendingQuantity } ) => {
// Register mock cart store
mockRemoveItemFromCart = jest
.fn()
.mockReturnValue( { type: 'removeItemFromCartAction' } );
mockChangeCartItemQuantity = jest
.fn()
.mockReturnValue( { type: 'changeCartItemQuantityAction' } );
registry.registerStore( storeKey, {

registry.registerStore( CART_STORE_KEY, {
reducer: () => ( {} ),
actions: {
removeItemFromCart: mockRemoveItemFromCart,
Expand All @@ -63,6 +67,9 @@ describe( 'useStoreCartItemQuantity', () => {
.mockReturnValue( isPendingQuantity ),
},
} );

// Register actual checkout store
registry.registerStore( CHECKOUT_STORE_KEY, checkoutStoreConfig );
};

beforeEach( () => {
Expand Down
36 changes: 22 additions & 14 deletions assets/js/base/context/hooks/cart/use-store-cart-item-quantity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useCallback, useState, useEffect } from '@wordpress/element';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { useDebounce } from 'use-debounce';
import { usePrevious } from '@woocommerce/base-hooks';
import { triggerFragmentRefresh } from '@woocommerce/base-utils';
Expand All @@ -20,7 +20,6 @@ import {
* Internal dependencies
*/
import { useStoreCart } from './use-store-cart';
import { useCheckoutContext } from '../../providers/cart-checkout';

/**
* Ensures the object passed has props key: string and quantity: number
Expand Down Expand Up @@ -54,14 +53,17 @@ export const useStoreCartItemQuantity = (
const { key: cartItemKey = '', quantity: cartItemQuantity = 1 } =
verifiedCartItem;
const { cartErrors } = useStoreCart();
const { dispatchActions } = useCheckoutContext();
const { incrementCalculating, decrementCalculating } = useDispatch(
CHECKOUT_STORE_KEY
);

// Store quantity in hook state. This is used to keep the UI updated while server request is updated.
const [ quantity, setQuantity ] = useState< number >( cartItemQuantity );
const [ debouncedQuantity ] = useDebounce< number >( quantity, 400 );
const previousDebouncedQuantity = usePrevious( debouncedQuantity );
const { removeItemFromCart, changeCartItemQuantity } =
useDispatch( storeKey );
const { removeItemFromCart, changeCartItemQuantity } = useDispatch(
CART_STORE_KEY
);

// Update local state when server updates.
useEffect( () => setQuantity( cartItemQuantity ), [ cartItemQuantity ] );
Expand All @@ -75,7 +77,7 @@ export const useStoreCartItemQuantity = (
delete: false,
};
}
const store = select( storeKey );
const store = select( CART_STORE_KEY );
return {
quantity: store.isItemPendingQuantity( cartItemKey ),
delete: store.isItemPendingDelete( cartItemKey ),
Expand Down Expand Up @@ -112,29 +114,35 @@ export const useStoreCartItemQuantity = (

useEffect( () => {
if ( isPending.delete ) {
dispatchActions.incrementCalculating();
incrementCalculating();
} else {
dispatchActions.decrementCalculating();
decrementCalculating();
}
return () => {
if ( isPending.delete ) {
dispatchActions.decrementCalculating();
decrementCalculating();
}
};
}, [ dispatchActions, isPending.delete ] );
}, [ decrementCalculating, incrementCalculating, isPending.delete ] );

useEffect( () => {
if ( isPending.quantity || debouncedQuantity !== quantity ) {
dispatchActions.incrementCalculating();
incrementCalculating();
} else {
dispatchActions.decrementCalculating();
decrementCalculating();
}
return () => {
if ( isPending.quantity || debouncedQuantity !== quantity ) {
dispatchActions.decrementCalculating();
decrementCalculating();
}
};
}, [ dispatchActions, isPending.quantity, debouncedQuantity, quantity ] );
}, [
incrementCalculating,
decrementCalculating,
isPending.quantity,
debouncedQuantity,
quantity,
] );

return {
isPendingDelete: isPending.delete,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { getSetting } from '@woocommerce/settings';
import deprecated from '@wordpress/deprecated';
import LoadingMask from '@woocommerce/base-components/loading-mask';
import type { PaymentMethodInterface } from '@woocommerce/types';
import { useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
Expand All @@ -30,17 +32,29 @@ import { useShippingData } from '../shipping/use-shipping-data';
*/
export const usePaymentMethodInterface = (): PaymentMethodInterface => {
const {
isCalculating,
isComplete,
isIdle,
isProcessing,
onCheckoutBeforeProcessing,
onCheckoutValidationBeforeProcessing,
onCheckoutAfterProcessingWithSuccess,
onCheckoutAfterProcessingWithError,
onSubmit,
customerId,
} = useCheckoutContext();
const {
isCalculating,
isComplete,
isIdle,
isProcessing,
customerId,
} = useSelect( ( select ) => {
const store = select( CHECKOUT_STORE_KEY );
return {
isComplete: store.isComplete(),
isIdle: store.isIdle(),
isProcessing: store.isProcessing(),
customerId: store.getCustomerId(),
isCalculating: store.isCalculating(),
};
} );

const {
currentStatus,
activePaymentMethod,
Expand Down
8 changes: 7 additions & 1 deletion assets/js/base/context/hooks/test/use-checkout-submit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { createRegistry, RegistryProvider } from '@wordpress/data';
* Internal dependencies
*/
import { useCheckoutSubmit } from '../use-checkout-submit';
import {
CHECKOUT_STORE_KEY,
config as checkoutStoreConfig,
} from '../../../../data/checkout';

const mockUseCheckoutContext = {
onSubmit: jest.fn(),
Expand Down Expand Up @@ -42,7 +46,9 @@ describe( 'useCheckoutSubmit', () => {
};

beforeEach( () => {
registry = createRegistry();
registry = createRegistry( {
[ CHECKOUT_STORE_KEY ]: checkoutStoreConfig,
} );
renderer = null;
} );

Expand Down
9 changes: 6 additions & 3 deletions assets/js/base/context/hooks/use-checkout-address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import {
BillingAddress,
} from '@woocommerce/settings';
import { useCallback } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
*/
import { useCheckoutContext } from '../providers/cart-checkout';
import { useCustomerData } from './use-customer-data';
import { useShippingData } from './shipping/use-shipping-data';

Expand All @@ -37,8 +38,10 @@ interface CheckoutAddress {
*/
export const useCheckoutAddress = (): CheckoutAddress => {
const { needsShipping } = useShippingData();
const { useShippingAsBilling, setUseShippingAsBilling } =
useCheckoutContext();
const { useShippingAsBilling } = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).getCheckoutState()
);
const { setUseShippingAsBilling } = useDispatch( CHECKOUT_STORE_KEY );
const {
billingAddress,
setBillingAddress,
Expand Down
16 changes: 10 additions & 6 deletions assets/js/base/context/hooks/use-checkout-extension-data.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
/**
* External dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback, useEffect, useRef } from '@wordpress/element';
import isShallowEqual from '@wordpress/is-shallow-equal';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
*/
import { useCheckoutContext } from '../providers/cart-checkout/checkout-state';
import type { CheckoutStateContextState } from '../providers/cart-checkout/checkout-state/types';
import type { CheckoutState } from '../../../data/checkout/types';

/**
* Custom hook for setting custom checkout data which is passed to the wc/store/checkout endpoint when processing orders.
*/
export const useCheckoutExtensionData = (): {
extensionData: CheckoutStateContextState[ 'extensionData' ];
extensionData: CheckoutState[ 'extensionData' ];
setExtensionData: (
namespace: string,
key: string,
value: unknown
) => void;
} => {
const { dispatchActions, extensionData } = useCheckoutContext();
const { setExtensionData } = useDispatch( CHECKOUT_STORE_KEY );
const { extensionData } = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).getCheckoutState()
);
const extensionDataRef = useRef( extensionData );

useEffect( () => {
Expand All @@ -33,15 +37,15 @@ export const useCheckoutExtensionData = (): {
const setExtensionDataWithNamespace = useCallback(
( namespace, key, value ) => {
const currentData = extensionDataRef.current[ namespace ] || {};
dispatchActions.setExtensionData( {
setExtensionData( {
...extensionDataRef.current,
[ namespace ]: {
...currentData,
[ key ]: value,
},
} );
},
[ dispatchActions ]
[ setExtensionData ]
);

return {
Expand Down
20 changes: 17 additions & 3 deletions assets/js/base/context/hooks/use-checkout-submit.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/**
* External dependencies
*/
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { useCheckoutContext } from '../providers/cart-checkout/checkout-state';
import { useCheckoutContext } from '../providers';
import { usePaymentMethodDataContext } from '../providers/cart-checkout/payment-methods';
import { usePaymentMethods } from './payment-methods/use-payment-methods';

Expand All @@ -16,14 +18,26 @@ import { usePaymentMethods } from './payment-methods/use-payment-methods';
*/
export const useCheckoutSubmit = () => {
const {
onSubmit,
isCalculating,
isBeforeProcessing,
isProcessing,
isAfterProcessing,
isComplete,
hasError,
} = useCheckoutContext();
} = useSelect( ( select ) => {
const store = select( CHECKOUT_STORE_KEY );
return {
isCalculating: store.isCalculating(),
isBeforeProcessing: store.isBeforeProcessing(),
isProcessing: store.isProcessing(),
isAfterProcessing: store.isAfterProcessing(),
isComplete: store.isComplete(),
hasError: store.hasError(),
};
} );

const { onSubmit } = useCheckoutContext();

const { paymentMethods = {} } = usePaymentMethods();
const { activePaymentMethod, currentStatus: paymentStatus } =
usePaymentMethodDataContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CheckoutProvider } from '../checkout-provider';
*/
export const CartProvider = ( { children, redirectUrl } ) => {
return (
<CheckoutProvider isCart={ true } redirectUrl={ redirectUrl }>
<CheckoutProvider redirectUrl={ redirectUrl }>
{ children }
</CheckoutProvider>
);
Expand Down
Loading

0 comments on commit 3b91c09

Please sign in to comment.