Handle multiple packages coming from slots (https://github.com/woocommerce/woocommerce-blocks/pull/7829)
* Handle multiple packages coming from slots * fix missing prop * fix typo * remove collapse param * rename trinary to ternary
This commit is contained in:
parent
e71a2fdbb0
commit
b58c5beb3e
|
@ -44,6 +44,10 @@ export type PackageRateRenderOption = (
|
|||
option: CartShippingPackageShippingRate
|
||||
) => PackageRateOption;
|
||||
|
||||
// A flag can be ternary if true, false, and undefined are all valid options.
|
||||
// In our case, we use this for collapsible and showItems, having a boolean will force that
|
||||
// option, having undefined will let the component decide the logic based on other factors.
|
||||
export type TernaryFlag = boolean | undefined;
|
||||
interface PackageProps {
|
||||
/* PackageId can be a string, WooCommerce Subscriptions uses strings for example, but WooCommerce core uses numbers */
|
||||
packageId: string | number;
|
||||
|
@ -51,9 +55,9 @@ interface PackageProps {
|
|||
collapse?: boolean;
|
||||
packageData: PackageData;
|
||||
className?: string;
|
||||
collapsible?: boolean;
|
||||
collapsible?: TernaryFlag;
|
||||
noResultsMessage: ReactElement;
|
||||
showItems?: boolean;
|
||||
showItems?: TernaryFlag;
|
||||
}
|
||||
|
||||
export const ShippingRatesControlPackage = ( {
|
||||
|
@ -62,15 +66,26 @@ export const ShippingRatesControlPackage = ( {
|
|||
noResultsMessage,
|
||||
renderOption,
|
||||
packageData,
|
||||
collapsible = false,
|
||||
collapse = false,
|
||||
showItems = false,
|
||||
collapsible,
|
||||
showItems,
|
||||
}: PackageProps ): ReactElement => {
|
||||
const { selectShippingRate } = useSelectShippingRate();
|
||||
const multiplePackages =
|
||||
document.querySelectorAll(
|
||||
'.wc-block-components-shipping-rates-control__package'
|
||||
).length > 1;
|
||||
|
||||
// If showItems is not set, we check if we have multiple packages.
|
||||
// We sometimes don't want to show items even if we have multiple packages.
|
||||
const shouldShowItems = showItems ?? multiplePackages;
|
||||
|
||||
// If collapsible is not set, we check if we have multiple packages.
|
||||
// We sometimes don't want to collapse even if we have multiple packages.
|
||||
const shouldBeCollapsible = collapsible ?? multiplePackages;
|
||||
|
||||
const header = (
|
||||
<>
|
||||
{ ( showItems || collapsible ) && (
|
||||
{ ( shouldBeCollapsible || shouldShowItems ) && (
|
||||
<div
|
||||
className="wc-block-components-shipping-rates-control__package-title"
|
||||
dangerouslySetInnerHTML={ {
|
||||
|
@ -78,7 +93,7 @@ export const ShippingRatesControlPackage = ( {
|
|||
} }
|
||||
/>
|
||||
) }
|
||||
{ showItems && (
|
||||
{ shouldShowItems && (
|
||||
<ul className="wc-block-components-shipping-rates-control__package-items">
|
||||
{ Object.values( packageData.items ).map( ( v ) => {
|
||||
const name = decodeEntities( v.name );
|
||||
|
@ -127,11 +142,15 @@ export const ShippingRatesControlPackage = ( {
|
|||
renderOption={ renderOption }
|
||||
/>
|
||||
);
|
||||
if ( collapsible ) {
|
||||
if ( shouldBeCollapsible ) {
|
||||
return (
|
||||
<Panel
|
||||
className="wc-block-components-shipping-rates-control__package"
|
||||
initialOpen={ ! collapse }
|
||||
// initialOpen remembers only the first value provided to it, so by the
|
||||
// time we know we have several packages, initialOpen would be hardcoded to true.
|
||||
// If we're rendering a panel, we're more likely rendering several
|
||||
// packages and we want to them to be closed initially.
|
||||
initialOpen={ false }
|
||||
title={ header }
|
||||
>
|
||||
{ body }
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.wc-block-components-panel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-panel__button {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
|
|
|
@ -19,13 +19,13 @@ import { ReactElement } from 'react';
|
|||
*/
|
||||
import ShippingRatesControlPackage, {
|
||||
PackageRateRenderOption,
|
||||
TernaryFlag,
|
||||
} from '../shipping-rates-control-package';
|
||||
|
||||
interface PackagesProps {
|
||||
packages: CartResponseShippingRate[];
|
||||
collapse?: boolean;
|
||||
collapsible?: boolean;
|
||||
showItems?: boolean;
|
||||
collapsible?: TernaryFlag;
|
||||
showItems?: TernaryFlag;
|
||||
noResultsMessage: ReactElement;
|
||||
renderOption: PackageRateRenderOption;
|
||||
}
|
||||
|
@ -35,18 +35,15 @@ interface PackagesProps {
|
|||
*
|
||||
* @param {Object} props Incoming props.
|
||||
* @param {Array} props.packages Array of packages.
|
||||
* @param {boolean} props.collapsible If the package should be rendered as a
|
||||
* @param {boolean|undefined} props.collapsible If the package should be rendered as a
|
||||
* @param {ReactElement} props.noResultsMessage Rendered when there are no rates in a package.
|
||||
* collapsible panel.
|
||||
* @param {boolean} props.collapse If the panel should be collapsed by default,
|
||||
* only works if collapsible is true.
|
||||
* @param {boolean} props.showItems If we should items below the package name.
|
||||
* @param {boolean|undefined} props.showItems If we should items below the package name.
|
||||
* @param {PackageRateRenderOption} [props.renderOption] Function to render a shipping rate.
|
||||
* @return {JSX.Element|null} Rendered components.
|
||||
*/
|
||||
const Packages = ( {
|
||||
packages,
|
||||
collapse,
|
||||
showItems,
|
||||
collapsible,
|
||||
noResultsMessage,
|
||||
|
@ -63,9 +60,8 @@ const Packages = ( {
|
|||
key={ packageId }
|
||||
packageId={ packageId }
|
||||
packageData={ packageData }
|
||||
collapsible={ !! collapsible }
|
||||
collapse={ !! collapse }
|
||||
showItems={ showItems || packages.length > 1 }
|
||||
collapsible={ collapsible }
|
||||
showItems={ showItems }
|
||||
noResultsMessage={ noResultsMessage }
|
||||
renderOption={ renderOption }
|
||||
/>
|
||||
|
@ -75,7 +71,8 @@ const Packages = ( {
|
|||
};
|
||||
|
||||
interface ShippingRatesControlProps {
|
||||
collapsible?: boolean;
|
||||
collapsible?: TernaryFlag;
|
||||
showItems?: TernaryFlag;
|
||||
shippingRates: CartResponseShippingRate[];
|
||||
className?: string;
|
||||
isLoadingRates: boolean;
|
||||
|
@ -86,20 +83,22 @@ interface ShippingRatesControlProps {
|
|||
/**
|
||||
* Renders the shipping rates control element.
|
||||
*
|
||||
* @param {Object} props Incoming props.
|
||||
* @param {Array} props.shippingRates Array of packages containing shipping rates.
|
||||
* @param {boolean} props.isLoadingRates True when rates are being loaded.
|
||||
* @param {string} props.className Class name for package rates.
|
||||
* @param {boolean} [props.collapsible] If true, when multiple packages are rendered they can be toggled open and closed.
|
||||
* @param {ReactElement} props.noResultsMessage Rendered when there are no packages.
|
||||
* @param {Function} [props.renderOption] Function to render a shipping rate.
|
||||
* @param {string} [props.context] String equal to the block name where the Slot is rendered
|
||||
* @param {Object} props Incoming props.
|
||||
* @param {Array} props.shippingRates Array of packages containing shipping rates.
|
||||
* @param {boolean} props.isLoadingRates True when rates are being loaded.
|
||||
* @param {string} props.className Class name for package rates.
|
||||
* @param {boolean|undefined} [props.collapsible] If true, when multiple packages are rendered they can be toggled open and closed.
|
||||
* @param {boolean|undefined} [props.showItems] If true, when multiple packages are rendered, you can see each package's items.
|
||||
* @param {ReactElement} props.noResultsMessage Rendered when there are no packages.
|
||||
* @param {Function} [props.renderOption] Function to render a shipping rate.
|
||||
* @param {string} [props.context] String equal to the block name where the Slot is rendered
|
||||
*/
|
||||
const ShippingRatesControl = ( {
|
||||
shippingRates,
|
||||
isLoadingRates,
|
||||
className,
|
||||
collapsible = false,
|
||||
collapsible,
|
||||
showItems,
|
||||
noResultsMessage,
|
||||
renderOption,
|
||||
context,
|
||||
|
@ -157,6 +156,7 @@ const ShippingRatesControl = ( {
|
|||
const slotFillProps = {
|
||||
className,
|
||||
collapsible,
|
||||
showItems,
|
||||
noResultsMessage,
|
||||
renderOption,
|
||||
extensions,
|
||||
|
@ -165,7 +165,6 @@ const ShippingRatesControl = ( {
|
|||
ShippingRatesControlPackage,
|
||||
},
|
||||
context,
|
||||
shippingRates,
|
||||
};
|
||||
const { isEditor } = useEditorContext();
|
||||
|
||||
|
@ -191,7 +190,6 @@ const ShippingRatesControl = ( {
|
|||
/>
|
||||
<ExperimentalOrderShippingPackages>
|
||||
<Packages
|
||||
showItems={ shippingRates.length > 1 }
|
||||
packages={ shippingRates }
|
||||
noResultsMessage={ noResultsMessage }
|
||||
renderOption={ renderOption }
|
||||
|
|
|
@ -23,7 +23,6 @@ const ShippingRateSelector = ( {
|
|||
<legend className="screen-reader-text">{ legend }</legend>
|
||||
<ShippingRatesControl
|
||||
className="wc-block-components-totals-shipping__options"
|
||||
collapsible={ true }
|
||||
noResultsMessage={
|
||||
<Notice
|
||||
isDismissible={ false }
|
||||
|
|
|
@ -99,6 +99,7 @@ const Block = (): JSX.Element | null => {
|
|||
</Notice>
|
||||
}
|
||||
renderOption={ renderShippingRatesControlOption }
|
||||
collapsible={ false }
|
||||
shippingRates={ shippingRates }
|
||||
isLoadingRates={ isLoadingRates }
|
||||
context="woocommerce/checkout"
|
||||
|
|
|
@ -47,9 +47,9 @@ Checkout:
|
|||
|
||||
### Passed parameters
|
||||
|
||||
- `collapsible`: `Boolean` If a shipping package panel should be collapsible or not, this is false in Checkout and true in Cart.
|
||||
- `collapse`: `Boolean` If a panel should be collapsed by default, this is true if there's more than 1 fill registered (Core Shipping options are registered as a fill and they're counted).
|
||||
- `showItems`: `Boolean` If we should show the content of each package, this is true if there's more than 1 fill registered (Core Shipping options are registered as a fill and they're counted).
|
||||
- `collapsible`: `Boolean|undefined` If a shipping package panel should be collapsible or not, this is false in Checkout and undefined in Cart.
|
||||
- `collapse`: `Boolean` If a panel should be collapsed by default, this is true if if panels are collapsible.
|
||||
- `showItems`: `Boolean|undefined` If we should show the content of each package, this is undefined in Cart and Checkout and is left to the actual package logic to decide.
|
||||
- `noResultsMessage`: A React element that you can render if there are no shipping options.
|
||||
- `renderOption`: a render function that takes a rate object and returns a render option.
|
||||
- `cart`: `wc/store/cart` data but in `camelCase` instead of `snake_case`. [Object breakdown.](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/c00da597efe4c16fcf5481c213d8052ec5df3766/assets/js/type-defs/cart.ts#L172-L188)
|
||||
|
|
|
@ -6,7 +6,7 @@ import classnames from 'classnames';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { createSlotFill, useSlot } from '../../slot';
|
||||
import { createSlotFill } from '../../slot';
|
||||
|
||||
const slotName = '__experimentalOrderShippingPackages';
|
||||
const {
|
||||
|
@ -16,17 +16,15 @@ const {
|
|||
|
||||
const Slot = ( {
|
||||
className,
|
||||
collapsible,
|
||||
noResultsMessage,
|
||||
renderOption,
|
||||
extensions,
|
||||
cart,
|
||||
components,
|
||||
context,
|
||||
shippingRates,
|
||||
collapsible,
|
||||
showItems,
|
||||
} ) => {
|
||||
const { fills } = useSlot( slotName );
|
||||
const hasMultiplePackages = fills.length > 1 || shippingRates?.length > 1;
|
||||
return (
|
||||
<OrderShippingPackagesSlot
|
||||
className={ classnames(
|
||||
|
@ -34,9 +32,9 @@ const Slot = ( {
|
|||
className
|
||||
) }
|
||||
fillProps={ {
|
||||
collapse: collapsible,
|
||||
collapsible,
|
||||
collapse: hasMultiplePackages,
|
||||
showItems: hasMultiplePackages,
|
||||
showItems,
|
||||
noResultsMessage,
|
||||
renderOption,
|
||||
extensions,
|
||||
|
|
Loading…
Reference in New Issue