From 0a8fe0a0c620e8436cbe2da931e015d54a0a27e0 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 10 Feb 2022 11:59:43 +0000 Subject: [PATCH] Remove `useSelectShippingRate` hook and adjust how local state works for `PackageRates` (https://github.com/woocommerce/woocommerce-blocks/pull/5802) * Convert radio component to TS and support uncontrolled components * Further radio control to typescript changes * Combine useSelectShippingRate and useSelectShippingRates * Remove useSelectShippingRate hook * Move local radio checked state to PackageRates * This is a Controlled component - update inline docs * useSelectShippingRates -> useSelectShippingRate rename --- .../shipping-rates-control-package/index.tsx | 15 +-- .../package-rates.tsx | 31 ++++-- .../js/base/components/radio-control/index.js | 52 ---------- .../base/components/radio-control/index.tsx | 55 +++++++++++ .../radio-control/option-layout.tsx | 7 +- .../radio-control/{option.js => option.tsx} | 11 ++- .../js/base/components/radio-control/types.ts | 40 ++++++++ .../js/base/context/hooks/shipping/index.js | 1 - .../shipping/use-select-shipping-rate.ts | 97 ++++++++----------- .../shipping/use-select-shipping-rates.ts | 55 ----------- .../providers/cart-checkout/shipping/index.js | 4 +- 11 files changed, 182 insertions(+), 186 deletions(-) delete mode 100644 plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.js create mode 100644 plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.tsx rename plugins/woocommerce-blocks/assets/js/base/components/radio-control/{option.js => option.tsx} (82%) create mode 100644 plugins/woocommerce-blocks/assets/js/base/components/radio-control/types.ts delete mode 100644 plugins/woocommerce-blocks/assets/js/base/context/hooks/shipping/use-select-shipping-rates.ts diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/index.tsx index 764fd1e3a62..5693c58f61d 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/index.tsx @@ -57,7 +57,7 @@ interface PackageProps { export const ShippingRatesControlPackage = ( { packageId, - className, + className = '', noResultsMessage, renderOption, packageData, @@ -65,10 +65,7 @@ export const ShippingRatesControlPackage = ( { collapse = false, showItems = false, }: PackageProps ): ReactElement => { - const { selectShippingRate, selectedShippingRate } = useSelectShippingRate( - packageId, - packageData.shipping_rates - ); + const { selectShippingRate } = useSelectShippingRate(); const header = ( <> @@ -117,8 +114,12 @@ export const ShippingRatesControlPackage = ( { className={ className } noResultsMessage={ noResultsMessage } rates={ packageData.shipping_rates } - onSelectRate={ selectShippingRate } - selected={ selectedShippingRate } + onSelectRate={ ( newShippingRateId ) => + selectShippingRate( newShippingRateId, packageId ) + } + selectedRate={ packageData.shipping_rates.find( + ( rate ) => rate.selected + ) } renderOption={ renderOption } /> ); diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/package-rates.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/package-rates.tsx index ed350d61cb1..045ddb79164 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/package-rates.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/shipping-rates-control-package/package-rates.tsx @@ -1,11 +1,11 @@ /** * External dependencies */ +import { useState, useEffect } from '@wordpress/element'; import RadioControl, { RadioControlOptionLayout, } from '@woocommerce/base-components/radio-control'; import type { PackageRateOption } from '@woocommerce/type-defs/shipping'; -import type { ReactElement } from 'react'; import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart'; /** @@ -20,18 +20,30 @@ interface PackageRates { option: CartShippingPackageShippingRate ) => PackageRateOption; className?: string; - noResultsMessage: ReactElement; - selected?: string; + noResultsMessage: JSX.Element; + selectedRate: CartShippingPackageShippingRate | undefined; } const PackageRates = ( { - className, + className = '', noResultsMessage, onSelectRate, rates, renderOption = renderPackageRateOption, - selected, -}: PackageRates ): ReactElement => { + selectedRate, +}: PackageRates ): JSX.Element => { + const selectedRateId = selectedRate?.rate_id || ''; + + // Store selected rate ID in local state so shipping rates changes are shown in the UI instantly. + const [ selectedOption, setSelectedOption ] = useState( selectedRateId ); + + // Update the selected option if cart state changes in the data stores. + useEffect( () => { + if ( selectedRateId ) { + setSelectedOption( selectedRateId ); + } + }, [ selectedRateId ] ); + if ( rates.length === 0 ) { return noResultsMessage; } @@ -40,10 +52,11 @@ const PackageRates = ( { return ( { - onSelectRate( selectedRateId ); + onChange={ ( value: string ) => { + setSelectedOption( value ); + onSelectRate( value ); } } - selected={ selected } + selected={ selectedOption } options={ rates.map( renderOption ) } /> ); diff --git a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.js b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.js deleted file mode 100644 index f72c5d52f7c..00000000000 --- a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; -import { withInstanceId } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import RadioControlOption from './option'; -import './style.scss'; - -const RadioControl = ( { - className = '', - instanceId, - id, - selected, - onChange = () => {}, - options = [], -} ) => { - const radioControlId = id || instanceId; - - return ( - options.length && ( -
- { options.map( ( option ) => ( - { - onChange( value ); - if ( typeof option.onChange === 'function' ) { - option.onChange( value ); - } - } } - /> - ) ) } -
- ) - ); -}; - -export default withInstanceId( RadioControl ); -export { RadioControlOption }; -export { default as RadioControlOptionLayout } from './option-layout'; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.tsx new file mode 100644 index 00000000000..82d14f3a581 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/index.tsx @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { useInstanceId } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import RadioControlOption from './option'; +import type { RadioControlProps } from './types'; +import './style.scss'; + +const RadioControl = ( { + className = '', + id, + selected, + onChange = () => void 0, + options = [], +}: RadioControlProps ): JSX.Element | null => { + const instanceId = useInstanceId( RadioControl ); + const radioControlId = id || instanceId; + + if ( ! options.length ) { + return null; + } + + return ( +
+ { options.map( ( option ) => ( + { + onChange( value ); + if ( typeof option.onChange === 'function' ) { + option.onChange( value ); + } + } } + /> + ) ) } +
+ ); +}; + +export default RadioControl; +export { default as RadioControlOption } from './option'; +export { default as RadioControlOptionLayout } from './option-layout'; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option-layout.tsx b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option-layout.tsx index 1fe47792ad4..41d327f3edd 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option-layout.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option-layout.tsx @@ -1,8 +1,7 @@ /** - * External dependencies + * Internal dependencies */ -import type { ReactElement } from 'react'; -import type { PackageRateOption } from '@woocommerce/type-defs/shipping'; +import type { RadioControlOptionLayout } from './types'; const OptionLayout = ( { label, @@ -10,7 +9,7 @@ const OptionLayout = ( { description, secondaryDescription, id, -}: Partial< PackageRateOption > ): ReactElement => { +}: RadioControlOptionLayout ): JSX.Element => { return (
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.js b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.tsx similarity index 82% rename from plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.js rename to plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.tsx index 3aba2d231ff..3ff328592d7 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.js +++ b/plugins/woocommerce-blocks/assets/js/base/components/radio-control/option.tsx @@ -7,8 +7,14 @@ import classnames from 'classnames'; * Internal dependencies */ import OptionLayout from './option-layout'; +import type { RadioControlOptionProps } from './types'; -const Option = ( { checked, name, onChange, option } ) => { +const Option = ( { + checked, + name, + onChange, + option, +}: RadioControlOptionProps ): JSX.Element => { const { value, label, @@ -16,7 +22,8 @@ const Option = ( { checked, name, onChange, option } ) => { secondaryLabel, secondaryDescription, } = option; - const onChangeValue = ( event ) => onChange( event.target.value ); + const onChangeValue = ( event: React.ChangeEvent< HTMLInputElement > ) => + onChange( event.target.value ); return (