From c3f7e2e268b75d1982068703430ed4f607430562 Mon Sep 17 00:00:00 2001 From: Taylor Lodge Date: Fri, 16 Aug 2024 11:51:27 +1200 Subject: [PATCH] fix(builders): buildRoutingMachine not working with routing events containing dots --- src/builders.spec.tsx | 51 +++++++++++++++++++++++++++++++++++-------- src/builders.tsx | 13 +++++++++-- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/builders.spec.tsx b/src/builders.spec.tsx index 14cd963..c3c14bc 100644 --- a/src/builders.spec.tsx +++ b/src/builders.spec.tsx @@ -34,16 +34,16 @@ 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
); @@ -66,5 +66,38 @@ 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(routingMachine, { + 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 b03fbff..a22d920 100644 --- a/src/builders.tsx +++ b/src/builders.tsx @@ -304,13 +304,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: (_ctx, e) => { return mappings[e.type as TRoutes[number]["event"]]; @@ -325,7 +334,7 @@ export function buildRoutingMachine( (acc, event) => ({ ...acc, [event]: { - target: `.${event}`, + target: `.${sanitizeEventName(event)}`, }, }), {}