diff --git a/.eslintignore b/.eslintignore index 1e41797..6c6501a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ *.snap +*.typegen.ts README.md \ No newline at end of file diff --git a/src/builders.spec.tsx b/src/builders.spec.tsx new file mode 100644 index 0000000..c0be825 --- /dev/null +++ b/src/builders.spec.tsx @@ -0,0 +1,18 @@ +import { render, waitFor } from "@testing-library/react"; +import React from "react"; + +import { viewToMachine } from "./builders"; +import { buildRootComponent } from "./xstateTree"; + +describe("xstate-tree builders", () => { + describe("viewToMachine", () => { + it("takes a React view and wraps it in an xstate-tree machine that renders that view", async () => { + const ViewMachine = viewToMachine(() =>
hello world
); + const Root = buildRootComponent(ViewMachine); + + const { getByText } = render(); + + await waitFor(() => getByText("hello world")); + }); + }); +}); diff --git a/src/builders.ts b/src/builders.ts index 14a57cc..319d44d 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -1,12 +1,13 @@ import React from "react"; -import type { - EventObject, - StateMachine, - AnyStateMachine, - ContextFrom, - EventFrom, - InterpreterFrom, - AnyFunction, +import { + type EventObject, + type StateMachine, + type AnyStateMachine, + type ContextFrom, + type EventFrom, + type InterpreterFrom, + type AnyFunction, + createMachine, } from "xstate"; import { Slot } from "./slots"; @@ -22,6 +23,7 @@ import { XStateTreeMachineMetaV1, XstateTreeMachineStateSchemaV1, XstateTreeMachineStateSchemaV2, + AnyXstateTreeMachine, } from "./types"; /** @@ -261,3 +263,20 @@ export function createXStateTreeMachine< return machine; } + +/** + * @public + * + * Simple utility builder to aid in integrating existing React views with xstate-tree + * + * @param view - the React view you want to invoke in an xstate machine + * @returns The view wrapped into an xstate-tree machine, ready to be invoked by other xstate machines or used with `buildRootComponent` + */ +export function viewToMachine(view: () => JSX.Element): AnyXstateTreeMachine { + return createXStateTreeMachine( + createMachine({ initial: "idle", states: { idle: {} } }), + { + View: view, + } + ); +} diff --git a/src/test-app/AppMachine.typegen.ts b/src/test-app/AppMachine.typegen.ts index 1d46e11..90e8106 100644 --- a/src/test-app/AppMachine.typegen.ts +++ b/src/test-app/AppMachine.typegen.ts @@ -1,27 +1,35 @@ -// This file was automatically generated. Edits will be overwritten -export interface Typegen0 { - "@@xstate/typegen": true; - internalEvents: { - "xstate.init": { type: "xstate.init" }; - }; - invokeSrcNameMap: { - OtherMachine: "done.invoke.app.otherScreen:invocation[0]"; - TodosMachine: "done.invoke.app.todos:invocation[0]"; - }; - missingImplementations: { - actions: never; - services: never; - guards: never; - delays: never; - }; - eventsCausingActions: {}; - eventsCausingServices: { - OtherMachine: "GO_SETTINGS"; - TodosMachine: "GO_HOME"; - }; - eventsCausingGuards: {}; - eventsCausingDelays: {}; - matchesStates: "otherScreen" | "todos" | "waitingForRoute"; - tags: never; -} + // This file was automatically generated. Edits will be overwritten + + export interface Typegen0 { + '@@xstate/typegen': true; + internalEvents: { + "xstate.init": { type: "xstate.init" }; + }; + invokeSrcNameMap: { + "OtherMachine": "done.invoke.app.otherScreen:invocation[0]"; +"TodosMachine": "done.invoke.app.todos:invocation[0]"; + }; + missingImplementations: { + actions: never; + delays: never; + guards: never; + services: never; + }; + eventsCausingActions: { + + }; + eventsCausingDelays: { + + }; + eventsCausingGuards: { + + }; + eventsCausingServices: { + "OtherMachine": "GO_SETTINGS"; +"TodosMachine": "GO_HOME"; + }; + matchesStates: "otherScreen" | "todos" | "waitingForRoute"; + tags: never; + } + \ No newline at end of file diff --git a/src/xstateTree.tsx b/src/xstateTree.tsx index fda1a1e..c3bbb5b 100644 --- a/src/xstateTree.tsx +++ b/src/xstateTree.tsx @@ -78,6 +78,7 @@ const getViewForInterpreter = memoize( useEffect(() => { if (activeRouteEvents) { activeRouteEvents.forEach((event) => { + // @ts-ignore fixed in v5 branch if (interpreter.state.nextEvents.includes(event.type)) { interpreter.send(event); } diff --git a/xstate-tree.api.md b/xstate-tree.api.md index d34658c..0a7aa3d 100644 --- a/xstate-tree.api.md +++ b/xstate-tree.api.md @@ -5,7 +5,7 @@ ```ts import { AnyEventObject } from 'xstate'; -import type { AnyFunction } from 'xstate'; +import { AnyFunction } from 'xstate'; import { AnyStateMachine } from 'xstate'; import { BaseActionObject } from 'xstate'; import { ComponentPropsWithRef } from 'react'; @@ -13,7 +13,7 @@ import { ContextFrom } from 'xstate'; import { EventFrom } from 'xstate'; import { EventObject } from 'xstate'; import { History as History_2 } from 'history'; -import type { InterpreterFrom } from 'xstate'; +import { InterpreterFrom } from 'xstate'; import { JSXElementConstructor } from 'react'; import { ParsedQuery } from 'query-string'; import { default as React_2 } from 'react'; @@ -439,6 +439,9 @@ export type ViewProps JSX.Element): AnyXstateTreeMachine; + // @public (undocumented) export type XstateTreeHistory = History_2<{ meta?: T;