Skip to content

Commit c3f7e2e

Browse files
committed
fix(builders): buildRoutingMachine not working with routing events containing dots
1 parent 5950109 commit c3f7e2e

File tree

2 files changed

+53
-11
lines changed

2 files changed

+53
-11
lines changed

src/builders.spec.tsx

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ describe("xstate-tree builders", () => {
3434
const hist: XstateTreeHistory = createMemoryHistory();
3535
const createRoute = buildCreateRoute(() => hist, "/");
3636

37-
const fooRoute = createRoute.simpleRoute()({
38-
url: "/foo/",
39-
event: "GO_TO_FOO",
40-
});
41-
const barRoute = createRoute.simpleRoute()({
42-
url: "/bar/",
43-
event: "GO_TO_BAR",
44-
});
45-
4637
it("takes a mapping of routes to machines and returns a machine that invokes those machines when those routes events are broadcast", async () => {
38+
const fooRoute = createRoute.simpleRoute()({
39+
url: "/foo/",
40+
event: "GO_TO_FOO",
41+
});
42+
const barRoute = createRoute.simpleRoute()({
43+
url: "/bar/",
44+
event: "GO_TO_BAR",
45+
});
46+
4747
const FooMachine = viewToMachine(() => <div>foo</div>);
4848
const BarMachine = viewToMachine(() => <div>bar</div>);
4949

@@ -66,5 +66,38 @@ describe("xstate-tree builders", () => {
6666
act(() => barRoute.navigate());
6767
await waitFor(() => getByText("bar"));
6868
});
69+
70+
it("handles routing events that contain . in them", async () => {
71+
const fooRoute = createRoute.simpleRoute()({
72+
url: "/foo/",
73+
event: "routing.foo",
74+
});
75+
const barRoute = createRoute.simpleRoute()({
76+
url: "/bar/",
77+
event: "routing.bar",
78+
});
79+
80+
const FooMachine = viewToMachine(() => <div>foo</div>);
81+
const BarMachine = viewToMachine(() => <div>bar</div>);
82+
83+
const routingMachine = buildRoutingMachine([fooRoute, barRoute], {
84+
"routing.foo": FooMachine,
85+
"routing.bar": BarMachine,
86+
});
87+
88+
const Root = buildRootComponent(routingMachine, {
89+
history: hist,
90+
basePath: "/",
91+
routes: [fooRoute, barRoute],
92+
});
93+
94+
const { getByText } = render(<Root />);
95+
96+
act(() => fooRoute.navigate());
97+
await waitFor(() => getByText("foo"));
98+
99+
act(() => barRoute.navigate());
100+
await waitFor(() => getByText("bar"));
101+
});
69102
});
70103
});

src/builders.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,13 +304,22 @@ export function buildRoutingMachine<TRoutes extends AnyRoute[]>(
304304
_routes: TRoutes,
305305
mappings: Record<TRoutes[number]["event"], AnyXstateTreeMachine>
306306
): AnyXstateTreeMachine {
307+
/**
308+
* States in xstate can't contain dots, since the states are named after the routing events
309+
* if the routing event contains a dot that will make a state with a dot in it
310+
* this function sanitizes the event name to remove dots and is used for the state names and targets
311+
*/
312+
function sanitizeEventName(event: string) {
313+
return event.replace(/\.([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
314+
}
315+
307316
const contentSlot = singleSlot("Content");
308317
const mappingsToStates = Object.entries<AnyXstateTreeMachine>(
309318
mappings
310319
).reduce((acc, [event, _machine]) => {
311320
return {
312321
...acc,
313-
[event]: {
322+
[sanitizeEventName(event)]: {
314323
invoke: {
315324
src: (_ctx, e) => {
316325
return mappings[e.type as TRoutes[number]["event"]];
@@ -325,7 +334,7 @@ export function buildRoutingMachine<TRoutes extends AnyRoute[]>(
325334
(acc, event) => ({
326335
...acc,
327336
[event]: {
328-
target: `.${event}`,
337+
target: `.${sanitizeEventName(event)}`,
329338
},
330339
}),
331340
{}

0 commit comments

Comments
 (0)