Skip to content

Commit 6575a92

Browse files
committed
chore(react-router): removing rr5 test app since it will no longer be able to work, updating workflow to use rr6 tests, updating rr6 status docs
1 parent fc6e482 commit 6575a92

File tree

29 files changed

+128
-25094
lines changed

29 files changed

+128
-25094
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ jobs:
176176
strategy:
177177
fail-fast: false
178178
matrix:
179-
apps: [reactrouter5]
179+
apps: [reactrouter6]
180180
needs: [build-react, build-react-router]
181181
runs-on: ubuntu-latest
182182
steps:

.github/workflows/stencil-nightly.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ jobs:
186186
strategy:
187187
fail-fast: false
188188
matrix:
189-
apps: [reactrouter5]
189+
apps: [reactrouter6]
190190
needs: [build-react, build-react-router]
191191
runs-on: ubuntu-latest
192192
steps:
Lines changed: 126 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,172 @@
1-
# React Router 6 Status Report
1+
# React Router 6 Migration
22

33
**Branch:** `sk/react-router-6`
44
**Design Docs:** [PR #305](https://github.com/ionic-team/ionic-framework-design-documents/pull/305)
5-
**Last Updated:** November 22, 2025
5+
**Last Updated:** November 26, 2025
66

7-
## Summary
7+
## Overview
88

9-
The React Router 6 work is in good shape. The core router integration matches the design docs, the test app exercises most of the interesting cases, and the latest Cypress run shows that almost everything behaves as expected. What remains are a small set of advanced flows around not-found handling, some tab context behavior, and one swipe-to-go-back scenario.
9+
The `@ionic/react-router` package has been updated to support React Router 6. This migration replaces the React Router 5 integration with native RR6 APIs while preserving Ionic's navigation patterns, animations, and view lifecycle management.
1010

11-
At this point the work feels like late-stage refinement rather than architectural surgery.
11+
All Cypress tests are now passing.
1212

13-
- Total Cypress tests: 69
14-
- Passing: 62
15-
- Failing: 7
16-
- Suites with failures: `routing.cy.js`, `swipe-to-go-back.cy.js`, `tab-context.cy.js`
13+
| Metric | Count |
14+
|--------|-------|
15+
| Total tests | 70 |
16+
| Passing | 70 |
17+
| Failing | 0 |
1718

18-
Once those are cleaned up and the types/docs are brought up to date, this should be ready for an alpha release.
19+
## What Changed
1920

20-
---
21+
### Package Dependencies
2122

22-
## Test status (Cypress – React Router 6 test app)
23+
The `@ionic/react-router` package now requires React Router 6:
2324

24-
Latest run of `npm run cypress` against the RR6 test app:
25-
26-
- `dynamic-ionpage-classnames.cy.js` – 1/1 passing
27-
- `dynamic-routes.cy.js` – 3/3 passing
28-
- `dynamic-tabs.cy.js` – 3/3 passing
29-
- `multiple-tabs.cy.js` – 4/4 passing
30-
- `nested-outlets.cy.js` – 11/11 passing
31-
- `outlet-ref.cy.js` – 1/1 passing
32-
- `overlays.cy.js` – 3/3 passing
33-
- `refs.cy.js` – 2/2 passing
34-
- `replace-actions.cy.js` – 1/1 passing
35-
- `routing.cy.js` – 24/28 passing (4 failing)
36-
- `swipe-to-go-back.cy.js` – 7/8 passing (1 failing)
37-
- `tab-context.cy.js` – 0/2 passing (2 failing)
38-
- `tabs.cy.js` – 2/2 passing
39-
40-
So the bulk of the suite is green. The remaining problems are concentrated in three areas:
41-
42-
1. A handful of routing edge cases in `routing.cy.js`.
43-
2. One swipe-back path that gets confused after tab switching.
44-
3. The small tab-context demo that drives tabs via `IonTabsContext`.
45-
46-
---
47-
48-
## What’s working well
49-
50-
This is the “you don’t need to worry about these anymore” list.
51-
52-
### Core architecture
53-
54-
The main pieces match the RR6 design docs and behave sensibly in practice:
55-
56-
- **`IonRouter`** uses `useLocation` / `useNavigate` from React Router 6, tracks a `LocationHistory`, and computes `RouteInfo` objects that drive Ionic’s view stacks and directions.
57-
- **`ReactRouterViewStack`** owns view items per outlet, handles RR6-style matching (including params and wildcards), and coordinates mount/unmount via `ViewLifeCycleManager`.
58-
- **`StackManager`** glues a given `ion-router-outlet` to the view stack. It:
59-
- picks entering/leaving views,
60-
- calls `routerOutlet.commit(...)` for transitions,
61-
- handles nested outlets via a computed parent path, and
62-
- wires in swipe-to-go-back.
63-
- **Router variants** (`IonReactRouter`, `IonReactHashRouter`, `IonReactMemoryRouter`) all run on top of this stack and are behaving as expected.
64-
65-
There are no obvious architectural gaps left; the remaining bugs are about particular flows using this machinery, not about missing pieces.
66-
67-
### Navigation scenarios covered by passing suites
68-
69-
All of these are currently green in Cypress and line up with the v5 behavior:
70-
71-
- **Dynamic routes** – adding/removing routes at runtime and hitting them directly.
72-
- **Dynamic tabs** – adding a second tab at runtime and keeping the view state correct.
73-
- **Multiple tab bars** – apps that switch between different tab sets.
74-
- **Nested outlets** – both the simple nested-outlet demo and the deeper `nested-outlet2` flows now pass end‑to‑end.
75-
- **Overlays** – modal navigation (including navigating away while a modal is open) cleans up correctly.
76-
- **Refs** – forwarding refs to Ionic components works for both function and class components.
77-
- **Replace actions** – replace-style navigation behaves as expected in the dedicated demo.
78-
- **Tabs** – the basic tabs test suite (separate from the tab-context demo) is fully passing.
25+
```json
26+
"peerDependencies": {
27+
"react-router": ">=6.0.0",
28+
"react-router-dom": ">=6.0.0"
29+
}
30+
```
7931

80-
Taken together, that covers the majority of patterns people actually use: plain stacks, nested outlets, “tabs within a shell route”, and overlays.
32+
### Core Components
8133

82-
---
34+
**IonRouter** was rewritten as a functional component using React hooks. It now uses `useLocation` and `useNavigate` from React Router 6 instead of the `withRouter` HOC and `history` object from v5. The component continues to manage `LocationHistory` and compute `RouteInfo` objects for Ionic's view stacks and transition directions.
8335

84-
## Remaining problem areas
36+
**ReactRouterViewStack** was substantially expanded to handle RR6's matching semantics. Key additions include:
37+
- Support for RR6's `PathMatch` objects and pattern matching
38+
- Handling of index routes and wildcard routes (`*`)
39+
- View identity tracking for parameterized routes (`/user/:id`)
40+
- Proper computation of parent paths for nested outlets
8541

86-
### 1. `routing.cy.js` – four failing tests
42+
**StackManager** was updated to work with the new view stack implementation. Changes include:
43+
- Parent path derivation using RR6's route matching
44+
- Improved handling of `Navigate` redirect components
45+
- Better coordination of entering/leaving views during transitions
8746

88-
The main routing demo is mostly green, but four tests still fail:
47+
### New Utilities
8948

90-
1. **Not-found route**`/routing/asdf` is expected to render a `not-found` page and never does. This suggests that either the catch‑all route is not wired correctly for the RR6 matching rules, or the page is mounted but never becomes the active view item.
91-
2. **Menu + redirect interaction** – the “Menu → Favorites → Menu → Home with redirect” flow expects Favorites to be hidden and Home to be visible; Cypress still finds the old page. This points to a corner case in how we hide/unmount views when a redirect happens off a menu selection.
92-
3. **Back button visibility on pushed page** – after pushing a new page, the test expects the back button to appear. The view content appears, but we never hit the “show back button” state. This is likely a mismatch between our `LocationHistory` bookkeeping and how we decide whether the current view has something to go back to.
93-
4. **Parameterized route instances** – the “mount new view item instances of parameterized routes” test times out with a value mismatch (`'1'` vs something else). This is the same area where we do view cloning for `/user/1``/user/2`; we may be reusing the wrong view item or not updating route params the way the test expects.
49+
Four utility modules were added to support RR6's routing model:
9450

95-
All of these are focused on how we track history and view identity, not on basic RR6 API usage.
51+
| File | Purpose |
52+
|------|---------|
53+
| `matchPath.ts` | Extended path matching with RR6 pattern syntax |
54+
| `matchRoutesFromChildren.ts` | Converts Route children to RouteObjects for RR6's `matchRoutes` |
55+
| `derivePathnameToMatch.ts` | Computes the pathname portion relevant to a nested outlet |
56+
| `findRoutesNode.ts` | Locates Routes containers in the component tree |
9657

97-
### 2. `swipe-to-go-back.cy.js` – one failing test
58+
### Test App Updates
9859

99-
Seven of the eight swipe tests pass. The remaining failure is:
60+
All test pages in `packages/react-router/test/base/src/pages/` were updated to use RR6 syntax:
61+
- `<Route path="/foo" component={Foo} />` became `<Route path="/foo" element={<Foo />} />`
62+
- Nested routes now require trailing wildcards (`path="parent/*"`) when they contain child outlets
63+
- `<Redirect to="..." />` became `<Navigate to="..." replace />`
64+
- Route params accessed via `useParams()` instead of `props.match.params`
10065

101-
- **“should swipe and go back to correct tab after switching tabs”** – after switching tabs and using the swipe gesture, we expect to land back on the correct tab page (`data-pageid=home`) but never see it.
66+
## Test Coverage
10267

103-
The other swipe tests (including aborting gestures and swiping within a tab) behave correctly, so the problem is very specific: combining tab switching with a swipe‑back is leaving the `LocationHistory` and/or active view in a slightly inconsistent state.
68+
The Cypress test suite covers the following scenarios:
10469

105-
### 3. `tab-context.cy.js` – both tests failing
70+
| Suite | Tests | Description |
71+
|-------|-------|-------------|
72+
| routing.cy.js | 29 | Core navigation, tabs, back button, redirects, params |
73+
| nested-outlets.cy.js | 11 | Nested `IonRouterOutlet` behavior, back navigation |
74+
| swipe-to-go-back.cy.js | 8 | Gesture navigation, abort handling, tab interactions |
75+
| multiple-tabs.cy.js | 4 | Switching between different tab configurations |
76+
| dynamic-tabs.cy.js | 3 | Adding tabs at runtime |
77+
| dynamic-routes.cy.js | 3 | Adding routes at runtime |
78+
| overlays.cy.js | 3 | Modal cleanup on navigation |
79+
| refs.cy.js | 2 | Ref forwarding to Ionic components |
80+
| tabs.cy.js | 2 | Basic tab navigation and history |
81+
| tab-context.cy.js | 2 | Programmatic tab switching via context |
82+
| dynamic-ionpage-classnames.cy.js | 1 | Dynamic class application to IonPage |
83+
| outlet-ref.cy.js | 1 | Ref access to IonRouterOutlet |
84+
| replace-actions.cy.js | 1 | History replacement behavior |
10685

107-
The small tab-context demo, which exercises `IonTabsContext.selectTab`, still fails both of its tests:
86+
## Known Limitations
10887

109-
1. Navigating from `/tab-context` and calling `selectTab('tab2')` tries to click a button that lives on a page still marked as hidden (`ion-page-hidden`). In other words, we update the active tab in context, but the underlying view stack has not actually switched visible pages.
110-
2. Starting on `/tab-context/tab1` and trying to flip back to tab1 via context never finds the expected `tab2` page, which again points to view items not lining up with what the context thinks is active.
88+
### Route Path Syntax
11189

112-
The “normal” tabs test suite does not have this problem, so this looks isolated to how we bridge the context helper into the RR6 router + view stack.
90+
Nested outlets require parent routes to include a trailing wildcard:
11391

114-
---
92+
```tsx
93+
// Correct
94+
<Route path="parent/*" element={<Parent />} />
11595

116-
## Non-test gaps and cleanup work
96+
// Incorrect - child routes won't match
97+
<Route path="parent" element={<Parent />} />
98+
```
11799

118-
A few things that don’t show up as Cypress failures but are worth fixing before we put a label on this.
100+
This aligns with React Router 6's nested routing semantics where child routes are matched relative to the parent's path.
119101

120-
### TypeScript and types
102+
## Next Steps
121103

122-
`@ionic/react-router` is built against React Router 6, but the devDependencies still pull in v5 type packages:
104+
### Before Alpha Release
123105

124-
```json
125-
"@types/react-router": "^5.0.3",
126-
"@types/react-router-dom": "^5.1.5"
127-
```
128-
129-
That hasn’t bitten us in the test app, but it’s confusing and will matter for downstream consumers. We should:
130-
131-
- Drop the v5 type packages or replace them with the RR6 equivalents.
132-
- Run a full type check across the package once that change is made.
106+
1. **Run a TypeScript strict check** on the `@ionic/react-router` package to catch any type regressions
107+
2. **Manual testing pass** through the test app to verify animations and gestures feel correct
133108

134109
### Documentation
135110

136-
Right now the only “docs” for this work are the design document and this status file. Before releasing anything, we should have:
137-
138-
- A short migration guide for going from the v5 integration to the v6 one.
139-
- A clear explanation of any Ionic‑specific behavior that differs from plain React Router 6 (for example, `routeOptions.unmount`, how `LocationHistory` works, and how nested outlets should be declared with `/*`).
111+
The following documentation should be prepared before public release:
140112

141-
### Debug logging and general polish
113+
1. **Migration guide** covering:
114+
- Route syntax changes (`component` to `element`, `Redirect` to `Navigate`)
115+
- Nested route wildcard requirements
116+
- Accessing route params with hooks
117+
- Any removed or deprecated APIs
142118

143-
During bring‑up we added logging in a few places to trace routing behavior. Before shipping we should:
119+
2. **Updated API reference** for:
120+
- `IonReactRouter`, `IonReactHashRouter`, `IonReactMemoryRouter`
121+
- `IonRouterOutlet` behavior with nested routes
122+
- `routeOptions.unmount` and `LocationHistory` behavior
144123

145-
- Audit `IonRouter`, `ReactRouterViewStack`, and `StackManager` for stray `console.log` / `console.warn` calls.
146-
- Make sure any remaining logs are behind a flag or obviously diagnostic.
124+
### CI Integration
147125

148-
---
126+
The GitHub Actions workflows have been updated to run the RR6 test app:
127+
- `.github/workflows/build.yml`
128+
- `.github/workflows/stencil-nightly.yml`
149129

150-
## Short-term plan
130+
## Architecture Reference
151131

152-
This is a realistic sequence of work to get from the current state to “alpha ready”:
132+
The data flow through the routing system:
153133

154-
1. **Close out the remaining E2E failures**
155-
- Fix the not‑found and redirect/menu cases in `routing.cy.js`.
156-
- Correct the back‑button logic and parameterized route instance handling in that same spec.
157-
- Align swipe‑back behavior after tab switches with the existing (passing) swipe tests.
158-
- Make `IonTabsContext.selectTab` and the RR6 router agree on which view is active in the tab-context demo.
134+
```
135+
Browser History Change
136+
137+
138+
IonRouter (useLocation/useNavigate)
139+
140+
├── Updates LocationHistory
141+
├── Computes RouteInfo (action, direction, params)
142+
143+
144+
RouteManagerContext
145+
146+
147+
StackManager (per IonRouterOutlet)
148+
149+
├── Derives parent path from route children
150+
├── Matches routes using ReactRouterViewStack
151+
├── Determines entering/leaving views
152+
153+
154+
ion-router-outlet.commit()
155+
156+
157+
Native Ionic Transition
158+
```
159159

160-
2. **Clean up types and build configuration**
161-
- Remove or update the v5 `@types/react-router*` packages.
162-
- Run TypeScript with strict settings for `@ionic/react-router` and fix any fallout.
160+
The key insight is that Ionic intercepts React Router's navigation events and translates them into its own view management system, which enables native-feeling animations and gestures while still using React Router for URL management.
163161

164-
3. **Write the migration notes**
165-
- Document the route shape expectations (e.g., `parent/*` when using nested outlets).
166-
- Call out differences vs the v5 integration, especially around history behavior and any removed APIs.
162+
## Related Files
167163

168-
4. **Do a quick manual pass on the test app**
169-
- Click through the main demos (tabs, nested outlets, overlays, swipe‑back) with an eye for anything the tests don’t currently cover.
164+
Source code:
165+
- `packages/react-router/src/ReactRouter/IonRouter.tsx`
166+
- `packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx`
167+
- `packages/react-router/src/ReactRouter/StackManager.tsx`
168+
- `packages/react-router/src/ReactRouter/utils/`
170169

171-
If we keep the changes tightly focused on these areas, we shouldn’t need to revisit the overall architecture again.
170+
Test app:
171+
- `packages/react-router/test/base/` (shared test code)
172+
- `packages/react-router/test/apps/reactrouter6/` (RR6 config)

0 commit comments

Comments
 (0)