Skip to content

Commit d68d03e

Browse files
authored
feat: add support for basename in data routers (#9026)
* feat: add support for basename in data routers * add changeset
1 parent 5a90367 commit d68d03e

File tree

7 files changed

+232
-12
lines changed

7 files changed

+232
-12
lines changed

.changeset/sixty-otters-teach.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-router": patch
3+
"react-router-dom": patch
4+
"@remix-run/router": patch
5+
---
6+
7+
feat: add basename support for data routers

packages/react-router-dom/__tests__/DataBrowserRouter-test.tsx

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ function testDomRouter(
7777
`);
7878
});
7979

80-
it("renders the first route that matches the URL when wrapped in a 'basename' Route", () => {
81-
// In data routers there is no basename and you should instead use a root route
80+
it("renders the first route that matches the URL when wrapped in a root Route", () => {
8281
let { container } = render(
8382
<TestDataRouter
8483
window={getWindow("/my/base/path/thing")}
@@ -101,6 +100,26 @@ function testDomRouter(
101100
`);
102101
});
103102

103+
it("supports a basename prop", () => {
104+
let { container } = render(
105+
<TestDataRouter
106+
basename="/my/base/path"
107+
window={getWindow("/my/base/path/thing")}
108+
hydrationData={{}}
109+
>
110+
<Route path="thing" element={<h1>Heyooo</h1>} />
111+
</TestDataRouter>
112+
);
113+
114+
expect(getHtml(container)).toMatchInlineSnapshot(`
115+
"<div>
116+
<h1>
117+
Heyooo
118+
</h1>
119+
</div>"
120+
`);
121+
});
122+
104123
it("renders with hydration data", async () => {
105124
let { container } = render(
106125
<TestDataRouter
@@ -270,6 +289,51 @@ function testDomRouter(
270289
await waitFor(() => screen.getByText("Foo Heading"));
271290
});
272291

292+
it("handles link navigations when using a basename", async () => {
293+
let testWindow = getWindow("/base/name/foo");
294+
render(
295+
<TestDataRouter
296+
basename="/base/name"
297+
window={testWindow}
298+
hydrationData={{}}
299+
>
300+
<Route path="/" element={<Layout />}>
301+
<Route path="foo" element={<h1>Foo Heading</h1>} />
302+
<Route path="bar" element={<h1>Bar Heading</h1>} />
303+
</Route>
304+
</TestDataRouter>
305+
);
306+
307+
function Layout() {
308+
return (
309+
<div>
310+
<Link to="/foo">Link to Foo</Link>
311+
<Link to="/bar">Link to Bar</Link>
312+
<Outlet />
313+
</div>
314+
);
315+
}
316+
317+
function assertPathname(pathname) {
318+
if (name === "<DataHashRouter>") {
319+
expect(testWindow.location.hash).toEqual("#" + pathname);
320+
} else {
321+
expect(testWindow.location.pathname).toEqual(pathname);
322+
}
323+
}
324+
325+
assertPathname("/base/name/foo");
326+
327+
expect(screen.getByText("Foo Heading")).toBeDefined();
328+
fireEvent.click(screen.getByText("Link to Bar"));
329+
await waitFor(() => screen.getByText("Bar Heading"));
330+
assertPathname("/base/name/bar");
331+
332+
fireEvent.click(screen.getByText("Link to Foo"));
333+
await waitFor(() => screen.getByText("Foo Heading"));
334+
assertPathname("/base/name/foo");
335+
});
336+
273337
it("executes route loaders on navigation", async () => {
274338
let barDefer = defer();
275339

packages/react-router-dom/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export {
164164
////////////////////////////////////////////////////////////////////////////////
165165

166166
export interface DataBrowserRouterProps {
167+
basename?: string;
167168
children?: React.ReactNode;
168169
hydrationData?: HydrationState;
169170
fallbackElement?: React.ReactNode;
@@ -172,18 +173,21 @@ export interface DataBrowserRouterProps {
172173
}
173174

174175
export function DataBrowserRouter({
176+
basename,
175177
children,
176178
fallbackElement,
177179
hydrationData,
178180
routes,
179181
window,
180182
}: DataBrowserRouterProps): React.ReactElement {
181183
return useRenderDataRouter({
184+
basename,
182185
children,
183186
fallbackElement,
184187
routes,
185188
createRouter: (routes) =>
186189
createBrowserRouter({
190+
basename,
187191
routes,
188192
hydrationData,
189193
window,
@@ -192,6 +196,7 @@ export function DataBrowserRouter({
192196
}
193197

194198
export interface DataHashRouterProps {
199+
basename?: string;
195200
children?: React.ReactNode;
196201
hydrationData?: HydrationState;
197202
fallbackElement?: React.ReactNode;
@@ -200,18 +205,21 @@ export interface DataHashRouterProps {
200205
}
201206

202207
export function DataHashRouter({
208+
basename,
203209
children,
204210
hydrationData,
205211
fallbackElement,
206212
routes,
207213
window,
208214
}: DataBrowserRouterProps): React.ReactElement {
209215
return useRenderDataRouter({
216+
basename,
210217
children,
211218
fallbackElement,
212219
routes,
213220
createRouter: (routes) =>
214221
createHashRouter({
222+
basename,
215223
routes,
216224
hydrationData,
217225
window,

packages/react-router/__tests__/DataMemoryRouter-test.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,14 @@ describe("<DataMemoryRouter>", () => {
8686
`);
8787
});
8888

89-
it("renders the first route that matches the URL when wrapped in a 'basename' route", () => {
90-
// In data routers there is no basename and you should instead use a root route
89+
it("renders the first route that matches the URL when wrapped in a root route", () => {
9190
let { container } = render(
9291
<DataMemoryRouter
9392
initialEntries={["/my/base/path/thing"]}
9493
hydrationData={{}}
9594
>
9695
<Route path="/my/base/path">
97-
<Route element={<Outlet />}>
98-
<Route path="thing" element={<h1>Heyooo</h1>} />
99-
</Route>
96+
<Route path="thing" element={<h1>Heyooo</h1>} />
10097
</Route>
10198
</DataMemoryRouter>
10299
);
@@ -110,6 +107,26 @@ describe("<DataMemoryRouter>", () => {
110107
`);
111108
});
112109

110+
it("supports a basename prop", () => {
111+
let { container } = render(
112+
<DataMemoryRouter
113+
basename="/my/base/path"
114+
initialEntries={["/my/base/path/thing"]}
115+
hydrationData={{}}
116+
>
117+
<Route path="thing" element={<h1>Heyooo</h1>} />
118+
</DataMemoryRouter>
119+
);
120+
121+
expect(getHtml(container)).toMatchInlineSnapshot(`
122+
"<div>
123+
<h1>
124+
Heyooo
125+
</h1>
126+
</div>"
127+
`);
128+
});
129+
113130
it("renders with hydration data", async () => {
114131
let { container } = render(
115132
<DataMemoryRouter

packages/react-router/lib/components.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ export function _resetModuleScope() {
5656
* @private
5757
*/
5858
export function useRenderDataRouter({
59+
basename,
5960
children,
6061
fallbackElement,
6162
routes,
6263
createRouter,
6364
}: {
65+
basename?: string;
6466
children?: React.ReactNode;
6567
fallbackElement?: React.ReactNode;
6668
routes?: RouteObject[];
@@ -102,6 +104,7 @@ export function useRenderDataRouter({
102104
<DataRouterContext.Provider value={router}>
103105
<DataRouterStateContext.Provider value={state}>
104106
<Router
107+
basename={basename}
105108
location={state.location}
106109
navigationType={state.historyAction}
107110
navigator={navigator}
@@ -114,6 +117,7 @@ export function useRenderDataRouter({
114117
}
115118

116119
export interface DataMemoryRouterProps {
120+
basename?: string;
117121
children?: React.ReactNode;
118122
initialEntries?: InitialEntry[];
119123
initialIndex?: number;
@@ -123,6 +127,7 @@ export interface DataMemoryRouterProps {
123127
}
124128

125129
export function DataMemoryRouter({
130+
basename,
126131
children,
127132
initialEntries,
128133
initialIndex,
@@ -131,11 +136,13 @@ export function DataMemoryRouter({
131136
routes,
132137
}: DataMemoryRouterProps): React.ReactElement {
133138
return useRenderDataRouter({
139+
basename,
134140
children,
135141
fallbackElement,
136142
routes,
137143
createRouter: (routes) =>
138144
createMemoryRouter({
145+
basename,
139146
initialEntries,
140147
initialIndex,
141148
routes,

0 commit comments

Comments
 (0)