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

Commit

Permalink
Add querying selected shipping rate to the store (#1829)
Browse files Browse the repository at this point in the history
* add selecting shipping to store

* directly call useSelectShippingRate

* refactor cart keys transformation to reducer

* remove selecting first result and accept selecting

* move update shipping to new endpoint

* pass selected rates down

* select shipping right directly and fix editor issues
  • Loading branch information
senadir authored Mar 3, 2020
1 parent c456ea7 commit b7273b4
Show file tree
Hide file tree
Showing 19 changed files with 208 additions and 138 deletions.
5 changes: 4 additions & 1 deletion assets/js/base/components/shipping-calculator/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ const ShippingCalculatorAddress = ( { address: initialAddress, onUpdate } ) => {
<Button
className="wc-block-shipping-calculator-address__button"
disabled={ isShallowEqual( address, initialAddress ) }
onClick={ () => onUpdate( address ) }
onClick={ ( e ) => {
e.preventDefault();
return onUpdate( address );
} }
type="submit"
>
{ __( 'Update', 'woo-gutenberg-products-block' ) }
Expand Down
45 changes: 4 additions & 41 deletions assets/js/base/components/shipping-rates-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import PropTypes from 'prop-types';
import { usePrevious, useShippingRates } from '@woocommerce/base-hooks';
import { useEffect } from '@wordpress/element';
import { usePrevious } from '@woocommerce/base-hooks';

/**
* Internal dependencies
Expand All @@ -14,55 +13,19 @@ import LoadingMask from '../loading-mask';
import './style.scss';

const ShippingRatesControl = ( {
address,
shippingRates,
shippingRatesLoading,
className,
noResultsMessage,
onChange,
renderOption,
selected = [],
} ) => {
const { shippingRates, shippingRatesLoading } = useShippingRates( address );
const previousShippingRates = usePrevious(
shippingRates,
( newRates ) => newRates.length > 0
);

// Select first item when shipping rates are loaded.
useEffect(
() => {
if ( shippingRates.length === 0 ) {
return;
}

const isSelectedValid =
selected.length === shippingRates.length &&
selected.every( ( selectedId, i ) => {
const rates = shippingRates[ i ].shipping_rates;
return rates.some(
( { rate_id: rateId } ) => rateId === selectedId
);
} );

if ( isSelectedValid ) {
return;
}

const newShippingRates = shippingRates.map( ( shippingRate ) => {
if ( shippingRate.shipping_rates.length > 0 ) {
return shippingRate.shipping_rates[ 0 ].rate_id;
}
return null;
} );

if ( newShippingRates.length > 0 ) {
onChange( newShippingRates );
}
},
// We only want to run this when `shippingRates` changes,
// so there is no need to add `selected` to the effect dependencies.
[ shippingRates ]
);

if ( shippingRatesLoading ) {
return (
<LoadingMask
Expand All @@ -78,7 +41,7 @@ const ShippingRatesControl = ( {
onChange={ onChange }
renderOption={ renderOption }
selected={ selected }
shippingRates={ previousShippingRates || [] }
shippingRates={ previousShippingRates || shippingRates }
/>
</LoadingMask>
);
Expand Down
20 changes: 13 additions & 7 deletions assets/js/base/components/shipping-rates-control/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* External dependencies
*/
import PropTypes from 'prop-types';
import { useState } from '@wordpress/element';
import { useSelectShippingRate } from '@woocommerce/base-hooks';

/**
* Internal dependencies
Expand All @@ -12,23 +14,27 @@ import './style.scss';
const Packages = ( {
className,
noResultsMessage,
onChange = () => {},
renderOption,
selected = [],
shippingRates,
shippingRates = [],
} ) => {
const { selectShippingRate } = useSelectShippingRate();
const initiallySelectedRates = shippingRates.map(
( p ) => p.shipping_rates.find( ( rate ) => rate.selected ).rate_id
);
const [ selectedShipping, setSelectedShipping ] = useState(
initiallySelectedRates
);
return shippingRates.map( ( shippingRate, i ) => (
<Package
key={ shippingRate.package_id }
className={ className }
noResultsMessage={ noResultsMessage }
onChange={ ( newShippingRate ) => {
const newSelected = [ ...selected ];
newSelected[ i ] = newShippingRate;
onChange( newSelected );
setSelectedShipping( [ newShippingRate ] );
selectShippingRate( newShippingRate, i );
} }
renderOption={ renderOption }
selected={ selected[ i ] }
selected={ selectedShipping[ i ] }
shippingRate={ shippingRate }
showItems={ shippingRates.length > 1 }
/>
Expand Down
1 change: 1 addition & 0 deletions assets/js/base/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './use-previous';
export * from './checkout';
export * from './payment-methods';
export * from './use-shipping-rates';
export * from './use-select-shipping-rate';
4 changes: 2 additions & 2 deletions assets/js/base/hooks/use-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ import { useShallowEqual } from './use-shallow-equal';
* `'products/attributes'`
* @param {Array} options.resourceValues An array of values (in correct order)
* that are substituted in the route
* placeholders for the collection route.
* placeholders for the collection route (optional).
* Example: `[10, 20]`
* @param {Object} options.query An object of key value pairs for the
* query to execute on the collection
* (optional). Example:
* `{ order: 'ASC', order_by: 'price' }`
* @param {boolean} options.shouldSelect If false, the previous results will be
* returned and internal selects will not
* fire.
* fire (optional).
*
* @return {Object} This hook will return an object with two properties:
* - results An array of collection items returned.
Expand Down
21 changes: 21 additions & 0 deletions assets/js/base/hooks/use-select-shipping-rate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @typedef { import('@woocommerce/type-defs/hooks').SelectedShippingRates } SelectedShippingRates */

/**
* External dependencies
*/
import { useDispatch } from '@wordpress/data';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';

/**
* This is a custom hook for loading the selected shipping rate from the cart store and actions for selecting a rate.
* See also: https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/master/src/RestApi/StoreApi
*
* @return {SelectedShippingRates} An object exposing data and actions from/for the
* store api /cart/select-shipping endpoint.
*/
export const useSelectShippingRate = () => {
const { selectShippingRate } = useDispatch( storeKey );
return {
selectShippingRate,
};
};
35 changes: 18 additions & 17 deletions assets/js/base/hooks/use-shipping-rates.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
/**
* External dependencies
*/
import { useDebounce } from 'use-debounce';

/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
/**
* Internal dependencies
*/
import { useCollection } from './use-collection';
import { useStoreCart } from './use-store-cart';

/**
* This is a custom hook that is wired up to the `wc/store/collections` data
* store for the `wc/store/cart/shipping-rates` route. Given a query object, this
* will ensure a component is kept up to date with the shipping rates matching that
* query in the store state.
*
* @param {Object} query An object containing any query arguments to be
* included with the collection request for the
* shipping rates. Does not have to be included.
*
* @return {Object} This hook will return an object with three properties:
* - shippingRates An array of shipping rate objects.
* - shippingRatesLoading A boolean indicating whether the shipping
* rates are still loading or not.
*/
export const useShippingRates = ( query ) => {
const [ debouncedQuery ] = useDebounce( query, 300 );
export const useShippingRates = () => {
const { shippingRates } = useStoreCart();
const results = useSelect( ( select, { dispatch } ) => {
const store = select( storeKey );
const shippingRatesLoading = store.areShippingRatesLoading();
const { updateShipping } = dispatch( storeKey );

const {
results: shippingRates,
isLoading: shippingRatesLoading,
} = useCollection( {
namespace: '/wc/store',
resourceName: 'cart/shipping-rates',
query: debouncedQuery,
} );
return {
shippingRatesLoading,
updateShipping,
};
}, [] );

return {
shippingRates,
shippingRatesLoading,
...results,
};
};
1 change: 1 addition & 0 deletions assets/js/base/hooks/use-store-cart.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const useStoreCart = ( options = { shouldSelect: true } ) => {

return {
cartCoupons: cartData.coupons,
shippingRates: cartData.shippingRates,
cartItems: cartData.items,
cartItemsCount: cartData.itemsCount,
cartItemsWeight: cartData.itemsWeight,
Expand Down
2 changes: 2 additions & 0 deletions assets/js/blocks/cart-checkout/cart/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const CartFrontend = ( {
cartIsLoading,
cartErrors,
cartCoupons,
shippingRates,
} = useStoreCart();

return (
Expand All @@ -51,6 +52,7 @@ const CartFrontend = ( {
}
isShippingCostHidden={ isShippingCostHidden }
isLoading={ cartIsLoading }
shippingRates={ shippingRates }
/>
</LoadingMask>
) }
Expand Down
Loading

0 comments on commit b7273b4

Please sign in to comment.