Skip to content

Commit

Permalink
Allow nested absolute route paths
Browse files Browse the repository at this point in the history
Also:

- Add (optional) `<Route index>` prop for index routes
- Return original route objects from `matchRoutes`
- Remove `PartialRouteObject` interface, use `RouteObject` instead
- Remove `createRoutesFromArray`

Fixes #7335
  • Loading branch information
mjackson committed Sep 10, 2021
1 parent f6df069 commit 1ecefcf
Show file tree
Hide file tree
Showing 11 changed files with 463 additions and 201 deletions.
7 changes: 1 addition & 6 deletions packages/react-router-dom/__tests__/link-href-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ describe("Link href", () => {

let anchor = renderer.root.findByType("a");

expect(anchor).not.toBeNull();
expect(anchor.props.href).toEqual("/about");
});

Expand Down Expand Up @@ -96,7 +95,7 @@ describe("Link href", () => {
});

let anchor = renderer.root.findByType("a");
expect(anchor).not.toBeNull();

expect(anchor.props.href).toEqual("/auth/forget-password");
});
});
Expand Down Expand Up @@ -125,7 +124,6 @@ describe("Link href", () => {

let anchor = renderer.root.findByType("a");

expect(anchor).not.toBeNull();
expect(anchor.props.href).toEqual("/home");
});
});
Expand Down Expand Up @@ -154,7 +152,6 @@ describe("Link href", () => {

let anchor = renderer.root.findByType("a");

expect(anchor).not.toBeNull();
expect(anchor.props.href).toEqual("/about");
});
});
Expand Down Expand Up @@ -183,7 +180,6 @@ describe("Link href", () => {

let anchor = renderer.root.findByType("a");

expect(anchor).not.toBeNull();
expect(anchor.props.href).toEqual("/about");
});
});
Expand Down Expand Up @@ -212,7 +208,6 @@ describe("Link href", () => {

let anchor = renderer.root.findByType("a");

expect(anchor).not.toBeNull();
expect(anchor.props.href).toEqual("/app/about");
});
});
Expand Down
3 changes: 0 additions & 3 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
useParams,
useResolvedPath,
useRoutes,
createRoutesFromArray,
createRoutesFromChildren,
generatePath,
matchRoutes,
Expand Down Expand Up @@ -55,7 +54,6 @@ export {
Route,
Router,
Routes,
createRoutesFromArray,
createRoutesFromChildren,
generatePath,
matchRoutes,
Expand All @@ -81,7 +79,6 @@ export type {
Navigator,
OutletProps,
Params,
PartialRouteObject,
PathMatch,
RouteMatch,
RouteObject,
Expand Down
3 changes: 0 additions & 3 deletions packages/react-router-native/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
Route,
Router,
Routes,
createRoutesFromArray,
createRoutesFromChildren,
generatePath,
matchRoutes,
Expand Down Expand Up @@ -48,7 +47,6 @@ export {
Route,
Router,
Routes,
createRoutesFromArray,
createRoutesFromChildren,
generatePath,
matchRoutes,
Expand All @@ -74,7 +72,6 @@ export type {
Navigator,
OutletProps,
Params,
PartialRouteObject,
PathMatch,
RouteMatch,
RouteObject,
Expand Down
46 changes: 46 additions & 0 deletions packages/react-router/__tests__/absolute-path-matching-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { RouteObject } from "react-router";
import { matchRoutes } from "react-router";

describe("absolute path matching", () => {
function pickPaths(routes: RouteObject[], pathname: string) {
let matches = matchRoutes(routes, { pathname });
return matches ? matches.map(match => match.route.path || "") : [];
}

it("matches a nested route with an absolute path", () => {
let routes = [
{
path: "/users",
children: [
{ index: true },
{ path: "add" },
{ path: "remove" },
{ path: "/users/:id" }
]
}
];

expect(pickPaths(routes, "/users")).toEqual(["/users", ""]);
expect(pickPaths(routes, "/users/add")).toEqual(["/users", "add"]);
expect(pickPaths(routes, "/users/remove")).toEqual(["/users", "remove"]);
expect(pickPaths(routes, "/users/123")).toEqual(["/users", "/users/:id"]);
});

it("throws when the nested path does not begin with its parent path", () => {
expect(() => {
matchRoutes(
[
{
path: "/users",
children: [
{ path: ":id" },
// This one should throw because it doesn't begin with /users
{ path: "/not/users" }
]
}
],
"/users/123"
);
}).toThrow("absolute child route path must start");
});
});
148 changes: 148 additions & 0 deletions packages/react-router/__tests__/createRoutesFromChildren-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import * as React from "react";
import { Route, createRoutesFromChildren } from "react-router";

describe("creating routes from JSX", () => {
it("creates a route config of nested JavaScript objects", () => {
expect(
createRoutesFromChildren(
<Route path="/">
<Route path="home" element={<h1>home</h1>} />
<Route path="about" element={<h1>about</h1>} />
<Route path="users">
<Route index element={<h1>users index</h1>} />
<Route path=":id" element={<h1>user profile</h1>} />
</Route>
</Route>
)
).toMatchInlineSnapshot(`
Array [
Object {
"caseSensitive": undefined,
"children": Array [
Object {
"caseSensitive": undefined,
"element": <Route
element={
<h1>
home
</h1>
}
path="home"
/>,
"index": undefined,
"path": "home",
},
Object {
"caseSensitive": undefined,
"element": <Route
element={
<h1>
about
</h1>
}
path="about"
/>,
"index": undefined,
"path": "about",
},
Object {
"caseSensitive": undefined,
"children": Array [
Object {
"caseSensitive": undefined,
"element": <Route
element={
<h1>
users index
</h1>
}
index={true}
/>,
"index": true,
"path": undefined,
},
Object {
"caseSensitive": undefined,
"element": <Route
element={
<h1>
user profile
</h1>
}
path=":id"
/>,
"index": undefined,
"path": ":id",
},
],
"element": <Route
path="users"
>
<Route
element={
<h1>
users index
</h1>
}
index={true}
/>
<Route
element={
<h1>
user profile
</h1>
}
path=":id"
/>
</Route>,
"index": undefined,
"path": "users",
},
],
"element": <Route
path="/"
>
<Route
element={
<h1>
home
</h1>
}
path="home"
/>
<Route
element={
<h1>
about
</h1>
}
path="about"
/>
<Route
path="users"
>
<Route
element={
<h1>
users index
</h1>
}
index={true}
/>
<Route
element={
<h1>
user profile
</h1>
}
path=":id"
/>
</Route>
</Route>,
"index": undefined,
"path": "/",
},
]
`);
});
});
24 changes: 24 additions & 0 deletions packages/react-router/__tests__/index-routes-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { matchRoutes } from "react-router";

describe("index route matching", () => {
it("throws when the index route has children", () => {
expect(() => {
matchRoutes(
[
{
path: "/users",
children: [
{
index: true,
// This config is not valid because index routes cannot have children
children: [{ path: "not-valid" }]
},
{ path: ":id" }
]
}
],
"/users/mj"
);
}).toThrow("must not have child routes");
});
});
12 changes: 6 additions & 6 deletions packages/react-router/__tests__/path-matching-test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { RouteObject } from "react-router";
import { matchRoutes } from "react-router";
import type { PartialRouteObject } from "react-router";

describe("path matching", () => {
function pickPaths(routes: PartialRouteObject[], pathname: string) {
function pickPaths(routes: RouteObject[], pathname: string) {
let matches = matchRoutes(routes, { pathname });
return matches ? matches.map(match => match.route.path) : null;
return matches ? matches.map(match => match.route.path || "") : null;
}

test("root vs. dynamic", () => {
Expand Down Expand Up @@ -71,7 +71,7 @@ describe("path matching", () => {
children: [{ path: "subjects" }]
},
{ path: "new" },
{ path: "/" },
{ index: true },
{ path: "*" }
]
},
Expand All @@ -87,7 +87,7 @@ describe("path matching", () => {
{ path: "*" }
];

expect(pickPaths(routes, "/courses")).toEqual(["courses", "/"]);
expect(pickPaths(routes, "/courses")).toEqual(["courses", ""]);
expect(pickPaths(routes, "/courses/routing")).toEqual(["courses", ":id"]);
expect(pickPaths(routes, "/courses/routing/subjects")).toEqual([
"courses",
Expand Down Expand Up @@ -115,7 +115,7 @@ describe("path matching", () => {
let routes = [
{
path: ":page",
children: [{ path: "/" }]
children: [{ index: true }]
},
{ path: "page" }
];
Expand Down

0 comments on commit 1ecefcf

Please sign in to comment.