equinor / equinor/fusion-framework

Backport `navigate()` method to NavigationProvider on `react-18` branch

Open
#4,325 0 comments 0 reactions 0 assignees View on GitHub
👾 React 🚀 feature 🧬 Modules
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.