From 07aa8186224b49a71f26290f423d42ae7f6cc605 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 23 Jan 2023 14:04:33 -0500 Subject: [PATCH 1/2] Respect preventScrollReset on fetcher.Form --- .changeset/chilly-stingrays-rhyme.md | 6 +++ packages/router/__tests__/router-test.ts | 63 ++++++++++++++++++++++++ packages/router/router.ts | 2 + 3 files changed, 71 insertions(+) create mode 100644 .changeset/chilly-stingrays-rhyme.md diff --git a/.changeset/chilly-stingrays-rhyme.md b/.changeset/chilly-stingrays-rhyme.md new file mode 100644 index 0000000000..0e1473ae18 --- /dev/null +++ b/.changeset/chilly-stingrays-rhyme.md @@ -0,0 +1,6 @@ +--- +"@remix-run/router": patch +"react-router-dom": patch +--- + +Respect `preventScrollReset` on `fetcher.Form` diff --git a/packages/router/__tests__/router-test.ts b/packages/router/__tests__/router-test.ts index c7340bbd69..790fc69ec0 100644 --- a/packages/router/__tests__/router-test.ts +++ b/packages/router/__tests__/router-test.ts @@ -6702,6 +6702,37 @@ describe("a router", () => { expect(t.router.state.restoreScrollPosition).toBe(null); expect(t.router.state.preventScrollReset).toBe(false); }); + + it("resets on fetch submissions that redirect", async () => { + let t = setup({ + routes: SCROLL_ROUTES, + initialEntries: ["/tasks"], + hydrationData: { + loaderData: { + tasks: "TASKS", + }, + }, + }); + + expect(t.router.state.restoreScrollPosition).toBe(false); + expect(t.router.state.preventScrollReset).toBe(false); + + let positions = {}; + let activeScrollPosition = 0; + t.router.enableScrollRestoration( + positions, + () => activeScrollPosition + ); + + let nav1 = await t.fetch("/tasks", { + formMethod: "post", + formData: createFormData({}), + }); + let nav2 = await nav1.actions.tasks.redirectReturn("/tasks"); + await nav2.loaders.tasks.resolve("TASKS 2"); + expect(t.router.state.restoreScrollPosition).toBe(null); + expect(t.router.state.preventScrollReset).toBe(false); + }); }); describe("user-specified flag preventScrollReset flag", () => { @@ -6823,6 +6854,38 @@ describe("a router", () => { expect(t.router.state.restoreScrollPosition).toBe(null); expect(t.router.state.preventScrollReset).toBe(true); }); + + it("prevents scroll reset on fetch submissions that redirect", async () => { + let t = setup({ + routes: SCROLL_ROUTES, + initialEntries: ["/tasks"], + hydrationData: { + loaderData: { + tasks: "TASKS", + }, + }, + }); + + expect(t.router.state.restoreScrollPosition).toBe(false); + expect(t.router.state.preventScrollReset).toBe(false); + + let positions = {}; + let activeScrollPosition = 0; + t.router.enableScrollRestoration( + positions, + () => activeScrollPosition + ); + + let nav1 = await t.fetch("/tasks", { + formMethod: "post", + formData: createFormData({}), + preventScrollReset: true, + }); + let nav2 = await nav1.actions.tasks.redirectReturn("/tasks"); + await nav2.loaders.tasks.resolve("TASKS 2"); + expect(t.router.state.restoreScrollPosition).toBe(null); + expect(t.router.state.preventScrollReset).toBe(true); + }); }); }); }); diff --git a/packages/router/router.ts b/packages/router/router.ts index 6b40d6bb10..dbd5114fb2 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -1507,6 +1507,8 @@ export function createRouter(init: RouterInit): Router { let { path, submission } = normalizeNavigateOptions(href, opts, true); let match = getTargetMatch(matches, path); + pendingPreventScrollReset = (opts && opts.preventScrollReset) === true; + if (submission && isMutationMethod(submission.formMethod)) { handleFetcherAction(key, routeId, path, match, matches, submission); return; From 3d19e46deea28bee36dda720dda3eaca7bacbf32 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 23 Jan 2023 14:18:56 -0500 Subject: [PATCH 2/2] Add preventScrollReset to form docs --- docs/components/form.md | 12 ++++++++++++ docs/components/scroll-restoration.md | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/components/form.md b/docs/components/form.md index 32602ab5e0..98a5f99eb4 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -215,6 +215,16 @@ See also: - [`useActionData`][useactiondata] - [`useSubmit`][usesubmit] +## `preventScrollReset` + +If you are using [``][scrollrestoration], this lets you prevent the scroll position from being reset to the top of the window when the form action redirects to a new location. + +```tsx +
+``` + +See also: [``][link-preventscrollreset] + # Examples TODO: More examples @@ -318,3 +328,5 @@ You can access those values from the `request.url` [formvalidation]: https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation [indexsearchparam]: ../guides/index-search-param [pickingarouter]: ../routers/picking-a-router +[scrollrestoration]: ./scroll-restoration +[link-preventscrollreset]: ./link#preventscrollreset diff --git a/docs/components/scroll-restoration.md b/docs/components/scroll-restoration.md index d3dd20fab3..00d39e6824 100644 --- a/docs/components/scroll-restoration.md +++ b/docs/components/scroll-restoration.md @@ -83,13 +83,14 @@ Or you may want to only use the pathname for some paths, and use the normal beha ## Preventing Scroll Reset -When navigation creates new scroll keys, the scroll position is reset to the top of the page. You can prevent the "scroll to top" behavior from your links: +When navigation creates new scroll keys, the scroll position is reset to the top of the page. You can prevent the "scroll to top" behavior from your links and forms: ```tsx + ``` -See also: [``][preventscrollreset] +See also: [``][preventscrollreset], [``][form-preventscrollreset] ## Scroll Flashing @@ -99,4 +100,5 @@ Server Rendering frameworks can prevent scroll flashing because they can send a [remix]: https://remix.run [preventscrollreset]: ../components/link#preventscrollreset +[form-preventscrollreset]: ../components/form#preventscrollreset [pickingarouter]: ../routers/picking-a-router