Skip to content

Commit 71ee40a

Browse files
authored
fix: submissions should ignore pathless layout routes (#9455)
* fix: submissions should ignore pathless layout routes * Add changset * bump bundle threshold
1 parent 35ff025 commit 71ee40a

File tree

5 files changed

+96
-4
lines changed

5 files changed

+96
-4
lines changed

.changeset/moody-bulldogs-enjoy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Ignore pathless layout routes when looking for proper submission action function

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
},
108108
"filesize": {
109109
"packages/router/dist/router.js": {
110-
"none": "100 kB"
110+
"none": "101 kB"
111111
},
112112
"packages/react-router/dist/react-router.production.min.js": {
113113
"none": "12.5 kB"

packages/router/__tests__/router-test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,6 +2859,56 @@ describe("a router", () => {
28592859
});
28602860
});
28612861

2862+
it("uses the proper action for pathless layout routes", async () => {
2863+
let t = setup({
2864+
routes: [
2865+
{
2866+
id: "parent",
2867+
path: "/parent",
2868+
action: true,
2869+
children: [
2870+
{
2871+
hasErrorBoundary: true,
2872+
children: [
2873+
{
2874+
id: "index",
2875+
index: true,
2876+
action: true,
2877+
},
2878+
],
2879+
},
2880+
],
2881+
},
2882+
],
2883+
});
2884+
debugger;
2885+
let A = await t.navigate("/parent", {
2886+
formMethod: "post",
2887+
formData: createFormData({ gosh: "dang" }),
2888+
});
2889+
await A.actions.parent.resolve("PARENT");
2890+
expect(t.router.state).toMatchObject({
2891+
location: { pathname: "/parent" },
2892+
actionData: {
2893+
parent: "PARENT",
2894+
},
2895+
errors: null,
2896+
});
2897+
2898+
let B = await t.navigate("/parent?index", {
2899+
formMethod: "post",
2900+
formData: createFormData({ gosh: "dang" }),
2901+
});
2902+
await B.actions.index.resolve("INDEX");
2903+
expect(t.router.state).toMatchObject({
2904+
location: { pathname: "/parent", search: "?index" },
2905+
actionData: {
2906+
index: "INDEX",
2907+
},
2908+
errors: null,
2909+
});
2910+
});
2911+
28622912
it("retains the index match when submitting to a layout route", async () => {
28632913
let t = setup({
28642914
routes: [

packages/router/router.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ErrorResponse,
2626
ResultType,
2727
convertRoutesToDataRoutes,
28+
getPathContributingMatches,
2829
invariant,
2930
isRouteErrorResponse,
3031
matchRoutes,
@@ -2838,11 +2839,15 @@ function getTargetMatch(
28382839
typeof location === "string" ? parsePath(location).search : location.search;
28392840
if (
28402841
matches[matches.length - 1].route.index &&
2841-
!hasNakedIndexQuery(search || "")
2842+
hasNakedIndexQuery(search || "")
28422843
) {
2843-
return matches.slice(-2)[0];
2844+
// Return the leaf index route when index is present
2845+
return matches[matches.length - 1];
28442846
}
2845-
return matches.slice(-1)[0];
2847+
// Otherwise grab the deepest "path contributing" match (ignoring index and
2848+
// pathless layout routes)
2849+
let pathMatches = getPathContributingMatches(matches);
2850+
return pathMatches[pathMatches.length - 1];
28462851
}
28472852

28482853
function createURL(location: Location | string): URL {

packages/router/utils.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,38 @@ function getInvalidPathError(
835835
);
836836
}
837837

838+
/**
839+
* When processing relative navigation we want to ignore ancestor routes that
840+
* do not contribute to the path, such that index/pathless layout routes don't
841+
* interfere.
842+
*
843+
* For example, when moving a route element into an index route and/or a
844+
* pathless layout route, relative link behavior contained within should stay
845+
* the same. Both of the following examples should link back to the root:
846+
*
847+
* <Route path="/">
848+
* <Route path="accounts" element={<Link to=".."}>
849+
* </Route>
850+
*
851+
* <Route path="/">
852+
* <Route path="accounts">
853+
* <Route element={<AccountsLayout />}> // <-- Does not contribute
854+
* <Route index element={<Link to=".."} /> // <-- Does not contribute
855+
* </Route
856+
* </Route>
857+
* </Route>
858+
*/
859+
export function getPathContributingMatches<
860+
T extends AgnosticRouteMatch = AgnosticRouteMatch
861+
>(matches: T[]) {
862+
return matches.filter(
863+
(match, index) =>
864+
index === 0 ||
865+
(!match.route.index &&
866+
match.pathnameBase !== matches[index - 1].pathnameBase)
867+
);
868+
}
869+
838870
/**
839871
* @private
840872
*/

0 commit comments

Comments
 (0)