equinor / equinor/fusion-framework
Backport `navigate()` method to NavigationProvider on `react-18` branch
- Dominant language
- TypeScript
- Stars
- 10
- Forks
- 10
- Avg merge
- 19h 40m
- Merged PRs (30d)
- 150
Description
## Story
As a **Fusion app developer on the `react-18` branch**, I want the `NavigationProvider` (Navigator) to expose a `navigate(to?, options?)` method, so that I can use the same navigation API available on `main` without upgrading to React 19.
## Context
The `react-18` branch of `equinor/fusion-framework` preserves React 18 compatibility for apps that have not yet migrated to React 19. On `main`, `@equinor/fusion-framework-module-navigation` v7.0.0 introduced a unified `navigate(to?, options?)` method on the `NavigationProvider` class (`NavigationProvider.ts`). This method combines the behavior of `push()` and `replace()` with explicit `NavigateOptions` (replace flag, state), and both `push()` and `replace()` now delegate to it internally.
On the `react-18` branch (`@equinor/fusion-framework-module-navigation` v6.0.1), the `Navigator` class in `navigator.ts` only exposes `push(to, state?)` and `replace(to, state?)`. There is no `navigate()` method. This means:
- Apps that target the `react-18` branch cannot use `navigate()` and must use the lower-level `push`/`replace` pair directly.
- Code written against the `main` branch API is not portable to `react-18` without changes.
- Portal/framework consumers that expect `navigate()` on the provider (e.g., context-change navigation patterns in `useAppContextNavigation`) cannot be backported cleanly.
### Source references
| Branch | Package | Version | Provider class | File |
|---|---|---|---|---|
| `main` | `@equinor/fusion-framework-module-navigation` | 7.0.0 | `NavigationProvider` | `packages/modules/navigation/src/NavigationProvider.ts` |
| `react-18` | `@equinor/fusion-framework-module-navigation` | 6.0.1 | `Navigator` | `packages/modules/navigation/src/navigator.ts` |
## Functional requirements
- Add a `navigate(to?: To, options?: Partial)` method to the `Navigator` class on the `react-18` branch.
- The method should accept an optional `To` target and optional `NavigateOptions` with at least `replace` (boolean) and `state` (unknown).
- When `options.replace` is `true`, the method should call the underlying `replace()`; otherwise it should call `push()`.
- The existing `push()` and `replace()` methods should remain unchanged (no delegation refactor required on this branch — keep the change minimal).
- Export a `NavigateOptions` type from the package so consumers can import it.
- The method signature should be compatible with the `main` branch `INavigationProvider.navigate()` contract to ease future migration.
## Scenarios
- [ ] **Given** an app using `@equinor/fusion-framework-module-navigation` v6.x on the `react-18` branch, **When** the developer calls `navigator.navigate('/users')`, **Then** a new history entry is pushed for `/users`.
- [ ] **Given** the same setup, **When** the developer calls `navigator.navigate('/login', { replace: true })`, **Then** the current history entry is replaced with `/login`.
- [ ] **Given** the same setup, **When** the developer calls `navigator.navigate('/detail', { state: { id: 42 } })`, **Then** the state `{ id: 42 }` is associated with the new history entry.
- [ ] **Given** the same setup, **When** the developer calls `navigator.navigate()` with no arguments, **Then** it should navigate to the current path (no-op or refresh semantics, matching `main` behavior).
- [ ] **Given** code that calls `provider.navigate(to, opts)` on `main`, **When** the same call is made on `react-18` after this change, **Then** the navigation result is equivalent (push or replace with state).
## Non-goals
- Refactoring `push()` and `replace()` to delegate to `navigate()` internally (the `main` branch does this, but it is not required for the backport — keep the diff small).
- Backporting the full `NavigationProvider` class rewrite, telemetry integration, or `BaseModuleProvider` base class from `main`.
- Changing the `INavigator` interface inheritance (Observable + History) on `react-18`.
- Adding the `@equinor/fusion-framework-react-router` package to the `react-18` branch.
## Validation
- Unit test: `Navigator.navigate(to)` pushes a history entry.
- Unit test: `Navigator.navigate(to, { replace: true })` replaces the current entry.
- Unit test: `Navigator.navigate(to, { state })` forwards state correctly.
- Type check: `NavigateOptions` is exported and importable.
- Existing `push()` and `replace()` tests continue to pass unchanged.
## Notes
- Target branch: `react-18` in `equinor/fusion-framework`
- Target package: `@equinor/fusion-framework-module-navigation` (currently v6.0.1)
- Reference implementation on `main`: [`NavigationProvider.ts`](https://github.com/equinor/fusion-framework/blob/main/packages/modules/navigation/src/NavigationProvider.ts)
Contributor guide
Research direction
Start by comparing packages/modules/navigation/src/navigator.ts on the react-18 branch with the main-branch reference packages/modules/navigation/src/NavigationProvider.ts. Trace the existing push() and replace() behavior and the package's exported types, then run the navigation unit tests. Done means navigate() supports push, replace, and state scenarios, NavigateOptions is importable, and existing tests still pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100