From ae99a1c3dcbd2c480a4da7d097f03562dbf4c87c Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 22 Aug 2024 10:39:01 -0400 Subject: [PATCH] Preserve pending view transitions through a router revalidation call (#11917) --- .changeset/soft-socks-remain.md | 5 ++ .../router/__tests__/view-transition-test.ts | 70 +++++++++++++++++++ packages/router/router.ts | 6 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 .changeset/soft-socks-remain.md diff --git a/.changeset/soft-socks-remain.md b/.changeset/soft-socks-remain.md new file mode 100644 index 0000000000..f061623eee --- /dev/null +++ b/.changeset/soft-socks-remain.md @@ -0,0 +1,5 @@ +--- +"@remix-run/router": patch +--- + +Preserve pending view transitions through a router revalidation call diff --git a/packages/router/__tests__/view-transition-test.ts b/packages/router/__tests__/view-transition-test.ts index b5f8121e07..3196471419 100644 --- a/packages/router/__tests__/view-transition-test.ts +++ b/packages/router/__tests__/view-transition-test.ts @@ -66,4 +66,74 @@ describe("view transitions", () => { unsubscribe(); t.router.dispose(); }); + + it("preserves pending view transitions through router.revalidate()", async () => { + let t = setup({ + routes: [{ path: "/" }, { id: "a", path: "/a", loader: true }], + }); + let spy = jest.fn(); + let unsubscribe = t.router.subscribe(spy); + + let A = await t.navigate("/a", { unstable_viewTransition: true }); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy.mock.calls[0]).toEqual([ + expect.objectContaining({ + navigation: expect.objectContaining({ state: "loading" }), + }), + expect.objectContaining({ unstable_viewTransitionOpts: undefined }), + ]); + expect(A.loaders.a.stub).toHaveBeenCalledTimes(1); + + // Interrupt the navigation loading state with a revalidation + let B = await t.revalidate(); + expect(spy).toHaveBeenCalledTimes(3); + expect(spy.mock.calls[1]).toEqual([ + expect.objectContaining({ + revalidation: "loading", + }), + expect.objectContaining({ + unstable_viewTransitionOpts: undefined, + }), + ]); + expect(spy.mock.calls[2]).toEqual([ + expect.objectContaining({ + navigation: expect.objectContaining({ state: "loading" }), + }), + expect.objectContaining({ + unstable_viewTransitionOpts: undefined, + }), + ]); + expect(spy).toHaveBeenLastCalledWith( + expect.objectContaining({ + navigation: expect.objectContaining({ state: "loading" }), + }), + expect.objectContaining({ + unstable_viewTransitionOpts: undefined, + }) + ); + expect(B.loaders.a.stub).toHaveBeenCalledTimes(1); + + await A.loaders.a.resolve("A"); + await B.loaders.a.resolve("A*"); + + expect(spy).toHaveBeenCalledTimes(4); + expect(spy.mock.calls[3]).toEqual([ + expect.objectContaining({ + navigation: IDLE_NAVIGATION, + location: expect.objectContaining({ pathname: "/a" }), + loaderData: { + a: "A*", + }, + }), + expect.objectContaining({ + unstable_viewTransitionOpts: { + currentLocation: expect.objectContaining({ pathname: "/" }), + nextLocation: expect.objectContaining({ pathname: "/a" }), + }, + }), + ]); + + unsubscribe(); + t.router.dispose(); + }); }); diff --git a/packages/router/router.ts b/packages/router/router.ts index da4a43db72..7d9c7f7e27 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -1479,7 +1479,11 @@ export function createRouter(init: RouterInit): Router { startNavigation( pendingAction || state.historyAction, state.navigation.location, - { overrideNavigation: state.navigation } + { + overrideNavigation: state.navigation, + // Proxy through any rending view transition + enableViewTransition: pendingViewTransitionEnabled === true, + } ); }