Skip to content

Commit 43de13c

Browse files
committed
encode pathnames in NavLink check
1 parent e29a004 commit 43de13c

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

packages/react-router-dom/__tests__/nav-link-active-test.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { JSDOM } from "jsdom";
88
import * as React from "react";
99
import * as TestRenderer from "react-test-renderer";
1010
import {
11+
BrowserRouter,
1112
MemoryRouter,
1213
Routes,
1314
Route,
@@ -189,6 +190,37 @@ describe("NavLink", () => {
189190

190191
expect(anchor.children[0]).toMatch("Home (current)");
191192
});
193+
194+
it("matches when portions of the url are encoded", () => {
195+
let renderer: TestRenderer.ReactTestRenderer;
196+
197+
TestRenderer.act(() => {
198+
renderer = TestRenderer.create(
199+
<BrowserRouter window={getWindow("/users/matt brophy")}>
200+
<Routes>
201+
<Route
202+
path="/users/:name"
203+
element={
204+
<>
205+
<NavLink to=".">Matt</NavLink>
206+
<NavLink to="/users/matt brophy">Matt</NavLink>
207+
<NavLink to="/users/michael jackson">Michael</NavLink>
208+
</>
209+
}
210+
/>
211+
</Routes>
212+
</BrowserRouter>
213+
);
214+
});
215+
216+
let anchors = renderer.root.findAllByType("a");
217+
218+
expect(anchors.map((a) => a.props.className)).toEqual([
219+
"active",
220+
"active",
221+
"",
222+
]);
223+
});
192224
});
193225

194226
describe("when it matches a partial URL segment", () => {
@@ -712,6 +744,64 @@ describe("NavLink using a data router", () => {
712744
await waitFor(() => screen.getByText("Baz page"));
713745
expect(screen.getByText("Link to Bar").className).toBe("");
714746
});
747+
748+
it("applies the default 'active'/'pending' classNames when the url has encoded characters", async () => {
749+
let barDfd = createDeferred();
750+
let bazDfd = createDeferred();
751+
let router = createBrowserRouter(
752+
createRoutesFromElements(
753+
<Route path="/" element={<Layout />}>
754+
<Route path="foo" element={<p>Foo page</p>} />
755+
<Route
756+
path="bar/:param"
757+
loader={() => barDfd.promise}
758+
element={<p>Bar page</p>}
759+
/>
760+
<Route
761+
path="baz-✅"
762+
loader={() => bazDfd.promise}
763+
element={<p>Baz page</p>}
764+
/>
765+
</Route>
766+
),
767+
{
768+
window: getWindow("/foo"),
769+
}
770+
);
771+
render(<RouterProvider router={router} />);
772+
773+
function Layout() {
774+
return (
775+
<>
776+
<NavLink to="/foo">Link to Foo</NavLink>
777+
<NavLink to="/bar/matt brophy">Link to Bar</NavLink>
778+
<NavLink to="/baz-✅">Link to Baz</NavLink>
779+
<Outlet />
780+
</>
781+
);
782+
}
783+
784+
expect(screen.getByText("Link to Bar").className).toBe("");
785+
expect(screen.getByText("Link to Baz").className).toBe("");
786+
787+
fireEvent.click(screen.getByText("Link to Bar"));
788+
expect(screen.getByText("Link to Bar").className).toBe("pending");
789+
expect(screen.getByText("Link to Baz").className).toBe("");
790+
791+
barDfd.resolve(null);
792+
await waitFor(() => screen.getByText("Bar page"));
793+
expect(screen.getByText("Link to Bar").className).toBe("active");
794+
expect(screen.getByText("Link to Baz").className).toBe("");
795+
796+
fireEvent.click(screen.getByText("Link to Baz"));
797+
expect(screen.getByText("Link to Bar").className).toBe("active");
798+
expect(screen.getByText("Link to Baz").className).toBe("pending");
799+
800+
bazDfd.resolve(null);
801+
await waitFor(() => screen.getByText("Baz page"));
802+
expect(screen.getByText("Link to Bar").className).toBe("");
803+
expect(screen.getByText("Link to Baz").className).toBe("active");
804+
});
715805
});
716806

717807
describe("NavLink under a Routes with a basename", () => {

packages/react-router-dom/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,9 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
444444
let path = useResolvedPath(to, { relative: rest.relative });
445445
let location = useLocation();
446446
let routerState = React.useContext(DataRouterStateContext);
447+
let { navigator } = React.useContext(NavigationContext);
447448

448-
let toPathname = path.pathname;
449+
let toPathname = navigator.encodeLocation(path).pathname;
449450
let locationPathname = location.pathname;
450451
let nextLocationPathname =
451452
routerState && routerState.navigation && routerState.navigation.location

0 commit comments

Comments
 (0)