@equinor/fusion-framework-react-app 10.0.7 → 11.0.0

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.
@@ -0,0 +1,119 @@
1
+ # Feature Flag
2
+
3
+ Add, read, and toggle feature flags in your Fusion app.
4
+
5
+ > [!NOTE]
6
+ > Requires `@equinor/fusion-framework-module-feature-flag` to be installed as a dependency. The `enableFeatureFlag` helper and `useFeature` hook will not work without it.
7
+
8
+ **Import:**
9
+
10
+ ```ts
11
+ import { enableFeatureFlag, useFeature } from '@equinor/fusion-framework-react-app/feature-flag';
12
+ ```
13
+
14
+ ## Overview
15
+
16
+ Feature flags let you ship code behind a toggle — enabling gradual rollouts, A/B testing, or developer-only features. The `enableFeatureFlag` helper registers flags in your app's configurator, and the `useFeature` hook reads and toggles them at runtime. Framework-level flags are also visible through this hook.
17
+
18
+ ## Configure Feature Flags
19
+
20
+ Register flags in your app's configuration callback. Each flag has a `key`, a `title`, and optionally `allowUrl` to enable URL-driven overrides (e.g. `?my-flag=true`):
21
+
22
+ ```ts
23
+ import { enableFeatureFlag } from '@equinor/fusion-framework-react-app/feature-flag';
24
+
25
+ export const configure = (configurator) => {
26
+ enableFeatureFlag(configurator, [
27
+ {
28
+ key: 'dark-mode',
29
+ title: 'Dark Mode',
30
+ enabled: false,
31
+ },
32
+ {
33
+ key: 'beta-dashboard',
34
+ title: 'Beta Dashboard',
35
+ enabled: false,
36
+ allowUrl: true, // can be toggled via URL query parameter — requires the navigation module to be registered
37
+ },
38
+ ]);
39
+ };
40
+ ```
41
+
42
+ ## useFeature
43
+
44
+ Reads a single feature flag by key and provides a toggle callback. Merges feature flags from both the framework scope and the application scope, so framework-level flags are visible alongside app-specific ones.
45
+
46
+ **Signature:**
47
+
48
+ ```ts
49
+ function useFeature<T = unknown>(key: string): {
50
+ feature?: IFeatureFlag<T>;
51
+ toggleFeature: (enabled?: boolean) => void;
52
+ error?: unknown;
53
+ };
54
+ ```
55
+
56
+ **Returns:**
57
+
58
+ | Property | Type | Description |
59
+ | --------------- | --------------------------- | ----------------------------------------------------- |
60
+ | `feature` | `IFeatureFlag<T> \| undefined` | The resolved feature flag, or `undefined` if not found |
61
+ | `toggleFeature` | `(enabled?: boolean) => void` | Toggle the flag; pass `true`/`false` to set explicitly, or omit to invert |
62
+ | `error` | `unknown` | Any error from the feature-flag observable |
63
+
64
+ ### End-to-End Example
65
+
66
+ ```tsx
67
+ import { useFeature } from '@equinor/fusion-framework-react-app/feature-flag';
68
+
69
+ const Dashboard = () => {
70
+ const { feature, toggleFeature } = useFeature('beta-dashboard');
71
+
72
+ return (
73
+ <div>
74
+ <button onClick={() => toggleFeature()}>
75
+ {feature?.enabled ? 'Disable' : 'Enable'} beta dashboard
76
+ </button>
77
+ {feature?.enabled && <BetaDashboard />}
78
+ </div>
79
+ );
80
+ };
81
+ ```
82
+
83
+ ### Read-Only Flag Check
84
+
85
+ ```tsx
86
+ import { useFeature } from '@equinor/fusion-framework-react-app/feature-flag';
87
+
88
+ const FeatureGate = ({ flagKey, children }: { flagKey: string; children: React.ReactNode }) => {
89
+ const { feature } = useFeature(flagKey);
90
+ if (!feature?.enabled) return null;
91
+ return <>{children}</>;
92
+ };
93
+ ```
94
+
95
+ ## Advanced Configuration
96
+
97
+ For advanced scenarios, pass a builder callback instead of an array. The builder exposes `addPlugin` — use it to register feature sources such as the local-storage or URL plugin directly:
98
+
99
+ ```ts
100
+ import { enableFeatureFlag } from '@equinor/fusion-framework-react-app/feature-flag';
101
+ import {
102
+ createLocalStoragePlugin,
103
+ createUrlPlugin,
104
+ } from '@equinor/fusion-framework-module-feature-flag/plugins';
105
+
106
+ export const configure = (configurator) => {
107
+ enableFeatureFlag(configurator, (builder) => {
108
+ builder.addPlugin(createLocalStoragePlugin([{ key: 'dark-mode', title: 'Dark Mode', enabled: false }]));
109
+ // only add URL plugin if your app also registers the navigation module
110
+ builder.addPlugin(createUrlPlugin([{ key: 'beta-dashboard', title: 'Beta Dashboard', enabled: false }]));
111
+ });
112
+ };
113
+ ```
114
+
115
+ ## Notes
116
+
117
+ - Framework-level flags are merged with app-level flags — if both define the same key, the app-level flag takes precedence
118
+ - **Array overload**: local-storage persistence and URL override support are wired automatically. Flags with `allowUrl: true` are passed to the URL plugin (requires the `navigation` module)
119
+ - **Builder-callback overload**: no plugins are added automatically — add `createLocalStoragePlugin` and/or `createUrlPlugin` explicitly if you need persistence or URL overrides
@@ -0,0 +1,93 @@
1
+ # Framework
2
+
3
+ Access framework-level (portal/host) context and services from within your app.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useFramework, useCurrentUser, useFrameworkHttpClient } from '@equinor/fusion-framework-react-app/framework';
9
+ // useFrameworkCurrentContext is exported from the /context sub-path:
10
+ import { useFrameworkCurrentContext } from '@equinor/fusion-framework-react-app/context';
11
+ ```
12
+
13
+ ## When to Use
14
+
15
+ Most apps should use the **app-scoped** hooks from the `/context` sub-path (`@equinor/fusion-framework-react-app/context`). The framework sub-path is for specific cases where you need data from the portal/host level, regardless of your app's own module configuration.
16
+
17
+ > [!NOTE]
18
+ > **Prefer `/context` unless you specifically need framework-level context.**
19
+ >
20
+ > `useCurrentContext` (from `/context`) reads from your app's own context module.
21
+ > `useFrameworkCurrentContext` (also from `/context`) reads from the portal's context, bypassing your app's context module entirely.
22
+
23
+ ## useFrameworkCurrentContext
24
+
25
+ Returns the currently selected context from the **framework-level** context module — the portal or host application's context, not your app's.
26
+
27
+ Use this when your app hasn't configured its own context module but still needs to read what context the portal has selected.
28
+
29
+ **Signature:**
30
+
31
+ ```ts
32
+ function useFrameworkCurrentContext(): {
33
+ currentContext: ContextItem | undefined;
34
+ setCurrentContext: (entry?: ContextItem | string | null) => void;
35
+ };
36
+ ```
37
+
38
+ **Returns:** An object with `currentContext` (the portal's active context, or `undefined` if none is selected) and `setCurrentContext` to change it.
39
+
40
+ **Example:**
41
+
42
+ ```tsx
43
+ import { useFrameworkCurrentContext } from '@equinor/fusion-framework-react-app/context';
44
+
45
+ const PortalContextInfo = () => {
46
+ const { currentContext } = useFrameworkCurrentContext();
47
+
48
+ if (!currentContext) return <p>No portal context selected</p>;
49
+
50
+ return <p>Portal context: {currentContext.title}</p>;
51
+ };
52
+ ```
53
+
54
+ ## useFramework
55
+
56
+ Returns the Fusion framework instance directly, giving access to all framework-level modules.
57
+
58
+ **Signature:**
59
+
60
+ ```ts
61
+ function useFramework(): Fusion;
62
+ ```
63
+
64
+ ## useCurrentUser
65
+
66
+ Returns the currently authenticated user from the framework.
67
+
68
+ **Signature:**
69
+
70
+ ```ts
71
+ function useCurrentUser(): AccountInfo | undefined;
72
+ ```
73
+
74
+ ## useFrameworkHttpClient
75
+
76
+ Returns an HTTP client from the framework-level HTTP module (not your app's configured clients). Useful for accessing portal-provided API clients.
77
+
78
+ **Signature:**
79
+
80
+ ```ts
81
+ function useFrameworkHttpClient(name: 'portal' | 'people'): IHttpClient;
82
+ ```
83
+
84
+ **Throws** if no client is configured for the given key.
85
+
86
+ ## App-Scoped vs Framework-Scoped
87
+
88
+ | Need | Use | Sub-path |
89
+ | --------------------------------------------- | ---------------------------- | ------------- |
90
+ | Context your app configured | `useCurrentContext` | `/context` |
91
+ | Portal-level context (no app context module) | `useFrameworkCurrentContext` | `/context` |
92
+ | App-specific HTTP client | `useHttpClient` | `/http` |
93
+ | Portal-level HTTP client | `useFrameworkHttpClient` | `/framework` |
@@ -0,0 +1,88 @@
1
+ # Help Center
2
+
3
+ Open the Fusion portal help sidesheet programmatically from your app using the `useHelpCenter` hook.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useHelpCenter } from '@equinor/fusion-framework-react-app/help-center';
9
+ ```
10
+
11
+ > [!NOTE]
12
+ > This hook dispatches a framework event to the portal shell. The help module must be enabled by the host — your app does not configure it directly.
13
+
14
+ ## useHelpCenter
15
+
16
+ Returns an object with methods for opening specific pages of the portal help sidesheet.
17
+
18
+ **Signature:**
19
+
20
+ ```ts
21
+ useHelpCenter(): HelpCenter;
22
+ ```
23
+
24
+ **Returned methods:**
25
+
26
+ | Method | Description |
27
+ | ------------------------------- | --------------------------------------------------- |
28
+ | `openHelp()` | Opens the help sidesheet on the home page |
29
+ | `openArticle(articleId: string)` | Opens a specific help article by its slug or ID |
30
+ | `openFaqs()` | Opens the FAQs page |
31
+ | `openSearch(search: string)` | Opens the search page with a pre-filled query |
32
+ | `openGovernance()` | Opens the governance tab |
33
+ | `openReleaseNotes()` | Opens the release notes page |
34
+
35
+ ## Examples
36
+
37
+ ### Open a Specific Article from a Button
38
+
39
+ ```tsx
40
+ import { useHelpCenter } from '@equinor/fusion-framework-react-app/help-center';
41
+
42
+ const HelpButton = () => {
43
+ const { openArticle } = useHelpCenter();
44
+
45
+ return (
46
+ <button onClick={() => openArticle('getting-started')}>
47
+ How to get started
48
+ </button>
49
+ );
50
+ };
51
+ ```
52
+
53
+ ### Open Help Home
54
+
55
+ ```tsx
56
+ import { useHelpCenter } from '@equinor/fusion-framework-react-app/help-center';
57
+
58
+ const HelpLink = () => {
59
+ const { openHelp } = useHelpCenter();
60
+ return <button onClick={openHelp}>Help</button>;
61
+ };
62
+ ```
63
+
64
+ ### Search Help Content
65
+
66
+ ```tsx
67
+ import { useHelpCenter } from '@equinor/fusion-framework-react-app/help-center';
68
+
69
+ const SearchHelp = ({ query }: { query: string }) => {
70
+ const { openSearch } = useHelpCenter();
71
+ return <button onClick={() => openSearch(query)}>Search help</button>;
72
+ };
73
+ ```
74
+
75
+ ## How It Works
76
+
77
+ Each method dispatches a `@Portal::FusionHelp::open` framework event with a `page` discriminator. The portal shell listens for this event and opens the corresponding help sidesheet page. The event module must be available in the app's module scope — this is the default when running inside a Fusion portal.
78
+
79
+ The known `page` values and their additional fields are:
80
+
81
+ | `page` value | Extra fields | Opened by |
82
+ | ----------------- | ----------------------------- | ------------------ |
83
+ | `home` | — | `openHelp()` |
84
+ | `article` | `articleId: string` | `openArticle(id)` |
85
+ | `faqs` | — | `openFaqs()` |
86
+ | `search` | `search: string` | `openSearch(q)` |
87
+ | `governance` | — | `openGovernance()` |
88
+ | `release-notes` | — | `openReleaseNotes()` |
package/docs/http.md ADDED
@@ -0,0 +1,118 @@
1
+ # HTTP
2
+
3
+ Make authenticated HTTP calls from your Fusion app using the framework-managed HTTP client.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useHttpClient } from '@equinor/fusion-framework-react-app/http';
9
+ ```
10
+
11
+ **Selectors sub-path:**
12
+
13
+ ```ts
14
+ import { jsonSelector, blobSelector } from '@equinor/fusion-framework-react-app/http/selectors';
15
+ ```
16
+
17
+ ## Overview
18
+
19
+ The `useHttpClient` hook provides access to named HTTP clients that are pre-configured with authentication, base URLs, and interceptors. Clients must be registered in the app configurator before use — the hook creates a memoised client instance by name.
20
+
21
+ The `selectors` sub-path re-exports response selectors (`jsonSelector`, `blobSelector`, `createSseSelector`) from the HTTP module, providing typed helpers for parsing fetch responses.
22
+
23
+ ## Configure an HTTP Client
24
+
25
+ Register a named client in your app's configuration callback:
26
+
27
+ ```ts
28
+ import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app';
29
+
30
+ export const configure: AppModuleInitiator = (configurator) => {
31
+ configurator.configureHttpClient('my-api', {
32
+ baseUri: 'https://api.example.com',
33
+ defaultScopes: ['api://my-api/.default'],
34
+ });
35
+ };
36
+ ```
37
+
38
+ ## useHttpClient
39
+
40
+ Returns a configured `IHttpClient` instance by name. Throws if no client is registered for the given key.
41
+
42
+ **Signature:**
43
+
44
+ ```ts
45
+ function useHttpClient(name: string): IHttpClient;
46
+ ```
47
+
48
+ | Parameter | Type | Description |
49
+ | --------- | -------- | ------------------------------------ |
50
+ | `name` | `string` | Named client key from configuration |
51
+
52
+ **Returns:** An `IHttpClient` instance with `fetch`, `json`, `blob`, and other request methods.
53
+
54
+ ### Fetch JSON Data
55
+
56
+ ```tsx
57
+ import { useEffect, useState } from 'react';
58
+ import { useHttpClient } from '@equinor/fusion-framework-react-app/http';
59
+
60
+ type Item = { id: string; name: string };
61
+
62
+ const ItemList = () => {
63
+ const client = useHttpClient('my-api');
64
+ const [items, setItems] = useState<Item[]>([]);
65
+
66
+ useEffect(() => {
67
+ client.json<Item[]>('/items')
68
+ .then(setItems);
69
+ }, [client]);
70
+
71
+ return (
72
+ <ul>
73
+ {items.map((item) => (
74
+ <li key={item.id}>{item.name}</li>
75
+ ))}
76
+ </ul>
77
+ );
78
+ };
79
+ ```
80
+
81
+ ### POST Data
82
+
83
+ ```tsx
84
+ import { useCallback } from 'react';
85
+ import { useHttpClient } from '@equinor/fusion-framework-react-app/http';
86
+
87
+ const CreateItem = () => {
88
+ const client = useHttpClient('my-api');
89
+
90
+ const handleSubmit = useCallback(async (name: string) => {
91
+ await client.fetch('/items', {
92
+ method: 'POST',
93
+ body: JSON.stringify({ name }),
94
+ headers: { 'Content-Type': 'application/json' },
95
+ });
96
+ }, [client]);
97
+
98
+ return <button onClick={() => handleSubmit('New item')}>Create</button>;
99
+ };
100
+ ```
101
+
102
+ ## Response Selectors
103
+
104
+ The `selectors` sub-path provides typed helpers for parsing HTTP responses:
105
+
106
+ | Selector | Description |
107
+ | -------------------- | ---------------------------------------------- |
108
+ | `jsonSelector` | Parses response as JSON; use a typed call (e.g. `client.json<T>(...)`) to get a typed result |
109
+ | `blobSelector` | Returns response as a `Blob` |
110
+ | `createSseSelector` | Creates a selector for Server-Sent Events streams |
111
+
112
+ These are re-exported from `@equinor/fusion-framework-module-http/selectors`.
113
+
114
+ ## Prerequisites
115
+
116
+ - HTTP clients must be configured in your app's configurator before calling `useHttpClient`
117
+ - Authentication scopes are attached automatically based on the client configuration
118
+ - For detailed HTTP module configuration, see the [`@equinor/fusion-framework-module-http` documentation](../../../modules/http/README.md)
@@ -0,0 +1,80 @@
1
+ # Navigation
2
+
3
+ Set up client-side routing in your Fusion app using the framework-managed navigation module.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useRouter, useNavigationModule } from '@equinor/fusion-framework-react-app/navigation';
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ The navigation module wraps React Router with Fusion's base path handling, ensuring your app's routes work correctly under the portal's URL structure (e.g. `/apps/my-app/...`). Use `useRouter` to create a router instance and pass it to `<RouterProvider>`.
14
+
15
+ > [!IMPORTANT]
16
+ > Do not use `createBrowserRouter` from React Router directly — it bypasses the Fusion base path and will cause routing conflicts inside the portal.
17
+
18
+ ## Enable Navigation
19
+
20
+ The navigation module must be enabled in your app's configurator. Always pass the basename so routes resolve correctly under the portal's URL prefix:
21
+
22
+ ```ts
23
+ import { enableNavigation } from '@equinor/fusion-framework-module-navigation';
24
+
25
+ export const configure = (configurator) => {
26
+ // Pass the basename that matches where the portal mounts your app.
27
+ // In a Fusion portal this is typically the app's appKey path, e.g. '/apps/my-app'.
28
+ enableNavigation(configurator, '/apps/my-app');
29
+ };
30
+ ```
31
+
32
+ Without a basename, routes will not be prefixed and will conflict with the portal's URL structure.
33
+
34
+ ## useRouter
35
+
36
+ Creates a router instance from route definitions. The returned router is compatible with `<Router>` from `@equinor/fusion-framework-react-router`.
37
+
38
+ > [!WARNING]
39
+ > `useRouter` calls `INavigationProvider.createRouter()`, which is **deprecated** in the navigation module and emits a telemetry warning at runtime. For new apps, prefer `<Router>` from `@equinor/fusion-framework-react-router` directly — it handles the basename and Fusion context automatically without requiring `useRouter`.
40
+
41
+ > [!CAUTION]
42
+ > **Routes must be stable or memoised.** If you pass a new array reference on every render, the router will be recreated each time, resetting navigation state. Define routes outside the component or use `useMemo`.
43
+
44
+ ### Minimal Routing Example
45
+
46
+ ```tsx
47
+ import { useRouter } from '@equinor/fusion-framework-react-app/navigation';
48
+ import { Router } from '@equinor/fusion-framework-react-router';
49
+
50
+ const Home = () => <h1>Home</h1>;
51
+ const Settings = () => <h1>Settings</h1>;
52
+
53
+ // Define routes outside the component to keep them stable
54
+ const routes = [
55
+ { path: '/', element: <Home /> },
56
+ { path: '/settings', element: <Settings /> },
57
+ ];
58
+
59
+ const App = () => {
60
+ const router = useRouter(routes);
61
+ return <Router router={router} />;
62
+ };
63
+ ```
64
+
65
+ ## useNavigationModule
66
+
67
+ Returns the navigation module provider instance directly. Use this for advanced scenarios where you need access to the full provider API (e.g. programmatic navigation outside of React Router's hooks).
68
+
69
+ **Signature:**
70
+
71
+ ```ts
72
+ function useNavigationModule(): INavigationProvider;
73
+ ```
74
+
75
+ **Throws** if the navigation module has not been enabled for the application.
76
+
77
+ ## Prerequisites
78
+
79
+ - The navigation module must be enabled in your app's configurator via `enableNavigation`
80
+ - Do not mix `useRouter` with direct `createBrowserRouter` calls — use one approach consistently
@@ -0,0 +1,86 @@
1
+ # Routing
2
+
3
+ The `@equinor/fusion-framework-react-app/routing` entry point re-exports the full public API of
4
+ `@equinor/fusion-framework-react-router` — including the `<Router>` component, the route builder
5
+ DSL, all React Router hooks, and types — so you can import everything from a single package without
6
+ adding `@equinor/fusion-framework-react-router` as a direct dependency.
7
+
8
+ ## Installation
9
+
10
+ `@equinor/fusion-framework-react-router` is an **optional peer dependency**. Install it alongside the app package:
11
+
12
+ ```bash
13
+ pnpm add @equinor/fusion-framework-react-router
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```ts
19
+ import { Router } from '@equinor/fusion-framework-react-app/routing';
20
+ import { layout, index, route, prefix } from '@equinor/fusion-framework-react-app/routing';
21
+ ```
22
+
23
+ Everything exported from `@equinor/fusion-framework-react-router` and its `/routes` DSL is
24
+ available from this single entry point.
25
+
26
+ ## Enable navigation in your configurator
27
+
28
+ The `<Router>` component reads `history` and `basename` from the Fusion navigation module.
29
+ Enable it in your configurator before mounting:
30
+
31
+ ```ts
32
+ import { enableNavigation } from '@equinor/fusion-framework-module-navigation';
33
+ import type { IAppConfigurator, AppEnv, Fusion } from '@equinor/fusion-framework-react-app';
34
+
35
+ export const configure = (
36
+ configurator: IAppConfigurator,
37
+ args: { fusion: Fusion; env: AppEnv },
38
+ ) => {
39
+ enableNavigation(configurator, {
40
+ configure: (config) => {
41
+ config.setBasename(args.env.basename);
42
+ },
43
+ });
44
+ };
45
+ ```
46
+
47
+ ## Define routes
48
+
49
+ ```ts
50
+ // src/routes.ts
51
+ import { layout, index, route, prefix } from '@equinor/fusion-framework-react-app/routing';
52
+
53
+ export default layout('./Layout.tsx', [
54
+ index('./pages/HomePage.tsx'),
55
+ prefix('products', [
56
+ index('./pages/ProductsPage.tsx'),
57
+ route(':id', './pages/ProductPage.tsx'),
58
+ ]),
59
+ ]);
60
+ ```
61
+
62
+ ## Mount the Router
63
+
64
+ ```tsx
65
+ // src/Router.tsx
66
+ import { Router } from '@equinor/fusion-framework-react-app/routing';
67
+ import routes from './routes';
68
+
69
+ export default function AppRouter() {
70
+ return <Router routes={routes} />;
71
+ }
72
+ ```
73
+
74
+ ## Use routing hooks
75
+
76
+ All React Router hooks are re-exported from the same entry point:
77
+
78
+ ```tsx
79
+ import { useNavigate, useParams, useLocation, Link } from '@equinor/fusion-framework-react-app/routing';
80
+ ```
81
+
82
+ ## See also
83
+
84
+ - [Getting started](/modules/react/router/getting-started) — full setup walkthrough for the standalone router package
85
+ - [Interop entry point](/modules/react/router/interop) — `MemoryRouter` and other react-router bridges for testing and mid-migration
86
+ - [Migration guide](/modules/react/router/migration) — moving from a plain react-router setup