Skip to content

Commit

Permalink
fix: properly handle ?index on fetcher get submissions (#9312)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Sep 20, 2022
1 parent 8b00e7a commit c430329
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-ravens-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/router": patch
---

fix: properly handle ?index on fetcher get submissions (#9312)
87 changes: 87 additions & 0 deletions packages/react-router-dom/__tests__/data-browser-router-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,93 @@ function testDomRouter(
`);
});

it("handles fetcher ?index params", async () => {
let { container } = render(
<TestDataRouter
window={getWindow("/parent")}
hydrationData={{ loaderData: { parent: null, index: null } }}
>
<Route
path="/parent"
element={<Outlet />}
action={() => "PARENT ACTION"}
loader={() => "PARENT LOADER"}
>
<Route
index
element={<Index />}
action={() => "INDEX ACTION"}
loader={() => "INDEX LOADER"}
/>
</Route>
</TestDataRouter>
);

function Index() {
let fetcher = useFetcher();

return (
<>
<p id="output">{fetcher.data}</p>
<button onClick={() => fetcher.load("/parent")}>
Load parent
</button>
<button onClick={() => fetcher.load("/parent?index")}>
Load index
</button>
<button onClick={() => fetcher.submit({})}>Submit empty</button>
<button
onClick={() =>
fetcher.submit({}, { method: "get", action: "/parent" })
}
>
Submit parent get
</button>
<button
onClick={() =>
fetcher.submit({}, { method: "get", action: "/parent?index" })
}
>
Submit index get
</button>
<button
onClick={() =>
fetcher.submit({}, { method: "post", action: "/parent" })
}
>
Submit parent post
</button>
<button
onClick={() =>
fetcher.submit(
{},
{ method: "post", action: "/parent?index" }
)
}
>
Submit index post
</button>
</>
);
}

async function clickAndAssert(btnText: string, expectedOutput: string) {
fireEvent.click(screen.getByText(btnText));
await waitFor(() => screen.getByText(new RegExp(expectedOutput)));
expect(getHtml(container.querySelector("#output"))).toContain(
expectedOutput
);
}

await clickAndAssert("Load parent", "PARENT LOADER");
await clickAndAssert("Load index", "INDEX LOADER");
await clickAndAssert("Submit empty", "INDEX LOADER");
await clickAndAssert("Submit parent get", "PARENT LOADER");
await clickAndAssert("Submit index get", "INDEX LOADER");
await clickAndAssert("Submit parent post", "PARENT ACTION");
await clickAndAssert("Submit index post", "INDEX ACTION");
});

it("handles fetcher.load errors", async () => {
let { container } = render(
<TestDataRouter
Expand Down
70 changes: 70 additions & 0 deletions packages/router/__tests__/router-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7605,6 +7605,76 @@ describe("a router", () => {
});
});
});

describe("fetcher ?index params", () => {
it("hits the proper Routes when ?index params are present", async () => {
let t = setup({
routes: [
{
id: "parent",
path: "parent",
action: true,
loader: true,
// Turn off revalidation after fetcher action submission for this test
shouldRevalidate: () => false,
children: [
{
id: "index",
index: true,
action: true,
loader: true,
// Turn off revalidation after fetcher action submission for this test
shouldRevalidate: () => false,
},
],
},
],
initialEntries: ["/parent"],
hydrationData: { loaderData: { parent: "PARENT", index: "INDEX" } },
});

let key = "KEY";

// fetcher.load()
let A = await t.fetch("/parent", key);
await A.loaders.parent.resolve("PARENT LOADER");
expect(t.router.getFetcher(key).data).toBe("PARENT LOADER");

let B = await t.fetch("/parent?index", key);
await B.loaders.index.resolve("INDEX LOADER");
expect(t.router.getFetcher(key).data).toBe("INDEX LOADER");

// fetcher.submit({}, { method: 'get' })
let C = await t.fetch("/parent", key, {
formMethod: "get",
formData: createFormData({}),
});
await C.loaders.parent.resolve("PARENT LOADER");
expect(t.router.getFetcher(key).data).toBe("PARENT LOADER");

let D = await t.fetch("/parent?index", key, {
formMethod: "get",
formData: createFormData({}),
});
await D.loaders.index.resolve("INDEX LOADER");
expect(t.router.getFetcher(key).data).toBe("INDEX LOADER");

// fetcher.submit({}, { method: 'post' })
let E = await t.fetch("/parent", key, {
formMethod: "post",
formData: createFormData({}),
});
await E.actions.parent.resolve("PARENT ACTION");
expect(t.router.getFetcher(key).data).toBe("PARENT ACTION");

let F = await t.fetch("/parent?index", key, {
formMethod: "post",
formData: createFormData({}),
});
await F.actions.index.resolve("INDEX ACTION");
expect(t.router.getFetcher(key).data).toBe("INDEX ACTION");
});
});
});

describe("deferred data", () => {
Expand Down
15 changes: 13 additions & 2 deletions packages/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,7 @@ export function createRouter(init: RouterInit): Router {
return;
}

let { path, submission } = normalizeNavigateOptions(href, opts);
let { path, submission } = normalizeNavigateOptions(href, opts, true);
let match = getTargetMatch(matches, path);

if (submission) {
Expand Down Expand Up @@ -2098,7 +2098,8 @@ export function getStaticContextFromError(
// URLSearchParams so they behave identically to links with query params
function normalizeNavigateOptions(
to: To,
opts?: RouterNavigateOptions
opts?: RouterNavigateOptions,
isFetcher = false
): {
path: string;
submission?: Submission;
Expand Down Expand Up @@ -2134,6 +2135,16 @@ function normalizeNavigateOptions(
let parsedPath = parsePath(path);
try {
let searchParams = convertFormDataToSearchParams(opts.formData);
// Since fetcher GET submissions only run a single loader (as opposed to
// navigation GET submissions which run all loaders), we need to preserve
// any incoming ?index params
if (
isFetcher &&
parsedPath.search &&
hasNakedIndexQuery(parsedPath.search)
) {
searchParams.append("index", "");
}
parsedPath.search = `?${searchParams}`;
} catch (e) {
return {
Expand Down

0 comments on commit c430329

Please sign in to comment.