From 58cf23f1dbdb4a2ea53883704b980209c525fb07 Mon Sep 17 00:00:00 2001
From: Thomas Roberts <5656702+opr@users.noreply.github.com>
Date: Mon, 13 Mar 2023 12:00:02 +0000
Subject: [PATCH] Add `ExperimentalOrderLocalPickupPackages` Slot/Fill (#8636)

* Add ExperimentalOrderLocalPickupPackages slot fill

* Render ExperimentalOrderLocalPickupPackages in local pickup block

* Update docs to include ExperimentalOrderLocalPickupPackages

* Use LocalPickupSelect component when rendering local pickup options
---
 .../checkout-pickup-options-block/block.tsx   | 43 ++++++++++----
 .../checkout-block/available-slot-fills.md    | 15 +++++
 packages/checkout/components/index.js         |  1 +
 .../order-local-pickup-packages/index.tsx     | 56 +++++++++++++++++++
 4 files changed, 103 insertions(+), 12 deletions(-)
 create mode 100644 packages/checkout/components/order-local-pickup-packages/index.tsx

diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx
index 4dea0a89bdb..342bbbec34c 100644
--- a/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx
+++ b/assets/js/blocks/checkout/inner-blocks/checkout-pickup-options-block/block.tsx
@@ -8,21 +8,23 @@ import {
 	useCallback,
 	createInterpolateElement,
 } from '@wordpress/element';
-import { useShippingData } from '@woocommerce/base-context/hooks';
+import { useShippingData, useStoreCart } from '@woocommerce/base-context/hooks';
 import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
 import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
 import { decodeEntities } from '@wordpress/html-entities';
 import { getSetting } from '@woocommerce/settings';
 import { Icon, mapMarker } from '@wordpress/icons';
-import RadioControl from '@woocommerce/base-components/radio-control';
 import type { RadioControlOption } from '@woocommerce/base-components/radio-control/types';
 import { CartShippingPackageShippingRate } from '@woocommerce/types';
 import { isPackageRateCollectable } from '@woocommerce/base-utils';
+import { ExperimentalOrderLocalPickupPackages } from '@woocommerce/blocks-checkout';
+import { LocalPickupSelect } from '@woocommerce/base-components/cart-checkout/local-pickup-select';
 
 /**
  * Internal dependencies
  */
 import './style.scss';
+import ShippingRatesControlPackage from '../../../../base/components/cart-checkout/shipping-rates-control-package';
 
 const getPickupLocation = (
 	option: CartShippingPackageShippingRate
@@ -133,6 +135,20 @@ const Block = (): JSX.Element | null => {
 		[ selectShippingRate ]
 	);
 
+	// Prepare props to pass to the ExperimentalOrderLocalPickupPackages slot fill.
+	// We need to pluck out receiveCart.
+	// eslint-disable-next-line no-unused-vars
+	const { extensions, receiveCart, ...cart } = useStoreCart();
+	const slotFillProps = {
+		extensions,
+		cart,
+		components: {
+			ShippingRatesControlPackage,
+			LocalPickupSelect,
+		},
+		renderPickupLocation,
+	};
+
 	// Update the selected option if there is no rate selected on mount.
 	useEffect( () => {
 		if ( ! selectedOption && pickupLocations[ 0 ] ) {
@@ -142,16 +158,19 @@ const Block = (): JSX.Element | null => {
 	}, [ onSelectRate, pickupLocations, selectedOption ] );
 
 	return (
-		<RadioControl
-			onChange={ ( value: string ) => {
-				setSelectedOption( value );
-				onSelectRate( value );
-			} }
-			selected={ selectedOption }
-			options={ pickupLocations.map( ( location ) =>
-				renderPickupLocation( location, shippingRates.length )
-			) }
-		/>
+		<>
+			<ExperimentalOrderLocalPickupPackages.Slot { ...slotFillProps } />
+			<ExperimentalOrderLocalPickupPackages>
+				<LocalPickupSelect
+					title={ shippingRates[ 0 ].name }
+					setSelectedOption={ setSelectedOption }
+					onSelectRate={ onSelectRate }
+					selectedOption={ selectedOption }
+					renderPickupLocation={ renderPickupLocation }
+					pickupLocations={ pickupLocations }
+				/>
+			</ExperimentalOrderLocalPickupPackages>
+		</>
 	);
 };
 
diff --git a/docs/third-party-developers/extensibility/checkout-block/available-slot-fills.md b/docs/third-party-developers/extensibility/checkout-block/available-slot-fills.md
index 5f3500f3461..b5071eec4e3 100644
--- a/docs/third-party-developers/extensibility/checkout-block/available-slot-fills.md
+++ b/docs/third-party-developers/extensibility/checkout-block/available-slot-fills.md
@@ -57,6 +57,21 @@ Checkout:
 -   `components`: an object containing components you can use to render your own shipping rates, it contains `ShippingRatesControlPackage`.
 -   `context`, equal to the name of the Block in which the fill is rendered: `woocommerce/cart` or `woocommerce/checkout`
 
+## ExperimentalOrderLocalPickupPackages
+
+This slot renders inside the Checkout Pickup Options block in the Checkout block. It does not render in the Cart block.
+
+Checkout:
+
+![Example of ExperimentalOrderLocalPickupPackages in the Checkout block](https://user-images.githubusercontent.com/5656702/222814945-a449d016-0621-4a70-b0f4-2ae1ce6487f1.png)
+
+### Passed parameters
+
+-   `renderPickupLocation`: a render function that renders the address details of a local pickup 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)
+-   `extensions`: external data registered by third-party developers using `ExtendSchema`, if you used `ExtendSchema` on `wc/store/cart` you would find your data under your namespace here.
+-   `components`: an object containing components you can use to render your own pickup rates, it contains `ShippingRatesControlPackage` and `RadioControl`.
+
 ## ExperimentalDiscountsMeta
 
 This slot renders below the `CouponCode` input.
diff --git a/packages/checkout/components/index.js b/packages/checkout/components/index.js
index eb12c946a46..66f7233175b 100644
--- a/packages/checkout/components/index.js
+++ b/packages/checkout/components/index.js
@@ -3,6 +3,7 @@ export { default as TotalsWrapper } from './totals-wrapper';
 export { default as ExperimentalOrderMeta } from './order-meta';
 export { default as ExperimentalDiscountsMeta } from './discounts-meta';
 export { default as ExperimentalOrderShippingPackages } from './order-shipping-packages';
+export { default as ExperimentalOrderLocalPickupPackages } from './order-local-pickup-packages';
 export { default as Panel } from './panel';
 export { default as Button } from './button';
 export { default as Label } from './label';
diff --git a/packages/checkout/components/order-local-pickup-packages/index.tsx b/packages/checkout/components/order-local-pickup-packages/index.tsx
new file mode 100644
index 00000000000..ef07b6d5fb0
--- /dev/null
+++ b/packages/checkout/components/order-local-pickup-packages/index.tsx
@@ -0,0 +1,56 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+import {
+	Cart,
+	CartShippingPackageShippingRate,
+} from '@woocommerce/type-defs/cart';
+import { Component } from '@wordpress/element';
+import { RadioControlOption } from '@woocommerce/base-components/radio-control/types';
+
+/**
+ * Internal dependencies
+ */
+import { createSlotFill } from '../../slot';
+
+const slotName = '__experimentalOrderLocalPickupPackages';
+const {
+	Fill: ExperimentalOrderLocalPickupPackages,
+	Slot: OrderLocalPickupPackagesSlot,
+	// eslint-disable-next-line @typescript-eslint/naming-convention
+} = createSlotFill( slotName );
+
+interface ExperimentalOrderLocalPickupPackagesProps {
+	extensions: Record< string, unknown >;
+	cart: Cart;
+	components: Record< string, Component >;
+	renderPickupLocation: (
+		option: CartShippingPackageShippingRate,
+		packageCount: number
+	) => RadioControlOption;
+}
+const Slot = ( {
+	extensions,
+	cart,
+	components,
+	renderPickupLocation,
+}: ExperimentalOrderLocalPickupPackagesProps ) => {
+	return (
+		<OrderLocalPickupPackagesSlot
+			className={ classnames(
+				'wc-block-components-local-pickup-rates-control'
+			) }
+			fillProps={ {
+				extensions,
+				cart,
+				components,
+				renderPickupLocation,
+			} }
+		/>
+	);
+};
+
+ExperimentalOrderLocalPickupPackages.Slot = Slot;
+
+export default ExperimentalOrderLocalPickupPackages;