import type { StoryContext, StoryFn } from "@storybook/react"; import React from "react"; import { createActorKitContext } from "./createActorKitContext"; import { createActorKitMockClient } from "./createActorKitMockClient"; import type { AnyActorKitStateMachine } from "./types"; import { ActorKitParameters } from "./storybook"; /** * Storybook decorator that sets up actor-kit state machines. * * There are two main patterns for testing with actor-kit: * * 1. Static Stories (Use parameters.actorKit + within): * - Use this decorator with parameters.actorKit * - Use `within(canvasElement)` in play functions * - Good for simple stories that don't need state manipulation * * 2. Interactive Stories (Use mount + direct client): * - Don't use this decorator * - Create client manually and use mount in play function * - Good for stories that need to manipulate state * * @example * ```tsx * // Pattern 1: Static Story * export const Static: Story = { * parameters: { * actorKit: { * session: { * "session-123": { ... } * } * } * }, * play: async ({ canvasElement }) => { * const canvas = within(canvasElement); * // Test UI state... * } * }; * * // Pattern 2: Interactive Story * export const Interactive: Story = { * play: async ({ canvasElement, mount }) => { * const client = createActorKitMockClient({...}); * const canvas = within(canvasElement); * * await mount( * * * * ); * * // Now you can manipulate client state... * client.produce((draft) => { ... }); * } * }; * ``` */ export const withActorKit = ({ actorType, context, }: { actorType: string; context: ReturnType>; }) => { return (Story: StoryFn, storyContext: StoryContext): React.ReactElement => { const actorKitParams = storyContext.parameters?.actorKit as | ActorKitParameters["actorKit"] | undefined; // If no params provided, just render the story without any providers if (!actorKitParams?.[actorType]) { return ; } // Create nested providers for each actor ID const actorSnapshots = actorKitParams[actorType]; // Recursively nest providers const createNestedProviders = ( actorIds: string[], index: number, children: React.ReactNode ): React.ReactElement => { if (index >= actorIds.length) { return children as React.ReactElement; } const actorId = actorIds[index]; const snapshot = actorSnapshots[actorId]; const client = createActorKitMockClient({ initialSnapshot: snapshot, }); return ( {createNestedProviders(actorIds, index + 1, children)} ); }; return createNestedProviders(Object.keys(actorSnapshots), 0, ); }; };