diff --git a/src/builders.spec.tsx b/src/builders.spec.tsx index 44f16be..16bba29 100644 --- a/src/builders.spec.tsx +++ b/src/builders.spec.tsx @@ -107,16 +107,15 @@ describe("xstate-tree builders", () => { const hist: XstateTreeHistory = createMemoryHistory(); const createRoute = buildCreateRoute(() => hist, "/"); - const fooRoute = createRoute.simpleRoute()({ - url: "/foo/", - event: "GO_TO_FOO", - }); - const barRoute = createRoute.simpleRoute()({ - url: "/bar/", - event: "GO_TO_BAR", - }); - it("takes a mapping of routes to machines and returns a machine that invokes those machines when those routes events are broadcast", async () => { + const fooRoute = createRoute.simpleRoute()({ + url: "/foo/", + event: "GO_TO_FOO", + }); + const barRoute = createRoute.simpleRoute()({ + url: "/bar/", + event: "GO_TO_BAR", + }); const FooMachine = viewToMachine(() =>
foo
); const BarMachine = viewToMachine(() =>
bar
); @@ -142,5 +141,40 @@ describe("xstate-tree builders", () => { act(() => barRoute.navigate()); await waitFor(() => getByText("bar")); }); + + it("handles routing events that contain . in them", async () => { + const fooRoute = createRoute.simpleRoute()({ + url: "/foo/", + event: "routing.foo", + }); + const barRoute = createRoute.simpleRoute()({ + url: "/bar/", + event: "routing.bar", + }); + const FooMachine = viewToMachine(() =>
foo
); + const BarMachine = viewToMachine(() =>
bar
); + + const routingMachine = buildRoutingMachine([fooRoute, barRoute], { + "routing.foo": FooMachine, + "routing.bar": BarMachine, + }); + + const Root = buildRootComponent({ + machine: routingMachine, + routing: { + history: hist, + basePath: "/", + routes: [fooRoute, barRoute], + }, + }); + + const { getByText } = render(); + + act(() => fooRoute.navigate()); + await waitFor(() => getByText("foo")); + + act(() => barRoute.navigate()); + await waitFor(() => getByText("bar")); + }); }); }); diff --git a/src/builders.tsx b/src/builders.tsx index 5865e5c..fa34a1b 100644 --- a/src/builders.tsx +++ b/src/builders.tsx @@ -111,13 +111,22 @@ export function buildRoutingMachine( _routes: TRoutes, mappings: Record ): AnyXstateTreeMachine { + /** + * States in xstate can't contain dots, since the states are named after the routing events + * if the routing event contains a dot that will make a state with a dot in it + * this function sanitizes the event name to remove dots and is used for the state names and targets + */ + function sanitizeEventName(event: string) { + return event.replace(/\.([a-zA-Z])/g, (_, letter) => letter.toUpperCase()); + } + const contentSlot = singleSlot("Content"); const mappingsToStates = Object.entries( mappings ).reduce((acc, [event, _machine]) => { return { ...acc, - [event]: { + [sanitizeEventName(event)]: { invoke: { src: event, id: contentSlot.getId(), @@ -130,7 +139,7 @@ export function buildRoutingMachine( (acc, event) => ({ ...acc, [event]: { - target: `.${event}`, + target: `.${sanitizeEventName(event)}`, }, }), {}