From d73d9ca12ec09827de3dcce5ddd9f32c8722f2cd Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 8 Apr 2020 12:20:41 +0100 Subject: [PATCH] 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 --- .../shipping-calculator/index.js | 15 +- .../totals/totals-shipping-item/index.js | 18 +-- .../context/cart-checkout/shipping/index.js | 8 +- .../js/base/hooks/cart/test/use-store-cart.js | 11 +- .../js/base/hooks/cart/use-store-cart.js | 14 +- .../assets/js/base/hooks/shipping/index.js | 1 - .../base/hooks/shipping/use-shipping-rates.js | 40 ------ .../js/base/hooks/test/use-shipping-rates.js | 132 ------------------ .../assets/js/type-defs/hooks.js | 47 ++++--- 9 files changed, 65 insertions(+), 221 deletions(-) delete mode 100644 plugins/woocommerce-blocks/assets/js/base/hooks/shipping/use-shipping-rates.js delete mode 100644 plugins/woocommerce-blocks/assets/js/base/hooks/test/use-shipping-rates.js diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.js index 9575392a25d..678bdd5e6f5 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.js @@ -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 (
{ + 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; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/totals-shipping-item/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/totals-shipping-item/index.js index 7a4dfd57719..02ab763a9ff 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/totals-shipping-item/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/totals/totals-shipping-item/index.js @@ -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 && ( { - setShippingAddress( newAddress ); + onUpdate={ () => { setIsShippingCalculatorOpen( false ); } } - address={ shippingAddress } - addressFields={ defaultAddressFields } /> ) } @@ -119,12 +114,9 @@ const TotalsShippingItem = ( { ) } { showCalculator && isShippingCalculatorOpen && ( { - setShippingAddress( newAddress ); + onUpdate={ () => { setIsShippingCalculatorOpen( false ); } } - address={ shippingAddress } - addressFields={ defaultAddressFields } /> ) } diff --git a/plugins/woocommerce-blocks/assets/js/base/context/cart-checkout/shipping/index.js b/plugins/woocommerce-blocks/assets/js/base/context/cart-checkout/shipping/index.js index 8d86734d091..7e2bf00121c 100644 --- a/plugins/woocommerce-blocks/assets/js/base/context/cart-checkout/shipping/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/context/cart-checkout/shipping/index.js @@ -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, diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/cart/test/use-store-cart.js b/plugins/woocommerce-blocks/assets/js/base/hooks/cart/test/use-store-cart.js index a1ad2774207..2c14cae99ee 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/cart/test/use-store-cart.js +++ b/plugins/woocommerce-blocks/assets/js/base/hooks/cart/test/use-store-cart.js @@ -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, { diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/cart/use-store-cart.js b/plugins/woocommerce-blocks/assets/js/base/hooks/cart/use-store-cart.js index af213ad98b4..db9d1c30a72 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/cart/use-store-cart.js +++ b/plugins/woocommerce-blocks/assets/js/base/hooks/cart/use-store-cart.js @@ -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 ] diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/index.js b/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/index.js index 8bff3df9835..94e1df3ad5c 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/index.js +++ b/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/index.js @@ -1,3 +1,2 @@ export * from './use-select-shipping-rate'; -export * from './use-shipping-rates'; export * from './use-shipping-address'; diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/use-shipping-rates.js b/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/use-shipping-rates.js deleted file mode 100644 index d37fcfac10b..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/shipping/use-shipping-rates.js +++ /dev/null @@ -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, - }; -}; diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-shipping-rates.js b/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-shipping-rates.js deleted file mode 100644 index 81739796e02..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-shipping-rates.js +++ /dev/null @@ -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 ) => ( - - - - ); - - const getTestComponent = () => () => { - const items = useShippingRates(); - return
; - }; - - 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(); - } ); -} ); diff --git a/plugins/woocommerce-blocks/assets/js/type-defs/hooks.js b/plugins/woocommerce-blocks/assets/js/type-defs/hooks.js index 25c7f8ad54f..7a606d35d7c 100644 --- a/plugins/woocommerce-blocks/assets/js/type-defs/hooks.js +++ b/plugins/woocommerce-blocks/assets/js/type-defs/hooks.js @@ -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. */ /**