Skip to content

Commit

Permalink
fix: Ignore pathless layout routes when matching actions (#4376)
Browse files Browse the repository at this point in the history
* fix: Ignore pathless layout routes when matching actions

* Add changeset

* Add comment

* Remove deuped function
  • Loading branch information
brophdawg11 committed Oct 20, 2022
1 parent fb42632 commit cd2e256
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 19 deletions.
6 changes: 6 additions & 0 deletions .changeset/stupid-games-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@remix-run/react": patch
"@remix-run/server-runtime": patch
---

Ignore pathless layout routes in action matches
61 changes: 61 additions & 0 deletions integration/form-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,47 @@ test.describe("Forms", () => {
)
}
`,

"app/routes/pathless-layout-parent.jsx": js`
import { json } from '@remix-run/server-runtime'
import { Form, Outlet, useActionData } from '@remix-run/react'
export async function action({ request }) {
return json({ submitted: true });
}
export default function () {
let data = useActionData();
return (
<>
<Form method="post">
<h1>Pathless Layout Parent</h1>
<button type="submit">Submit</button>
</Form>
<Outlet />
<p>{data?.submitted === true ? 'Submitted - Yes' : 'Submitted - No'}</p>
</>
);
}
`,

"app/routes/pathless-layout-parent/__pathless.jsx": js`
import { Outlet } from '@remix-run/react';
export default function () {
return (
<>
<h2>Pathless Layout</h2>
<Outlet />
</>
);
}
`,

"app/routes/pathless-layout-parent/__pathless/index.jsx": js`
export default function () {
return <h3>Pathless Layout Index</h3>
}
`,
},
});

Expand Down Expand Up @@ -942,4 +983,24 @@ test.describe("Forms", () => {
);
}
});

test("pathless layout routes are ignored in form actions", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/pathless-layout-parent");
let html = await app.getHtml();
expect(html).toMatch("Pathless Layout Parent");
expect(html).toMatch("Pathless Layout ");
expect(html).toMatch("Pathless Layout Index");

let el = getElement(html, `form`);
expect(el.attr("action")).toMatch("/pathless-layout-parent");

expect(await app.getHtml()).toMatch("Submitted - No");
// This submission should ignore the index route and the pathless layout
// route above it and hit the action in routes/pathless-layout-parent.jsx
await app.clickSubmitButton("/pathless-layout-parent");
expect(await app.getHtml()).toMatch("Submitted - Yes");
});
});
39 changes: 23 additions & 16 deletions packages/remix-react/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ export function createTransitionManager(init: TransitionManagerInit) {
invariant(matches, "No matches found");
if (fetchControllers.has(key)) abortFetcher(key);

let match = getFetcherRequestMatch(
let match = getRequestMatch(
new URL(href, window.location.href),
matches
);
Expand Down Expand Up @@ -644,17 +644,28 @@ export function createTransitionManager(init: TransitionManagerInit) {
return false;
}

function getFetcherRequestMatch(
url: URL,
matches: RouteMatch<ClientRoute>[]
) {
// Return the correct single match for a route (used for submission
// navigations and and fetchers)
// - ?index should try to match the leaf index route
// - otherwise it should match the deepest "path contributing" match, which
// ignores index and pathless routes
function getRequestMatch(url: URL, matches: ClientMatch[]) {
let match = matches.slice(-1)[0];

if (!isIndexRequestUrl(url) && match.route.index) {
return matches.slice(-2)[0];
if (isIndexRequestUrl(url) && match.route.index) {
return match;
}

return match;
return getPathContributingMatches(matches).slice(-1)[0];
}

// Filter index and pathless routes when looking for submission targets
function getPathContributingMatches(matches: ClientMatch[]) {
return matches.filter(
(match, index) =>
index === 0 ||
(!match.route.index && match.route.path && match.route.path.length > 0)
);
}

async function handleActionFetchSubmission(
Expand Down Expand Up @@ -1071,14 +1082,10 @@ export function createTransitionManager(init: TransitionManagerInit) {
// to run on layout/index routes. We do not want to mutate the eventual
// matches used for revalidation
let actionMatches = matches;
if (
!isIndexRequestUrl(createUrl(submission.action)) &&
actionMatches[matches.length - 1].route.index
) {
actionMatches = actionMatches.slice(0, -1);
}

let leafMatch = actionMatches.slice(-1)[0];
let leafMatch = getRequestMatch(
createUrl(submission.action),
actionMatches
);
let result = await callAction(submission, leafMatch, controller.signal);

if (controller.signal.aborted) {
Expand Down
14 changes: 11 additions & 3 deletions packages/remix-server-runtime/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -708,11 +708,19 @@ function isIndexRequestUrl(url: URL) {
function getRequestMatch(url: URL, matches: RouteMatch<ServerRoute>[]) {
let match = matches.slice(-1)[0];

if (!isIndexRequestUrl(url) && match.route.id.endsWith("/index")) {
return matches.slice(-2)[0];
if (isIndexRequestUrl(url) && match.route.id.endsWith("/index")) {
return match;
}

return match;
return getPathContributingMatches(matches).slice(-1)[0];
}

function getPathContributingMatches(matches: RouteMatch<ServerRoute>[]) {
return matches.filter(
(match, index) =>
index === 0 ||
(!match.route.index && match.route.path && match.route.path.length > 0)
);
}

function getDeepestRouteIdWithBoundary(
Expand Down

0 comments on commit cd2e256

Please sign in to comment.