diff --git a/packages/js/components/changelog/try-product-mvp-slotfill-experiments b/packages/js/components/changelog/try-product-mvp-slotfill-experiments
new file mode 100644
index 00000000000..7df70dd246d
--- /dev/null
+++ b/packages/js/components/changelog/try-product-mvp-slotfill-experiments
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Adding experimental component SlotContext
diff --git a/packages/js/components/src/index.ts b/packages/js/components/src/index.ts
index 94562eb2dce..8cea1e13515 100644
--- a/packages/js/components/src/index.ts
+++ b/packages/js/components/src/index.ts
@@ -92,3 +92,9 @@ export {
ProductFieldSection as __experimentalProductFieldSection,
} from './product-section-layout';
export * from './product-fields';
+export {
+ SlotContextProvider,
+ useSlotContext,
+ SlotContextType,
+ SlotContextHelpersType,
+} from './slot-context';
diff --git a/packages/js/components/src/slot-context/index.ts b/packages/js/components/src/slot-context/index.ts
new file mode 100644
index 00000000000..86ac84d1fdf
--- /dev/null
+++ b/packages/js/components/src/slot-context/index.ts
@@ -0,0 +1 @@
+export * from './slot-context';
diff --git a/packages/js/components/src/slot-context/slot-context.tsx b/packages/js/components/src/slot-context/slot-context.tsx
new file mode 100644
index 00000000000..49b70ccb86a
--- /dev/null
+++ b/packages/js/components/src/slot-context/slot-context.tsx
@@ -0,0 +1,104 @@
+/**
+ * External dependencies
+ */
+import {
+ createElement,
+ createContext,
+ useContext,
+ useCallback,
+ useReducer,
+} from '@wordpress/element';
+
+type FillConfigType = {
+ visible: boolean;
+};
+
+type FillType = Record< string, FillConfigType >;
+
+type FillCollection = readonly ( readonly JSX.Element[] )[];
+
+export type SlotContextHelpersType = {
+ hideFill: ( id: string ) => void;
+ showFill: ( id: string ) => void;
+ getFills: () => FillType;
+};
+
+export type SlotContextType = {
+ fills: FillType;
+ getFillHelpers: () => SlotContextHelpersType;
+ registerFill: ( id: string ) => void;
+ filterRegisteredFills: ( fillsArrays: FillCollection ) => FillCollection;
+};
+
+const SlotContext = createContext< SlotContextType | undefined >( undefined );
+
+export const SlotContextProvider: React.FC = ( { children } ) => {
+ const [ fills, updateFills ] = useReducer(
+ ( data: FillType, updates: FillType ) => ( { ...data, ...updates } ),
+ {}
+ );
+
+ const updateFillConfig = (
+ id: string,
+ update: Partial< FillConfigType >
+ ) => {
+ if ( ! fills[ id ] ) {
+ throw new Error( `No fill found with ID: ${ id }` );
+ }
+ updateFills( { [ id ]: { ...fills[ id ], ...update } } );
+ };
+
+ const registerFill = useCallback(
+ ( id: string ) => {
+ if ( fills[ id ] ) {
+ return;
+ }
+ updateFills( { [ id ]: { visible: true } } );
+ },
+ [ fills ]
+ );
+
+ const hideFill = useCallback(
+ ( id: string ) => updateFillConfig( id, { visible: false } ),
+ [ fills ]
+ );
+
+ const showFill = useCallback(
+ ( id: string ) => updateFillConfig( id, { visible: true } ),
+ [ fills ]
+ );
+
+ const getFills = useCallback( () => ( { ...fills } ), [ fills ] );
+
+ return (
+
+ fills[ arr[ 0 ].props._id ]?.visible !== false
+ );
+ },
+ fills,
+ } }
+ >
+ { children }
+
+ );
+};
+
+export const useSlotContext = () => {
+ const slotContext = useContext( SlotContext );
+
+ if ( slotContext === undefined ) {
+ throw new Error(
+ 'useSlotContext must be used within a SlotContextProvider'
+ );
+ }
+
+ return slotContext;
+};
diff --git a/packages/js/components/src/utils.tsx b/packages/js/components/src/utils.tsx
index 682a1a0aded..34117864d5d 100644
--- a/packages/js/components/src/utils.tsx
+++ b/packages/js/components/src/utils.tsx
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import React, { isValidElement, Fragment } from 'react';
+import { isValidElement, Fragment } from 'react';
import { Slot, Fill } from '@wordpress/components';
import { cloneElement, createElement } from '@wordpress/element';
@@ -13,15 +13,16 @@ import { cloneElement, createElement } from '@wordpress/element';
* @param {Array} props - Fill props.
* @return {Node} Node.
*/
-function createOrderedChildren< T = Fill.Props >(
+function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
children: React.ReactNode,
order: number,
- props: T
+ props: T,
+ injectProps?: S
) {
if ( typeof children === 'function' ) {
- return cloneElement( children( props ), { order } );
+ return cloneElement( children( props ), { order, ...injectProps } );
} else if ( isValidElement( children ) ) {
- return cloneElement( children, { ...props, order } );
+ return cloneElement( children, { ...props, order, ...injectProps } );
}
throw Error( 'Invalid children type' );
}
diff --git a/packages/js/components/src/woo-product-field-item/woo-product-field-item.tsx b/packages/js/components/src/woo-product-field-item/woo-product-field-item.tsx
index 3a62adf24df..2db6a28165d 100644
--- a/packages/js/components/src/woo-product-field-item/woo-product-field-item.tsx
+++ b/packages/js/components/src/woo-product-field-item/woo-product-field-item.tsx
@@ -9,6 +9,7 @@ import { createElement, Children } from '@wordpress/element';
* Internal dependencies
*/
import { createOrderedChildren, sortFillsByOrder } from '../utils';
+import { useSlotContext, SlotContextHelpersType } from '../slot-context';
type WooProductFieldItemProps = {
id: string;
@@ -23,36 +24,55 @@ type WooProductFieldSlotProps = {
export const WooProductFieldItem: React.FC< WooProductFieldItemProps > & {
Slot: React.FC< Slot.Props & WooProductFieldSlotProps >;
-} = ( { children, order = 20, section } ) => (
-
- { ( fillProps: Fill.Props ) => {
- return createOrderedChildren< Fill.Props >(
- children,
- order,
- fillProps
- );
- } }
-
-);
+} = ( { children, order = 20, section, id } ) => {
+ const { registerFill, getFillHelpers } = useSlotContext();
-WooProductFieldItem.Slot = ( { fillProps, section } ) => (
-
- { ( fills ) => {
- if ( ! sortFillsByOrder ) {
- return null;
- }
+ registerFill( id );
- return Children.map(
- sortFillsByOrder( fills )?.props.children,
- ( child ) => (
-
- { child }
-
- )
- );
- } }
-
-);
+ return (
+
+ { ( fillProps: Fill.Props ) => {
+ return createOrderedChildren<
+ Fill.Props & SlotContextHelpersType,
+ { _id: string }
+ >(
+ children,
+ order,
+ {
+ ...fillProps,
+ ...getFillHelpers(),
+ },
+ { _id: id }
+ );
+ } }
+
+ );
+};
+
+WooProductFieldItem.Slot = ( { fillProps, section } ) => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const { filterRegisteredFills } = useSlotContext();
+
+ return (
+
+ { ( fills ) => {
+ if ( ! sortFillsByOrder ) {
+ return null;
+ }
+
+ return Children.map(
+ sortFillsByOrder( filterRegisteredFills( fills ) )?.props
+ .children,
+ ( child ) => (
+
+ { child }
+
+ )
+ );
+ } }
+
+ );
+};
diff --git a/plugins/woocommerce-admin/client/products/fills/details-section/details-section-fills.tsx b/plugins/woocommerce-admin/client/products/fills/details-section/details-section-fills.tsx
index 73646b5d61c..bf203331e30 100644
--- a/plugins/woocommerce-admin/client/products/fills/details-section/details-section-fills.tsx
+++ b/plugins/woocommerce-admin/client/products/fills/details-section/details-section-fills.tsx
@@ -85,7 +85,5 @@ const DetailsSection = () => (
registerPlugin( 'wc-admin-product-editor-details-section', {
// @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated.
scope: 'woocommerce-product-editor',
- render: () => {
- return ;
- },
+ render: () => ,
} );
diff --git a/plugins/woocommerce-admin/client/products/product-form.tsx b/plugins/woocommerce-admin/client/products/product-form.tsx
index 60abf3f26b5..0f1a171fafb 100644
--- a/plugins/woocommerce-admin/client/products/product-form.tsx
+++ b/plugins/woocommerce-admin/client/products/product-form.tsx
@@ -5,6 +5,7 @@ import {
Form,
FormRef,
__experimentalWooProductSectionItem as WooProductSectionItem,
+ SlotContextProvider,
} from '@woocommerce/components';
import { PartialProduct, Product } from '@woocommerce/data';
import { PluginArea } from '@wordpress/plugins';
@@ -31,60 +32,64 @@ export const ProductForm: React.FC< {
formRef?: Ref< FormRef< Partial< Product > > >;
} > = ( { product, formRef } ) => {
return (
-
+
);
};
diff --git a/plugins/woocommerce/changelog/try-product-mvp-slotfill-experiments b/plugins/woocommerce/changelog/try-product-mvp-slotfill-experiments
new file mode 100644
index 00000000000..735fdc59890
--- /dev/null
+++ b/plugins/woocommerce/changelog/try-product-mvp-slotfill-experiments
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Trying experimental slot context with product editor fills.