react-router 6.3.0 → 6.4.0-pre.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.eslintrc +12 -0
  2. package/CHANGELOG.md +8 -0
  3. package/__tests__/.eslintrc +8 -0
  4. package/__tests__/DataMemoryRouter-test.tsx +1902 -0
  5. package/__tests__/Route-test.tsx +45 -0
  6. package/__tests__/Router-basename-test.tsx +110 -0
  7. package/__tests__/Router-test.tsx +62 -0
  8. package/__tests__/Routes-location-test.tsx +69 -0
  9. package/__tests__/Routes-test.tsx +148 -0
  10. package/__tests__/__snapshots__/route-matching-test.tsx.snap +197 -0
  11. package/__tests__/absolute-path-matching-test.tsx +61 -0
  12. package/__tests__/createRoutesFromChildren-test.tsx +189 -0
  13. package/__tests__/descendant-routes-params-test.tsx +67 -0
  14. package/__tests__/descendant-routes-splat-matching-test.tsx +241 -0
  15. package/__tests__/descendant-routes-warning-test.tsx +140 -0
  16. package/__tests__/generatePath-test.tsx +45 -0
  17. package/__tests__/gh-issue-8127-test.tsx +32 -0
  18. package/__tests__/gh-issue-8165-test.tsx +97 -0
  19. package/__tests__/greedy-matching-test.tsx +89 -0
  20. package/__tests__/index-routes-test.tsx +24 -0
  21. package/__tests__/layout-routes-test.tsx +283 -0
  22. package/__tests__/matchPath-test.tsx +335 -0
  23. package/__tests__/matchRoutes-test.tsx +144 -0
  24. package/__tests__/navigate-test.tsx +49 -0
  25. package/__tests__/params-decode-test.tsx +36 -0
  26. package/__tests__/path-matching-test.tsx +270 -0
  27. package/__tests__/resolvePath-test.tsx +50 -0
  28. package/__tests__/route-depth-order-matching-test.tsx +135 -0
  29. package/__tests__/route-matching-test.tsx +164 -0
  30. package/__tests__/same-component-lifecycle-test.tsx +57 -0
  31. package/__tests__/setup.ts +15 -0
  32. package/__tests__/useHref-basename-test.tsx +351 -0
  33. package/__tests__/useHref-test.tsx +287 -0
  34. package/__tests__/useLocation-test.tsx +29 -0
  35. package/__tests__/useMatch-test.tsx +137 -0
  36. package/__tests__/useNavigate-test.tsx +100 -0
  37. package/__tests__/useOutlet-test.tsx +355 -0
  38. package/__tests__/useParams-test.tsx +212 -0
  39. package/__tests__/useResolvedPath-test.tsx +109 -0
  40. package/__tests__/useRoutes-test.tsx +122 -0
  41. package/__tests__/utils/renderStrict.tsx +21 -0
  42. package/__tests__/utils/waitForRedirect.tsx +5 -0
  43. package/index.ts +187 -0
  44. package/jest-transformer.js +10 -0
  45. package/jest.config.js +10 -0
  46. package/lib/components.tsx +491 -0
  47. package/lib/context.ts +96 -0
  48. package/lib/hooks.tsx +689 -0
  49. package/lib/use-sync-external-store-shim/index.ts +31 -0
  50. package/lib/use-sync-external-store-shim/useSyncExternalStoreShimClient.ts +153 -0
  51. package/lib/use-sync-external-store-shim/useSyncExternalStoreShimServer.ts +20 -0
  52. package/node-main.js +7 -0
  53. package/package.json +7 -4
  54. package/tsconfig.json +20 -0
  55. package/LICENSE.md +0 -22
  56. package/index.d.ts +0 -14
  57. package/index.js +0 -941
  58. package/index.js.map +0 -1
  59. package/lib/components.d.ts +0 -110
  60. package/lib/context.d.ts +0 -31
  61. package/lib/hooks.d.ts +0 -99
  62. package/lib/router.d.ts +0 -120
  63. package/main.js +0 -19
  64. package/react-router.development.js +0 -895
  65. package/react-router.development.js.map +0 -1
  66. package/react-router.production.min.js +0 -12
  67. package/react-router.production.min.js.map +0 -1
  68. package/umd/react-router.development.js +0 -990
  69. package/umd/react-router.development.js.map +0 -1
  70. package/umd/react-router.production.min.js +0 -12
  71. package/umd/react-router.production.min.js.map +0 -1
@@ -0,0 +1,137 @@
1
+ import * as React from "react";
2
+ import * as TestRenderer from "react-test-renderer";
3
+ import { MemoryRouter, PathMatch, Routes, Route, useMatch } from "react-router";
4
+
5
+ function ShowMatch({ pattern }: { pattern: string }) {
6
+ return <pre>{JSON.stringify(useMatch(pattern), null, 2)}</pre>;
7
+ }
8
+
9
+ describe("useMatch", () => {
10
+ describe("when the path matches the current URL", () => {
11
+ it("returns the match", () => {
12
+ let renderer: TestRenderer.ReactTestRenderer;
13
+ TestRenderer.act(() => {
14
+ renderer = TestRenderer.create(
15
+ <MemoryRouter initialEntries={["/home"]}>
16
+ <Routes>
17
+ <Route path="/" element={<ShowMatch pattern="home" />}>
18
+ <Route path="/home" element={<h1>Home</h1>} />
19
+ </Route>
20
+ </Routes>
21
+ </MemoryRouter>
22
+ );
23
+ });
24
+
25
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
26
+ <pre>
27
+ {
28
+ "params": {},
29
+ "pathname": "/home",
30
+ "pathnameBase": "/home",
31
+ "pattern": {
32
+ "path": "home",
33
+ "caseSensitive": false,
34
+ "end": true
35
+ }
36
+ }
37
+ </pre>
38
+ `);
39
+ });
40
+ });
41
+
42
+ describe("when the current URL ends with a slash", () => {
43
+ it("returns the match.pathname with the trailing slash", () => {
44
+ let renderer: TestRenderer.ReactTestRenderer;
45
+ TestRenderer.act(() => {
46
+ renderer = TestRenderer.create(
47
+ <MemoryRouter initialEntries={["/home/"]}>
48
+ <Routes>
49
+ <Route path="/" element={<ShowMatch pattern="home" />}>
50
+ <Route path="/home" element={<h1>Home</h1>} />
51
+ </Route>
52
+ </Routes>
53
+ </MemoryRouter>
54
+ );
55
+ });
56
+
57
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
58
+ <pre>
59
+ {
60
+ "params": {},
61
+ "pathname": "/home/",
62
+ "pathnameBase": "/home",
63
+ "pattern": {
64
+ "path": "home",
65
+ "caseSensitive": false,
66
+ "end": true
67
+ }
68
+ }
69
+ </pre>
70
+ `);
71
+ });
72
+ });
73
+
74
+ describe("when the path does not match the current URL", () => {
75
+ it("returns null", () => {
76
+ let renderer: TestRenderer.ReactTestRenderer;
77
+ TestRenderer.act(() => {
78
+ renderer = TestRenderer.create(
79
+ <MemoryRouter initialEntries={["/home"]}>
80
+ <Routes>
81
+ <Route path="/" element={<ShowMatch pattern="about" />}>
82
+ <Route path="/home" element={<h1>Home</h1>} />
83
+ </Route>
84
+ </Routes>
85
+ </MemoryRouter>
86
+ );
87
+ });
88
+
89
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
90
+ <pre>
91
+ null
92
+ </pre>
93
+ `);
94
+ });
95
+ });
96
+
97
+ describe("when re-rendered with the same URL", () => {
98
+ it("returns the memoized match", () => {
99
+ let path = "/home";
100
+ let match: PathMatch<string>;
101
+ let firstMatch: PathMatch<string>;
102
+
103
+ function HomePage() {
104
+ match = useMatch(path);
105
+
106
+ if (!firstMatch) {
107
+ firstMatch = match;
108
+ }
109
+
110
+ return null;
111
+ }
112
+
113
+ let renderer: TestRenderer.ReactTestRenderer;
114
+ TestRenderer.act(() => {
115
+ renderer = TestRenderer.create(
116
+ <MemoryRouter initialEntries={[path]}>
117
+ <Routes>
118
+ <Route path={path} element={<HomePage />} />
119
+ </Routes>
120
+ </MemoryRouter>
121
+ );
122
+ });
123
+
124
+ TestRenderer.act(() => {
125
+ renderer.update(
126
+ <MemoryRouter initialEntries={[path]}>
127
+ <Routes>
128
+ <Route path={path} element={<HomePage />} />
129
+ </Routes>
130
+ </MemoryRouter>
131
+ );
132
+ });
133
+
134
+ expect(match).toBe(firstMatch);
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,100 @@
1
+ import * as React from "react";
2
+ import * as TestRenderer from "react-test-renderer";
3
+ import {
4
+ MemoryRouter,
5
+ Routes,
6
+ Route,
7
+ useNavigate,
8
+ useLocation,
9
+ } from "react-router";
10
+
11
+ describe("useNavigate", () => {
12
+ it("navigates to the new location", () => {
13
+ function Home() {
14
+ let navigate = useNavigate();
15
+
16
+ function handleClick() {
17
+ navigate("/about");
18
+ }
19
+
20
+ return (
21
+ <div>
22
+ <h1>Home</h1>
23
+ <button onClick={handleClick}>click me</button>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ let renderer: TestRenderer.ReactTestRenderer;
29
+ TestRenderer.act(() => {
30
+ renderer = TestRenderer.create(
31
+ <MemoryRouter initialEntries={["/home"]}>
32
+ <Routes>
33
+ <Route path="home" element={<Home />} />
34
+ <Route path="about" element={<h1>About</h1>} />
35
+ </Routes>
36
+ </MemoryRouter>
37
+ );
38
+ });
39
+
40
+ let button = renderer.root.findByType("button");
41
+
42
+ TestRenderer.act(() => {
43
+ button.props.onClick();
44
+ });
45
+
46
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
47
+ <h1>
48
+ About
49
+ </h1>
50
+ `);
51
+ });
52
+
53
+ describe("with state", () => {
54
+ it("adds the state to location.state", () => {
55
+ function Home() {
56
+ let navigate = useNavigate();
57
+
58
+ function handleClick() {
59
+ navigate("/about", { state: { from: "home" } });
60
+ }
61
+
62
+ return (
63
+ <div>
64
+ <h1>Home</h1>
65
+ <button onClick={handleClick}>click me</button>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ function ShowLocationState() {
71
+ return <p>location.state: {JSON.stringify(useLocation().state)}</p>;
72
+ }
73
+
74
+ let renderer: TestRenderer.ReactTestRenderer;
75
+ TestRenderer.act(() => {
76
+ renderer = TestRenderer.create(
77
+ <MemoryRouter initialEntries={["/home"]}>
78
+ <Routes>
79
+ <Route path="home" element={<Home />} />
80
+ <Route path="about" element={<ShowLocationState />} />
81
+ </Routes>
82
+ </MemoryRouter>
83
+ );
84
+ });
85
+
86
+ let button = renderer.root.findByType("button");
87
+
88
+ TestRenderer.act(() => {
89
+ button.props.onClick();
90
+ });
91
+
92
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
93
+ <p>
94
+ location.state:
95
+ {"from":"home"}
96
+ </p>
97
+ `);
98
+ });
99
+ });
100
+ });
@@ -0,0 +1,355 @@
1
+ import * as React from "react";
2
+ import * as TestRenderer from "react-test-renderer";
3
+ import {
4
+ MemoryRouter,
5
+ Routes,
6
+ Route,
7
+ useOutlet,
8
+ useOutletContext,
9
+ } from "react-router";
10
+
11
+ describe("useOutlet", () => {
12
+ describe("when there is no child route", () => {
13
+ it("returns null", () => {
14
+ function Home() {
15
+ return useOutlet();
16
+ }
17
+
18
+ let renderer: TestRenderer.ReactTestRenderer;
19
+ TestRenderer.act(() => {
20
+ renderer = TestRenderer.create(
21
+ <MemoryRouter initialEntries={["/home"]}>
22
+ <Routes>
23
+ <Route path="/home" element={<Home />} />
24
+ </Routes>
25
+ </MemoryRouter>
26
+ );
27
+ });
28
+
29
+ expect(renderer.toJSON()).toBeNull();
30
+ });
31
+
32
+ it("renders the fallback", () => {
33
+ function Home() {
34
+ let outlet = useOutlet();
35
+ return <div>{outlet ? "outlet" : "no outlet"}</div>;
36
+ }
37
+
38
+ let renderer: TestRenderer.ReactTestRenderer;
39
+ TestRenderer.act(() => {
40
+ renderer = TestRenderer.create(
41
+ <MemoryRouter initialEntries={["/home"]}>
42
+ <Routes>
43
+ <Route path="/home" element={<Home />} />
44
+ </Routes>
45
+ </MemoryRouter>
46
+ );
47
+ });
48
+
49
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
50
+ <div>
51
+ no outlet
52
+ </div>
53
+ `);
54
+ });
55
+
56
+ it("renders the fallback with context provided", () => {
57
+ function Home() {
58
+ let outlet = useOutlet({ some: "context" });
59
+ return <div>{outlet ? "outlet" : "no outlet"}</div>;
60
+ }
61
+
62
+ let renderer: TestRenderer.ReactTestRenderer;
63
+ TestRenderer.act(() => {
64
+ renderer = TestRenderer.create(
65
+ <MemoryRouter initialEntries={["/home"]}>
66
+ <Routes>
67
+ <Route path="/home" element={<Home />} />
68
+ </Routes>
69
+ </MemoryRouter>
70
+ );
71
+ });
72
+
73
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
74
+ <div>
75
+ no outlet
76
+ </div>
77
+ `);
78
+ });
79
+ });
80
+
81
+ describe("when there is a child route", () => {
82
+ it("returns an element", () => {
83
+ function Users() {
84
+ return useOutlet();
85
+ }
86
+
87
+ let renderer: TestRenderer.ReactTestRenderer;
88
+ TestRenderer.act(() => {
89
+ renderer = TestRenderer.create(
90
+ <MemoryRouter initialEntries={["/users/profile"]}>
91
+ <Routes>
92
+ <Route path="users" element={<Users />}>
93
+ <Route path="profile" element={<h1>Profile</h1>} />
94
+ </Route>
95
+ </Routes>
96
+ </MemoryRouter>
97
+ );
98
+ });
99
+
100
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
101
+ <h1>
102
+ Profile
103
+ </h1>
104
+ `);
105
+ });
106
+
107
+ it("returns an element when no context", () => {
108
+ function Home() {
109
+ let outlet = useOutlet();
110
+ return <div>{outlet ? "outlet" : "no outlet"}</div>;
111
+ }
112
+
113
+ let renderer: TestRenderer.ReactTestRenderer;
114
+ TestRenderer.act(() => {
115
+ renderer = TestRenderer.create(
116
+ <MemoryRouter initialEntries={["/home"]}>
117
+ <Routes>
118
+ <Route path="/home" element={<Home />}>
119
+ <Route index element={<div>index</div>} />
120
+ </Route>
121
+ </Routes>
122
+ </MemoryRouter>
123
+ );
124
+ });
125
+
126
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
127
+ <div>
128
+ outlet
129
+ </div>
130
+ `);
131
+ });
132
+
133
+ it("returns an element when context", () => {
134
+ function Home() {
135
+ let outlet = useOutlet({ some: "context" });
136
+ return <div>{outlet ? "outlet" : "no outlet"}</div>;
137
+ }
138
+
139
+ let renderer: TestRenderer.ReactTestRenderer;
140
+ TestRenderer.act(() => {
141
+ renderer = TestRenderer.create(
142
+ <MemoryRouter initialEntries={["/home"]}>
143
+ <Routes>
144
+ <Route path="/home" element={<Home />}>
145
+ <Route index element={<div>index</div>} />
146
+ </Route>
147
+ </Routes>
148
+ </MemoryRouter>
149
+ );
150
+ });
151
+
152
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
153
+ <div>
154
+ outlet
155
+ </div>
156
+ `);
157
+ });
158
+ });
159
+
160
+ describe("OutletContext when there is no context", () => {
161
+ it("returns null", () => {
162
+ function Users() {
163
+ return useOutlet();
164
+ }
165
+
166
+ function Profile() {
167
+ let outletContext = useOutletContext();
168
+
169
+ return (
170
+ <div>
171
+ <h1>Profile</h1>
172
+ <pre>{outletContext}</pre>
173
+ </div>
174
+ );
175
+ }
176
+
177
+ let renderer: TestRenderer.ReactTestRenderer;
178
+ TestRenderer.act(() => {
179
+ renderer = TestRenderer.create(
180
+ <MemoryRouter initialEntries={["/users/profile"]}>
181
+ <Routes>
182
+ <Route path="users" element={<Users />}>
183
+ <Route path="profile" element={<Profile />} />
184
+ </Route>
185
+ </Routes>
186
+ </MemoryRouter>
187
+ );
188
+ });
189
+
190
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
191
+ <div>
192
+ <h1>
193
+ Profile
194
+ </h1>
195
+ <pre />
196
+ </div>
197
+ `);
198
+ });
199
+ });
200
+
201
+ describe("OutletContext when there is context", () => {
202
+ it("returns the context", () => {
203
+ function Users() {
204
+ return useOutlet([
205
+ "Mary",
206
+ "Jane",
207
+ "Michael",
208
+ "Bert",
209
+ "Winifred",
210
+ "George",
211
+ ]);
212
+ }
213
+
214
+ function Profile() {
215
+ let outletContext = useOutletContext<string[]>();
216
+
217
+ return (
218
+ <div>
219
+ <h1>Profile</h1>
220
+ <ul>
221
+ {outletContext.map((name) => (
222
+ <li key={name}>{name}</li>
223
+ ))}
224
+ </ul>
225
+ </div>
226
+ );
227
+ }
228
+
229
+ let renderer: TestRenderer.ReactTestRenderer;
230
+ TestRenderer.act(() => {
231
+ renderer = TestRenderer.create(
232
+ <MemoryRouter initialEntries={["/users/profile"]}>
233
+ <Routes>
234
+ <Route path="users" element={<Users />}>
235
+ <Route path="profile" element={<Profile />} />
236
+ </Route>
237
+ </Routes>
238
+ </MemoryRouter>
239
+ );
240
+ });
241
+
242
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
243
+ <div>
244
+ <h1>
245
+ Profile
246
+ </h1>
247
+ <ul>
248
+ <li>
249
+ Mary
250
+ </li>
251
+ <li>
252
+ Jane
253
+ </li>
254
+ <li>
255
+ Michael
256
+ </li>
257
+ <li>
258
+ Bert
259
+ </li>
260
+ <li>
261
+ Winifred
262
+ </li>
263
+ <li>
264
+ George
265
+ </li>
266
+ </ul>
267
+ </div>
268
+ `);
269
+ });
270
+ });
271
+
272
+ describe("when child route without element prop", () => {
273
+ it("returns nested route element", () => {
274
+ function Layout() {
275
+ return useOutlet();
276
+ }
277
+
278
+ let renderer: TestRenderer.ReactTestRenderer;
279
+ TestRenderer.act(() => {
280
+ renderer = TestRenderer.create(
281
+ <MemoryRouter initialEntries={["/users/profile"]}>
282
+ <Routes>
283
+ <Route path="/" element={<Layout />}>
284
+ <Route path="users">
285
+ <Route path="profile" element={<h1>Profile</h1>} />
286
+ </Route>
287
+ </Route>
288
+ </Routes>
289
+ </MemoryRouter>
290
+ );
291
+ });
292
+
293
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
294
+ <h1>
295
+ Profile
296
+ </h1>
297
+ `);
298
+ });
299
+
300
+ it("returns the context", () => {
301
+ function Layout() {
302
+ return useOutlet(["Mary", "Jane", "Michael"]);
303
+ }
304
+
305
+ function Profile() {
306
+ let outletContext = useOutletContext<string[]>();
307
+
308
+ return (
309
+ <div>
310
+ <h1>Profile</h1>
311
+ <ul>
312
+ {outletContext.map((name) => (
313
+ <li key={name}>{name}</li>
314
+ ))}
315
+ </ul>
316
+ </div>
317
+ );
318
+ }
319
+
320
+ let renderer: TestRenderer.ReactTestRenderer;
321
+ TestRenderer.act(() => {
322
+ renderer = TestRenderer.create(
323
+ <MemoryRouter initialEntries={["/users/profile"]}>
324
+ <Routes>
325
+ <Route path="/" element={<Layout />}>
326
+ <Route path="users">
327
+ <Route path="profile" element={<Profile />} />
328
+ </Route>
329
+ </Route>
330
+ </Routes>
331
+ </MemoryRouter>
332
+ );
333
+ });
334
+
335
+ expect(renderer.toJSON()).toMatchInlineSnapshot(`
336
+ <div>
337
+ <h1>
338
+ Profile
339
+ </h1>
340
+ <ul>
341
+ <li>
342
+ Mary
343
+ </li>
344
+ <li>
345
+ Jane
346
+ </li>
347
+ <li>
348
+ Michael
349
+ </li>
350
+ </ul>
351
+ </div>
352
+ `);
353
+ });
354
+ });
355
+ });