diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 33677f10b58fc..aeb1de80a73c5 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -74,6 +74,7 @@
- `DropdownMenu` v2: add `GroupLabel` subcomponent ([#64854](https://github.com/WordPress/gutenberg/pull/64854)).
- `DropdownMenuV2`: update animation ([#64868](https://github.com/WordPress/gutenberg/pull/64868)).
- `Composite` V2: fix Storybook docgen ([#64682](https://github.com/WordPress/gutenberg/pull/64682)).
+- `Composite` V2: add "With Slot Fill" example ([#65051](https://github.com/WordPress/gutenberg/pull/65051)).
- `Composite` V2: accept store props on top-level component ([#64832](https://github.com/WordPress/gutenberg/pull/64832)).
## 28.6.0 (2024-08-21)
diff --git a/packages/components/src/composite/stories/index.story.tsx b/packages/components/src/composite/stories/index.story.tsx
index 80e3d85e3ce29..2fdec07925549 100644
--- a/packages/components/src/composite/stories/index.story.tsx
+++ b/packages/components/src/composite/stories/index.story.tsx
@@ -1,16 +1,18 @@
/**
* External dependencies
*/
-import type { Meta, StoryFn } from '@storybook/react';
+import type { Meta, StoryFn, StoryContext } from '@storybook/react';
/**
* WordPress dependencies
*/
import { isRTL } from '@wordpress/i18n';
+import { useContext, useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
+import { createSlotFill, Provider as SlotFillProvider } from '../../slot-fill';
import { Composite } from '..';
import { useCompositeStore } from '../store';
import { UseCompositeStorePlaceholder, transform } from './utils';
@@ -204,3 +206,134 @@ Typeahead.parameters = {
},
},
};
+
+const ExampleSlotFill = createSlotFill( 'Example' );
+
+const Slot = () => {
+ const compositeContext = useContext( Composite.Context );
+
+ // Forward the Slot's composite context to the Fill via fillProps, so that
+ // Composite components rendered inside the Fill can work as expected.
+ const fillProps = useMemo(
+ () => ( {
+ forwardedContext: [
+ [ Composite.Context.Provider, { value: compositeContext } ],
+ ],
+ } ),
+ [ compositeContext ]
+ );
+
+ return (
+
+ );
+};
+
+type ForwardedContextTuple< P = {} > = [
+ React.ComponentType< React.PropsWithChildren< P > >,
+ P,
+];
+
+const Fill = ( { children }: { children: React.ReactNode } ) => {
+ const innerMarkup = <>{ children }>;
+
+ return (
+
+ { ( fillProps: { forwardedContext?: ForwardedContextTuple[] } ) => {
+ const { forwardedContext = [] } = fillProps;
+
+ // Render all context providers forwarded by the Slot via fillProps.
+ return forwardedContext.reduce(
+ ( inner: JSX.Element, [ Provider, props ] ) => (
+ { inner }
+ ),
+ innerMarkup
+ );
+ } }
+
+ );
+};
+
+export const WithSlotFill: StoryFn< typeof UseCompositeStorePlaceholder > = (
+ props
+) => {
+ return (
+
+
+ Item one (direct child)
+
+ Item four (direct child)
+
+
+
+ Item two (from slot fill)
+ Item three (from slot fill)
+
+
+ );
+};
+WithSlotFill.args = {
+ ...Default.args,
+};
+WithSlotFill.parameters = {
+ docs: {
+ description: {
+ story: 'When rendering Composite components across a SlotFill, the Composite.Context should be manually forwarded from the Slot to the Fill component.',
+ },
+ source: {
+ transform: ( code: string, storyContext: StoryContext ) => {
+ return `const ExampleSlotFill = createSlotFill( 'Example' );
+
+const Slot = () => {
+ const compositeContext = useContext( Composite.Context );
+
+ // Forward the Slot's composite context to the Fill via fillProps, so that
+ // Composite components rendered inside the Fill can work as expected.
+ const fillProps = useMemo(
+ () => ( {
+ forwardedContext: [
+ [ Composite.Context.Provider, { value: compositeContext } ],
+ ],
+ } ),
+ [ compositeContext ]
+ );
+
+ return (
+
+ );
+};
+
+const Fill = ( { children } ) => {
+ const innerMarkup = <>{ children }>;
+
+ return (
+
+ { ( fillProps ) => {
+ const { forwardedContext = [] } = fillProps;
+
+ // Render all context providers forwarded by the Slot via fillProps.
+ return forwardedContext.reduce(
+ ( inner, [ Provider, props ] ) => (
+ { inner }
+ ),
+ innerMarkup
+ );
+ } }
+
+ );
+};
+
+// In a separate component:
+
+${ transform( code, storyContext ) }`;
+ },
+ },
+ },
+};