Refactor some of the shipping hooks/context usage (https://github.com/woocommerce/woocommerce-blocks/pull/2146)
* Counting helpers for shipping rates and packages * Use new helpers * Make shipping calculator use shipping context directly * Totals should use current address * Avoid useShippingRates * Return rates and other items from useShippingRatse in useStoreCart instead * Update tests * Merge conflict * Merge conflict
This commit is contained in:
parent
9a4fc8e2cc
commit
d73d9ca12e
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import { useShippingDataContext } from '@woocommerce/base-context';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -9,13 +10,18 @@ import PropTypes from 'prop-types';
|
|||
import ShippingCalculatorAddress from './address';
|
||||
import './style.scss';
|
||||
|
||||
const ShippingCalculator = ( { onUpdate, address, addressFields } ) => {
|
||||
const ShippingCalculator = ( {
|
||||
onUpdate = () => {},
|
||||
addressFields = [ 'country', 'state', 'city', 'postcode' ],
|
||||
} ) => {
|
||||
const { shippingAddress, setShippingAddress } = useShippingDataContext();
|
||||
return (
|
||||
<div className="wc-block-cart__shipping-calculator">
|
||||
<ShippingCalculatorAddress
|
||||
address={ address }
|
||||
address={ shippingAddress }
|
||||
addressFields={ addressFields }
|
||||
onUpdate={ ( newAddress ) => {
|
||||
setShippingAddress( newAddress );
|
||||
onUpdate( newAddress );
|
||||
} }
|
||||
/>
|
||||
|
@ -24,9 +30,8 @@ const ShippingCalculator = ( { onUpdate, address, addressFields } ) => {
|
|||
};
|
||||
|
||||
ShippingCalculator.propTypes = {
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
address: PropTypes.object.isRequired,
|
||||
addressFields: PropTypes.array.isRequired,
|
||||
onUpdate: PropTypes.func,
|
||||
addressFields: PropTypes.array,
|
||||
};
|
||||
|
||||
export default ShippingCalculator;
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from '@woocommerce/base-components/cart-checkout';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { useShippingRates } from '@woocommerce/base-hooks';
|
||||
import { useStoreCart } from '@woocommerce/base-hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -32,14 +32,12 @@ const TotalsShippingItem = ( {
|
|||
const [ isShippingCalculatorOpen, setIsShippingCalculatorOpen ] = useState(
|
||||
false
|
||||
);
|
||||
const defaultAddressFields = [ 'country', 'state', 'city', 'postcode' ];
|
||||
const {
|
||||
shippingRates,
|
||||
shippingAddress,
|
||||
shippingRatesLoading,
|
||||
hasShippingAddress,
|
||||
setShippingAddress,
|
||||
} = useShippingRates();
|
||||
shippingAddress,
|
||||
} = useStoreCart();
|
||||
const totalShippingValue = DISPLAY_CART_PRICES_INCLUDING_TAX
|
||||
? parseInt( values.total_shipping, 10 ) +
|
||||
parseInt( values.total_shipping_tax, 10 )
|
||||
|
@ -80,12 +78,9 @@ const TotalsShippingItem = ( {
|
|||
<>
|
||||
{ showCalculator && isShippingCalculatorOpen && (
|
||||
<ShippingCalculator
|
||||
onUpdate={ ( newAddress ) => {
|
||||
setShippingAddress( newAddress );
|
||||
onUpdate={ () => {
|
||||
setIsShippingCalculatorOpen( false );
|
||||
} }
|
||||
address={ shippingAddress }
|
||||
addressFields={ defaultAddressFields }
|
||||
/>
|
||||
) }
|
||||
</>
|
||||
|
@ -119,12 +114,9 @@ const TotalsShippingItem = ( {
|
|||
) }
|
||||
{ showCalculator && isShippingCalculatorOpen && (
|
||||
<ShippingCalculator
|
||||
onUpdate={ ( newAddress ) => {
|
||||
setShippingAddress( newAddress );
|
||||
onUpdate={ () => {
|
||||
setIsShippingCalculatorOpen( false );
|
||||
} }
|
||||
address={ shippingAddress }
|
||||
addressFields={ defaultAddressFields }
|
||||
/>
|
||||
) }
|
||||
</>
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
} from '@wordpress/element';
|
||||
import {
|
||||
useShippingAddress,
|
||||
useShippingRates,
|
||||
useStoreCart,
|
||||
useSelectShippingRate,
|
||||
} from '@woocommerce/base-hooks';
|
||||
|
@ -65,7 +64,11 @@ export const useShippingDataContext = () => {
|
|||
*/
|
||||
export const ShippingDataProvider = ( { children } ) => {
|
||||
const { dispatchActions } = useCheckoutContext();
|
||||
const { cartNeedsShipping: needsShipping } = useStoreCart();
|
||||
const {
|
||||
cartNeedsShipping: needsShipping,
|
||||
shippingRates,
|
||||
shippingRatesLoading,
|
||||
} = useStoreCart();
|
||||
const [ shippingErrorStatus, dispatchErrorStatus ] = useReducer(
|
||||
errorStatusReducer,
|
||||
NONE
|
||||
|
@ -73,7 +76,6 @@ export const ShippingDataProvider = ( { children } ) => {
|
|||
const [ observers, subscriber ] = useReducer( emitReducer, {} );
|
||||
const { shippingAddress, setShippingAddress } = useShippingAddress();
|
||||
const currentObservers = useRef( observers );
|
||||
const { shippingRates, shippingRatesLoading } = useShippingRates();
|
||||
const {
|
||||
selectShippingRate: setSelectedRates,
|
||||
selectedShippingRates: selectedRates,
|
||||
|
|
|
@ -34,13 +34,15 @@ describe( 'useStoreCart', () => {
|
|||
cartIsLoading: false,
|
||||
cartItemErrors: [],
|
||||
cartErrors: [],
|
||||
shippingRates: previewCart.shipping_rates,
|
||||
shippingAddress: {
|
||||
country: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postcode: '',
|
||||
},
|
||||
shippingRates: previewCart.shipping_rates,
|
||||
shippingRatesLoading: false,
|
||||
hasShippingAddress: false,
|
||||
};
|
||||
|
||||
const mockCartItems = [ { key: '1', id: 1, name: 'Lorem Ipsum' } ];
|
||||
|
@ -67,11 +69,13 @@ describe( 'useStoreCart', () => {
|
|||
cartItemsCount: 1,
|
||||
cartItemsWeight: 10,
|
||||
cartNeedsShipping: true,
|
||||
shippingAddress: mockShippingAddress,
|
||||
shippingRates: [],
|
||||
cartTotals: mockCartTotals,
|
||||
cartIsLoading: mockCartIsLoading,
|
||||
cartErrors: mockCartErrors,
|
||||
shippingAddress: mockShippingAddress,
|
||||
shippingRates: [],
|
||||
shippingRatesLoading: false,
|
||||
hasShippingAddress: false,
|
||||
};
|
||||
|
||||
const getWrappedComponents = ( Component ) => (
|
||||
|
@ -94,6 +98,7 @@ describe( 'useStoreCart', () => {
|
|||
hasFinishedResolution: jest
|
||||
.fn()
|
||||
.mockReturnValue( ! mockCartIsLoading ),
|
||||
areShippingRatesLoading: jest.fn().mockReturnValue( false ),
|
||||
},
|
||||
};
|
||||
registry.registerStore( storeKey, {
|
||||
|
|
|
@ -33,6 +33,8 @@ export const defaultCartData = {
|
|||
country: '',
|
||||
},
|
||||
shippingRates: [],
|
||||
shippingRatesLoading: false,
|
||||
hasShippingAddress: false,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -68,13 +70,15 @@ export const useStoreCart = ( options = { shouldSelect: true } ) => {
|
|||
cartTotals: previewCart.totals,
|
||||
cartIsLoading: false,
|
||||
cartErrors: [],
|
||||
shippingRates: previewCart.shipping_rates,
|
||||
shippingAddress: {
|
||||
country: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postcode: '',
|
||||
},
|
||||
shippingRates: previewCart.shipping_rates,
|
||||
shippingRatesLoading: false,
|
||||
hasShippingAddress: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -85,10 +89,10 @@ export const useStoreCart = ( options = { shouldSelect: true } ) => {
|
|||
const cartIsLoading = ! store.hasFinishedResolution(
|
||||
'getCartData'
|
||||
);
|
||||
const shippingRatesLoading = store.areShippingRatesLoading();
|
||||
|
||||
return {
|
||||
cartCoupons: cartData.coupons,
|
||||
shippingRates: cartData.shippingRates,
|
||||
shippingAddress: cartData.shippingAddress,
|
||||
cartItems: cartData.items,
|
||||
cartItemsCount: cartData.itemsCount,
|
||||
cartItemsWeight: cartData.itemsWeight,
|
||||
|
@ -97,6 +101,10 @@ export const useStoreCart = ( options = { shouldSelect: true } ) => {
|
|||
cartTotals,
|
||||
cartIsLoading,
|
||||
cartErrors,
|
||||
shippingAddress: cartData.shippingAddress,
|
||||
shippingRates: cartData.shippingRates,
|
||||
shippingRatesLoading,
|
||||
hasShippingAddress: !! cartData.shippingAddress.country,
|
||||
};
|
||||
},
|
||||
[ shouldSelect ]
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
export * from './use-select-shipping-rate';
|
||||
export * from './use-shipping-rates';
|
||||
export * from './use-shipping-address';
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { useStoreCart } from '../cart/use-store-cart';
|
||||
import { useShippingAddress } from '../shipping/use-shipping-address';
|
||||
/**
|
||||
* This is a custom hook that is wired up to the `wc/store/cart/shipping-rates` route.
|
||||
* Given a a set of default fields keys, this will handle shipping form state and load
|
||||
* new rates when certain fields change.
|
||||
*
|
||||
* @return {Object} This hook will return an object with three properties:
|
||||
* - {Boolean} shippingRatesLoading A boolean indicating whether the shipping
|
||||
* rates are still loading or not.
|
||||
* - {Function} setShippingAddress An function that optimistically
|
||||
* update shipping address and dispatches async rate fetching.
|
||||
* - {Object} shippingAddress An object containing shipping address.
|
||||
* - {Object} shippingAddress True when address data exists.
|
||||
*/
|
||||
export const useShippingRates = () => {
|
||||
const { cartErrors, shippingRates } = useStoreCart();
|
||||
const { shippingAddress, setShippingAddress } = useShippingAddress();
|
||||
const shippingRatesLoading = useSelect(
|
||||
( select ) => select( storeKey ).areShippingRatesLoading(),
|
||||
[]
|
||||
);
|
||||
return {
|
||||
shippingRates,
|
||||
shippingAddress,
|
||||
setShippingAddress,
|
||||
shippingRatesLoading,
|
||||
shippingRatesErrors: cartErrors,
|
||||
hasShippingAddress: !! shippingAddress.country,
|
||||
};
|
||||
};
|
|
@ -1,132 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import TestRenderer, { act } from 'react-test-renderer';
|
||||
import { createRegistry, RegistryProvider } from '@wordpress/data';
|
||||
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { useShippingRates } from '../shipping';
|
||||
|
||||
jest.mock( '@woocommerce/block-data', () => ( {
|
||||
__esModule: true,
|
||||
CART_STORE_KEY: 'test/store',
|
||||
} ) );
|
||||
|
||||
describe( 'useShippingRates', () => {
|
||||
let registry, mocks, renderer;
|
||||
const getProps = ( testRenderer ) => {
|
||||
const {
|
||||
shippingRates,
|
||||
shippingAddress,
|
||||
setShippingAddress,
|
||||
shippingRatesLoading,
|
||||
} = testRenderer.root.findByType( 'div' ).props;
|
||||
return {
|
||||
shippingRates,
|
||||
shippingAddress,
|
||||
setShippingAddress,
|
||||
shippingRatesLoading,
|
||||
};
|
||||
};
|
||||
|
||||
const getWrappedComponents = ( Component ) => (
|
||||
<RegistryProvider value={ registry }>
|
||||
<Component />
|
||||
</RegistryProvider>
|
||||
);
|
||||
|
||||
const getTestComponent = () => () => {
|
||||
const items = useShippingRates();
|
||||
return <div { ...items } />;
|
||||
};
|
||||
|
||||
const mockCartData = {
|
||||
coupons: [],
|
||||
items: [ { foo: 'bar' } ],
|
||||
itemsCount: 123,
|
||||
itemsWeight: 123,
|
||||
needsShipping: false,
|
||||
shippingAddress: {
|
||||
country: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postcode: '',
|
||||
},
|
||||
shippingRates: [
|
||||
{
|
||||
shippingRates: [ { foo: 'bar' } ],
|
||||
destination: {
|
||||
country: '',
|
||||
state: '',
|
||||
city: '',
|
||||
postcode: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const setUpMocks = () => {
|
||||
mocks = {
|
||||
selectors: {
|
||||
getCartData: jest.fn().mockReturnValue( mockCartData ),
|
||||
getCartErrors: jest.fn().mockReturnValue( false ),
|
||||
getCartTotals: jest.fn().mockReturnValue( 123 ),
|
||||
areShippingRatesLoading: jest.fn().mockReturnValue( false ),
|
||||
hasFinishedResolution: jest.fn().mockReturnValue( true ),
|
||||
},
|
||||
};
|
||||
registry.registerStore( storeKey, {
|
||||
reducer: () => ( {} ),
|
||||
selectors: mocks.selectors,
|
||||
} );
|
||||
};
|
||||
|
||||
beforeEach( () => {
|
||||
registry = createRegistry();
|
||||
mocks = {};
|
||||
renderer = null;
|
||||
setUpMocks();
|
||||
} );
|
||||
it( 'should return expected address provided by the store', () => {
|
||||
const TestComponent = getTestComponent();
|
||||
act( () => {
|
||||
renderer = TestRenderer.create(
|
||||
getWrappedComponents( TestComponent )
|
||||
);
|
||||
} );
|
||||
|
||||
const { shippingAddress } = getProps( renderer );
|
||||
expect( shippingAddress ).toStrictEqual( mockCartData.shippingAddress );
|
||||
// rerender
|
||||
act( () => {
|
||||
renderer.update( getWrappedComponents( TestComponent ) );
|
||||
} );
|
||||
// re-render should result in same shippingAddress object.
|
||||
const { shippingAddress: newShippingAddress } = getProps( renderer );
|
||||
expect( newShippingAddress ).toStrictEqual( shippingAddress );
|
||||
renderer.unmount();
|
||||
} );
|
||||
|
||||
it( 'should return expected shipping rates provided by the store', () => {
|
||||
const TestComponent = getTestComponent();
|
||||
act( () => {
|
||||
renderer = TestRenderer.create(
|
||||
getWrappedComponents( TestComponent )
|
||||
);
|
||||
} );
|
||||
|
||||
const { shippingRates } = getProps( renderer );
|
||||
expect( shippingRates ).toStrictEqual( mockCartData.shippingRates );
|
||||
// rerender
|
||||
act( () => {
|
||||
renderer.update( getWrappedComponents( TestComponent ) );
|
||||
} );
|
||||
// re-render should result in same shippingAddress object.
|
||||
const { shippingRates: newShippingRates } = getProps( renderer );
|
||||
expect( newShippingRates ).toStrictEqual( shippingRates );
|
||||
renderer.unmount();
|
||||
} );
|
||||
} );
|
|
@ -6,27 +6,32 @@
|
|||
/**
|
||||
* @typedef {Object} StoreCart
|
||||
*
|
||||
* @property {Array} cartCoupons An array of coupons applied
|
||||
* to the cart.
|
||||
* @property {Array} shippingRates array of selected shipping
|
||||
* rates
|
||||
* @property {CartShippingAddress} shippingAddress Shipping address for the
|
||||
* cart.
|
||||
* @property {Array} cartItems An array of items in the
|
||||
* cart.
|
||||
* @property {number} cartItemsCount The number of items in the
|
||||
* cart.
|
||||
* @property {number} cartItemsWeight The weight of all items in
|
||||
* the cart.
|
||||
* @property {boolean} cartNeedsShipping True when the cart will
|
||||
* require shipping.
|
||||
* @property {Array} cartItemErrors Item validation errors.
|
||||
* @property {Object} cartTotals Cart and line total
|
||||
* amounts.
|
||||
* @property {boolean} cartIsLoading True when cart data is
|
||||
* being loaded.
|
||||
* @property {Array} cartErrors An array of errors thrown
|
||||
* by the cart.
|
||||
* @property {Array} cartCoupons An array of coupons applied
|
||||
* to the cart.
|
||||
* @property {Array} cartItems An array of items in the
|
||||
* cart.
|
||||
* @property {number} cartItemsCount The number of items in the
|
||||
* cart.
|
||||
* @property {number} cartItemsWeight The weight of all items in
|
||||
* the cart.
|
||||
* @property {boolean} cartNeedsShipping True when the cart will
|
||||
* require shipping.
|
||||
* @property {Array} cartItemErrors Item validation errors.
|
||||
* @property {Object} cartTotals Cart and line total
|
||||
* amounts.
|
||||
* @property {boolean} cartIsLoading True when cart data is
|
||||
* being loaded.
|
||||
* @property {Array} cartErrors An array of errors thrown
|
||||
* by the cart.
|
||||
* @property {CartShippingAddress} shippingAddress Shipping address for the
|
||||
* cart.
|
||||
* @property {Array} shippingRates array of selected shipping
|
||||
* rates.
|
||||
* @property {boolean} shippingRatesLoading Whether or not the
|
||||
* shipping rates are
|
||||
* being loaded.
|
||||
* @property {boolean} hasShippingAddress Whether or not the cart
|
||||
* has a shipping address yet.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue