diff --git a/contributors.yml b/contributors.yml index 5bc61ba49e..b340059b5a 100644 --- a/contributors.yml +++ b/contributors.yml @@ -23,6 +23,7 @@ - hyesungoh - IbraRouisDev - Isammoc +- JaffParker - JakubDrozd - janpaepke - jimniels diff --git a/packages/react-router/__tests__/navigate-test.tsx b/packages/react-router/__tests__/navigate-test.tsx index ef6ba56bc2..4a6446608d 100644 --- a/packages/react-router/__tests__/navigate-test.tsx +++ b/packages/react-router/__tests__/navigate-test.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import * as TestRenderer from "react-test-renderer"; -import { MemoryRouter, Navigate, Routes, Route } from "react-router"; +import { MemoryRouter, Navigate, Outlet, Routes, Route } from "react-router"; describe("", () => { describe("with an absolute href", () => { @@ -45,5 +45,152 @@ describe("", () => { `); }); + + it("handles upward navigation from an index routes", () => { + let renderer: TestRenderer.ReactTestRenderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + + } /> + + About} /> + + + ); + }); + + expect(renderer.toJSON()).toMatchInlineSnapshot(` +

+ About +

+ `); + }); + + it("handles upward navigation from inside a pathless layout route", () => { + let renderer: TestRenderer.ReactTestRenderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + }> + } /> + + About} /> + + + ); + }); + + expect(renderer.toJSON()).toMatchInlineSnapshot(` +

+ About +

+ `); + }); + + it("handles upward navigation from inside multiple pathless layout routes + index route", () => { + let renderer: TestRenderer.ReactTestRenderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + + }> + }> + }> + } /> + + + + + About} /> + + + ); + }); + + expect(renderer.toJSON()).toMatchInlineSnapshot(` +

+ About +

+ `); + }); + + it("handles upward navigation from inside multiple pathless layout routes + path route", () => { + let renderer: TestRenderer.ReactTestRenderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + }> + }> + }> + }> + } + /> + + + + + About} /> + + + ); + }); + + expect(renderer.toJSON()).toMatchInlineSnapshot(` +

+ About +

+ `); + }); + + it("handles parent navigation from inside multiple pathless layout routes", () => { + let renderer: TestRenderer.ReactTestRenderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + +

Home

+ + + } + > + }> + }> + }> + +

Page

+ + + } + /> +
+
+
+
+ About} /> +
+
+ ); + }); + + expect(renderer.toJSON()).toMatchInlineSnapshot(` +

+ Home +

+ `); + }); }); }); diff --git a/packages/react-router/lib/hooks.tsx b/packages/react-router/lib/hooks.tsx index 1f5adfabf4..11ce9accee 100644 --- a/packages/react-router/lib/hooks.tsx +++ b/packages/react-router/lib/hooks.tsx @@ -155,8 +155,18 @@ export function useNavigate(): NavigateFunction { let { matches } = React.useContext(RouteContext); let { pathname: locationPathname } = useLocation(); + // Ignore pathless matches (i.e., share the same pathname as their ancestor) + let pathContributingMatches = matches.filter( + (match, index) => + index === 0 || match.pathnameBase !== matches[index - 1].pathnameBase + ); + + if (matches.length > 0 && matches[matches.length - 1].route.index) { + pathContributingMatches.pop(); + } + let routePathnamesJson = JSON.stringify( - matches.map((match) => match.pathnameBase) + pathContributingMatches.map((match) => match.pathnameBase) ); let activeRef = React.useRef(false);