Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: submissions should ignore pathless layout routes #9455

Merged
merged 3 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/moody-bulldogs-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/router": patch
---

Ignore pathless layout routes when looking for proper submission action function
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
},
"filesize": {
"packages/router/dist/router.js": {
"none": "100 kB"
"none": "101 kB"
},
"packages/react-router/dist/react-router.production.min.js": {
"none": "12.5 kB"
Expand Down
50 changes: 50 additions & 0 deletions packages/router/__tests__/router-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2859,6 +2859,56 @@ describe("a router", () => {
});
});

it("uses the proper action for pathless layout routes", async () => {
let t = setup({
routes: [
{
id: "parent",
path: "/parent",
action: true,
children: [
{
hasErrorBoundary: true,
children: [
{
id: "index",
index: true,
action: true,
},
],
},
],
},
],
});
debugger;
let A = await t.navigate("/parent", {
formMethod: "post",
formData: createFormData({ gosh: "dang" }),
});
await A.actions.parent.resolve("PARENT");
expect(t.router.state).toMatchObject({
location: { pathname: "/parent" },
actionData: {
parent: "PARENT",
},
errors: null,
});

let B = await t.navigate("/parent?index", {
formMethod: "post",
formData: createFormData({ gosh: "dang" }),
});
await B.actions.index.resolve("INDEX");
expect(t.router.state).toMatchObject({
location: { pathname: "/parent", search: "?index" },
actionData: {
index: "INDEX",
},
errors: null,
});
});

it("retains the index match when submitting to a layout route", async () => {
let t = setup({
routes: [
Expand Down
11 changes: 8 additions & 3 deletions packages/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
ErrorResponse,
ResultType,
convertRoutesToDataRoutes,
getPathContributingMatches,
invariant,
isRouteErrorResponse,
matchRoutes,
Expand Down Expand Up @@ -2836,11 +2837,15 @@ function getTargetMatch(
typeof location === "string" ? parsePath(location).search : location.search;
if (
matches[matches.length - 1].route.index &&
!hasNakedIndexQuery(search || "")
hasNakedIndexQuery(search || "")
) {
return matches.slice(-2)[0];
// Return the leaf index route when index is present
return matches[matches.length - 1];
}
return matches.slice(-1)[0];
// Otherwise grab the deepest "path contributing" match (ignoring index and
// pathless layout routes)
let pathMatches = getPathContributingMatches(matches);
return pathMatches[pathMatches.length - 1];
}

function createURL(location: Location | string): URL {
Expand Down
32 changes: 32 additions & 0 deletions packages/router/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,38 @@ function getInvalidPathError(
);
}

/**
* When processing relative navigation we want to ignore ancestor routes that
* do not contribute to the path, such that index/pathless layout routes don't
* interfere.
*
* For example, when moving a route element into an index route and/or a
* pathless layout route, relative link behavior contained within should stay
* the same. Both of the following examples should link back to the root:
*
* <Route path="/">
* <Route path="accounts" element={<Link to=".."}>
* </Route>
*
* <Route path="/">
* <Route path="accounts">
* <Route element={<AccountsLayout />}> // <-- Does not contribute
* <Route index element={<Link to=".."} /> // <-- Does not contribute
* </Route
* </Route>
* </Route>
*/
export function getPathContributingMatches<
T extends AgnosticRouteMatch = AgnosticRouteMatch
>(matches: T[]) {
return matches.filter(
(match, index) =>
index === 0 ||
(!match.route.index &&
match.pathnameBase !== matches[index - 1].pathnameBase)
);
}

/**
* @private
*/
Expand Down