Update canMakePayment to receive cart as argument and make it react to changes in billingData. (https://github.com/woocommerce/woocommerce-blocks/pull/4776)
* Add cartCoupons to canMakePayment argument * Add cart to the paymentMethodArgument and improve the dependencies for the effect calling refreshCanMakePayments * Debounce refreshCanMakePayments The initial approach was to debounce billingData and use this value as a dependency for the useEffect that runs refreshCanMakePayments. But because the depencies array can always change we decided to debounce the callback instead, ensuring this way that callback is not called multiple times: for example when typing a field in the billing address. Debounced was chosen instead of throttle because we want to call refreshCanMakePayments once the change event has stopped, with the final value. * Update types and docs related to canMakePaymentArgument * Mock the /cart call when testing payment methods * Remove unused cartCoupons key in canMakePaymentArguments' interface
This commit is contained in:
parent
63582e47b2
commit
27600b3309
|
@ -26,7 +26,7 @@ import type {
|
|||
CartResponseBillingAddress,
|
||||
CartResponseShippingAddress,
|
||||
CartResponseCouponItem,
|
||||
CartResponseCouponItemWithLabel,
|
||||
CartResponseCoupons,
|
||||
} from '@woocommerce/types';
|
||||
import {
|
||||
emptyHiddenAddressFields,
|
||||
|
@ -129,6 +129,7 @@ export const defaultCartData: StoreCart = {
|
|||
*
|
||||
* @return {StoreCart} Object containing cart data.
|
||||
*/
|
||||
|
||||
export const useStoreCart = (
|
||||
options: { shouldSelect: boolean } = { shouldSelect: true }
|
||||
): StoreCart => {
|
||||
|
@ -136,7 +137,6 @@ export const useStoreCart = (
|
|||
const previewCart = previewData?.previewCart;
|
||||
const { shouldSelect } = options;
|
||||
const currentResults = useRef();
|
||||
|
||||
const results: StoreCart = useSelect(
|
||||
( select, { dispatch } ) => {
|
||||
if ( ! shouldSelect ) {
|
||||
|
@ -194,7 +194,7 @@ export const useStoreCart = (
|
|||
// Add a text property to the coupon to allow extensions to modify
|
||||
// the text used to display the coupon, without affecting the
|
||||
// functionality when it comes to removing the coupon.
|
||||
const cartCoupons: CartResponseCouponItemWithLabel[] =
|
||||
const cartCoupons: CartResponseCoupons =
|
||||
cartData.coupons.length > 0
|
||||
? cartData.coupons.map(
|
||||
( coupon: CartResponseCouponItem ) => ( {
|
||||
|
|
|
@ -15,6 +15,7 @@ import type {
|
|||
PaymentMethodConfigInstance,
|
||||
ExpressPaymentMethodConfigInstance,
|
||||
} from '@woocommerce/type-defs/payments';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -51,12 +52,10 @@ const usePaymentMethodRegistration = (
|
|||
const { billingData, shippingAddress } = useCustomerDataContext();
|
||||
const selectedShippingMethods = useShallowEqual( selectedRates );
|
||||
const paymentMethodsOrder = useShallowEqual( paymentMethodsSortOrder );
|
||||
const {
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
paymentRequirements,
|
||||
} = useStoreCart();
|
||||
const cart = useStoreCart();
|
||||
const { cartTotals, cartNeedsShipping, paymentRequirements } = cart;
|
||||
const canPayArgument = useRef( {
|
||||
cart,
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
billingData,
|
||||
|
@ -68,6 +67,7 @@ const usePaymentMethodRegistration = (
|
|||
|
||||
useEffect( () => {
|
||||
canPayArgument.current = {
|
||||
cart,
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
billingData,
|
||||
|
@ -76,6 +76,7 @@ const usePaymentMethodRegistration = (
|
|||
paymentRequirements,
|
||||
};
|
||||
}, [
|
||||
cart,
|
||||
cartTotals,
|
||||
cartNeedsShipping,
|
||||
billingData,
|
||||
|
@ -160,16 +161,21 @@ const usePaymentMethodRegistration = (
|
|||
registeredPaymentMethods,
|
||||
] );
|
||||
|
||||
const [ debouncedRefreshCanMakePayments ] = useDebouncedCallback(
|
||||
refreshCanMakePayments,
|
||||
500
|
||||
);
|
||||
|
||||
// Determine which payment methods are available initially and whenever
|
||||
// shipping methods or cart totals change.
|
||||
// shipping methods, cart or the billing data change.
|
||||
// Some payment methods (e.g. COD) can be disabled for specific shipping methods.
|
||||
useEffect( () => {
|
||||
refreshCanMakePayments();
|
||||
debouncedRefreshCanMakePayments();
|
||||
}, [
|
||||
refreshCanMakePayments,
|
||||
cartTotals,
|
||||
debouncedRefreshCanMakePayments,
|
||||
cart,
|
||||
selectedShippingMethods,
|
||||
paymentRequirements,
|
||||
billingData,
|
||||
] );
|
||||
|
||||
return isInitialized;
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { previewCart } from '@woocommerce/resource-previews';
|
||||
import { dispatch } from '@wordpress/data';
|
||||
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
||||
import {
|
||||
registerPaymentMethod,
|
||||
__experimentalDeRegisterPaymentMethod,
|
||||
|
@ -15,6 +18,7 @@ import {
|
|||
* Internal dependencies
|
||||
*/
|
||||
import PaymentMethods from '../payment-methods';
|
||||
import { defaultCartState } from '../../../../data/default-states';
|
||||
|
||||
jest.mock( '../saved-payment-method-options', () => ( { onChange } ) => {
|
||||
return (
|
||||
|
@ -63,6 +67,22 @@ const resetMockPaymentMethods = () => {
|
|||
};
|
||||
|
||||
describe( 'PaymentMethods', () => {
|
||||
beforeEach( async () => {
|
||||
fetchMock.mockResponse( ( req ) => {
|
||||
if ( req.url.match( /wc\/store\/cart/ ) ) {
|
||||
return Promise.resolve( JSON.stringify( previewCart ) );
|
||||
}
|
||||
return Promise.resolve( '' );
|
||||
} );
|
||||
// need to clear the store resolution state between tests.
|
||||
await dispatch( storeKey ).invalidateResolutionForStore();
|
||||
await dispatch( storeKey ).receiveCart( defaultCartState.cartData );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
fetchMock.resetMocks();
|
||||
} );
|
||||
|
||||
test( 'should show no payment methods component when there are no payment methods', async () => {
|
||||
render(
|
||||
<PaymentMethodDataProvider>
|
||||
|
@ -78,6 +98,8 @@ describe( 'PaymentMethods', () => {
|
|||
// creates an extra `div` with the notice contents used for a11y.
|
||||
expect( noPaymentMethods.length ).toBeGreaterThanOrEqual( 1 );
|
||||
} );
|
||||
// ["`select` control in `@wordpress/data-controls` is deprecated. Please use built-in `resolveSelect` control in `@wordpress/data` instead."]
|
||||
expect( console ).toHaveWarned();
|
||||
} );
|
||||
|
||||
test( 'selecting new payment method', async () => {
|
||||
|
|
|
@ -26,6 +26,8 @@ export interface CartResponseCouponItemWithLabel
|
|||
label: string;
|
||||
}
|
||||
|
||||
export type CartResponseCoupons = CartResponseCouponItemWithLabel[];
|
||||
|
||||
export interface ResponseFirstNameLastName {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
|||
CartResponseBillingAddress,
|
||||
CartResponseShippingRate,
|
||||
CartResponse,
|
||||
CartResponseCoupons,
|
||||
} from './cart-response';
|
||||
import type { ResponseError } from '../../data/types';
|
||||
export interface StoreCartItemQuantity {
|
||||
|
@ -31,7 +32,7 @@ export interface StoreCartCoupon {
|
|||
}
|
||||
|
||||
export interface StoreCart {
|
||||
cartCoupons: Array< CartResponseCouponItem >;
|
||||
cartCoupons: CartResponseCoupons;
|
||||
cartItems: Array< CartResponseItem >;
|
||||
cartFees: Array< CartResponseFeeItem >;
|
||||
cartItemsCount: number;
|
||||
|
|
|
@ -6,7 +6,7 @@ import type { ReactNode } from 'react';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { CartTotals } from './cart';
|
||||
import type { CartTotals, Cart } from './cart';
|
||||
import {
|
||||
CartResponseBillingAddress,
|
||||
CartResponseShippingAddress,
|
||||
|
@ -27,6 +27,7 @@ export interface Supports extends SupportsConfiguration {
|
|||
}
|
||||
|
||||
export interface CanMakePaymentArgument {
|
||||
cart: Cart;
|
||||
cartTotals: CartTotals;
|
||||
cartNeedsShipping: boolean;
|
||||
billingData: CartResponseBillingAddress;
|
||||
|
|
|
@ -52,7 +52,7 @@ The registry function expects a JavaScript object with options specific to the p
|
|||
registerExpressPaymentMethod( options );
|
||||
```
|
||||
|
||||
The options you feed the configuration instance should be an object in this shape (see `ExpressPaymentMethodRegistrationOptions` typedef):
|
||||
The options you feed the configuration instance should be an object in this shape (see `ExpressPaymentMethodConfiguration` typedef):
|
||||
|
||||
```js
|
||||
const options = {
|
||||
|
@ -87,11 +87,12 @@ A callback to determine whether the payment method should be available as an opt
|
|||
|
||||
```
|
||||
canMakePayment( {
|
||||
cart: Cart,
|
||||
cartTotals: CartTotals,
|
||||
cartNeedsShipping: boolean,
|
||||
shippingAddress: CartShippingAddress,
|
||||
billingData: BillingData,
|
||||
selectedShippingMethods: string[],
|
||||
selectedShippingMethods: Record<string,unknown>,
|
||||
paymentRequirements: string[],
|
||||
} )
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue