@equinor/fusion-framework-react-app 10.0.6 → 10.0.8

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.
@@ -1 +1 @@
1
- export declare const version = "10.0.6";
1
+ export declare const version = "10.0.8";
@@ -0,0 +1,70 @@
1
+ # AG Grid
2
+
3
+ Apply the Fusion-managed AG Grid theme to your `<AgGridReact>` instance.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useTheme } from '@equinor/fusion-framework-react-ag-grid';
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ The `useTheme` hook returns the current AG Grid theme configured by the Fusion AG Grid module. Pass the returned theme to `<AgGridReact>` to ensure your grid uses the correct Fusion/EDS styling.
14
+
15
+ > [!IMPORTANT]
16
+ > The AG Grid module must be enabled in the app configurator before `useTheme` is available. If the module is not registered, the hook throws an error.
17
+
18
+ ## useTheme
19
+
20
+ Returns the active AG Grid `Theme` object from the application-scoped AG Grid module.
21
+
22
+ **Signature:**
23
+
24
+ ```ts
25
+ function useTheme(): Theme;
26
+ ```
27
+
28
+ **Returns:** A `Theme` object compatible with AG Grid's `theme` prop.
29
+
30
+ **Throws** if the AG Grid module is not registered in the application.
31
+
32
+ ## Usage
33
+
34
+ ### Setup
35
+
36
+ Enable the AG Grid module in your app's configurator:
37
+
38
+ ```ts
39
+ import { enableAgGrid } from '@equinor/fusion-framework-module-ag-grid';
40
+
41
+ export const configure = (configurator) => {
42
+ enableAgGrid(configurator);
43
+ };
44
+ ```
45
+
46
+ ### Apply Theme to AgGridReact
47
+
48
+ ```tsx
49
+ import { useTheme } from '@equinor/fusion-framework-react-ag-grid';
50
+ import { AgGridReact } from '@equinor/fusion-framework-react-ag-grid';
51
+
52
+ const MyGrid = ({ rowData, columnDefs }) => {
53
+ const theme = useTheme();
54
+
55
+ return (
56
+ <div style={{ height: 500 }}>
57
+ <AgGridReact
58
+ theme={theme}
59
+ rowData={rowData}
60
+ columnDefs={columnDefs}
61
+ />
62
+ </div>
63
+ );
64
+ };
65
+ ```
66
+
67
+ ## Notes
68
+
69
+ - The theme is sourced from `@equinor/fusion-framework-module-ag-grid` — see the module documentation for detailed AG Grid configuration options
70
+ - The Fusion AG Grid theme follows EDS design tokens for consistent styling across apps
@@ -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,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,139 @@
1
+ # Settings
2
+
3
+ Persist and read app-specific user settings using the Fusion settings service.
4
+
5
+ **Import:**
6
+
7
+ ```ts
8
+ import { useAppSetting, useAppSettings } from '@equinor/fusion-framework-react-app/settings';
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ App settings let users customise their experience (theme, layout, filters) with values persisted per-user by the Fusion platform. The `useAppSetting` hook manages a single setting, while `useAppSettings` provides access to all settings at once.
14
+
15
+ ## Type Your Settings
16
+
17
+ Use module augmentation to get type-safe setting keys and values:
18
+
19
+ ```ts
20
+ declare module '@equinor/fusion-framework-react-app/settings' {
21
+ interface AppSettings {
22
+ theme: 'default' | 'light' | 'dark';
23
+ mode: 'simple' | 'advanced';
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## useAppSetting
29
+
30
+ Manages a single setting by key. Returns the current value and a setter function, similar to `useState`.
31
+
32
+ **Signature:**
33
+
34
+ ```ts
35
+ function useAppSetting<TSettings, TProp extends keyof TSettings>(
36
+ prop: TProp,
37
+ defaultValue?: TSettings[TProp],
38
+ hooks?: AppSettingsStatusHooks & {
39
+ onError?: (error: Error | null) => void;
40
+ onUpdated?: () => void;
41
+ },
42
+ ): [TSettings[TProp] | undefined, (update: TSettings[TProp] | ((current: TSettings[TProp] | undefined) => TSettings[TProp])) => void];
43
+ ```
44
+
45
+ ### Example
46
+
47
+ ```tsx
48
+ import { useCallback } from 'react';
49
+ import { useAppSetting } from '@equinor/fusion-framework-react-app/settings';
50
+
51
+ const ThemeSwitcher = () => {
52
+ const [theme, setTheme] = useAppSetting('theme', 'default');
53
+ const [mode, setMode] = useAppSetting('mode', 'simple');
54
+
55
+ const toggleMode = useCallback(() => {
56
+ setMode((current) => (current === 'simple' ? 'advanced' : 'simple'));
57
+ }, [setMode]);
58
+
59
+ return (
60
+ <div>
61
+ {/* cast: e.target.value is string, but setTheme expects the union type */}
62
+ <select value={theme ?? 'default'} onChange={(e) => setTheme(e.target.value as AppSettings['theme'])}>
63
+ <option value="default">Default</option>
64
+ <option value="light">Light</option>
65
+ <option value="dark">Dark</option>
66
+ </select>
67
+ <button onClick={toggleMode}>Toggle mode ({mode})</button>
68
+ </div>
69
+ );
70
+ };
71
+ ```
72
+
73
+ ## useAppSettings
74
+
75
+ Returns all settings as a single object with a bulk setter. Use `useAppSetting` for individual settings when possible — `useAppSettings` triggers a re-render on _any_ setting change.
76
+
77
+ **Signature:**
78
+
79
+ ```ts
80
+ function useAppSettings<TSettings>(
81
+ defaultValue?: TSettings,
82
+ hooks?: AppSettingsStatusHooks & {
83
+ onError?: (error: Error | null) => void;
84
+ onUpdated?: () => void;
85
+ },
86
+ ): [TSettings, (update: TSettings | ((current: TSettings | undefined) => TSettings)) => void];
87
+ ```
88
+
89
+ > [!WARNING]
90
+ > **`setSettings` must include all settings, not just the ones you want to change.** Use a callback to merge with the current state:
91
+ >
92
+ > ```ts
93
+ > setSettings((current) => ({ ...current, theme: 'dark' }));
94
+ > ```
95
+
96
+ ### Example
97
+
98
+ ```tsx
99
+ import { useCallback } from 'react';
100
+ import { useAppSettings } from '@equinor/fusion-framework-react-app/settings';
101
+
102
+ const SettingsPanel = () => {
103
+ const [settings, setSettings] = useAppSettings();
104
+
105
+ const updateTheme = useCallback(
106
+ (theme: AppSettings['theme']) => setSettings((current) => ({ ...current, theme })),
107
+ [setSettings],
108
+ );
109
+
110
+ return <ThemeSelector value={settings?.theme} onChange={updateTheme} />;
111
+ };
112
+ ```
113
+
114
+ ## Status Hooks
115
+
116
+ Both hooks accept optional status callbacks for loading, updating, and error states:
117
+
118
+ ```tsx
119
+ const [loading, setLoading] = useState(false);
120
+ const [updating, setUpdating] = useState(false);
121
+ const [error, setError] = useState<Error | null>(null);
122
+
123
+ const [theme, setTheme] = useAppSetting('theme', 'default', {
124
+ onLoading: setLoading,
125
+ onUpdating: setUpdating,
126
+ onError: setError,
127
+ onUpdated: useCallback(() => console.log('Saved'), []),
128
+ });
129
+ ```
130
+
131
+ > [!NOTE]
132
+ > `onUpdating` and `onLoading` reflect the _global_ settings state, not individual settings. Disable update buttons while either is `true`.
133
+
134
+ ## Notes
135
+
136
+ - Settings are async — there is a loading phase when the component mounts and an updating phase when values are saved
137
+ - `useAppSetting` captures the `hooks` object once on mount (via `useState`); **the callbacks must be stable before the first render** — changes after mount are ignored
138
+ - `useAppSettings` reads `hooks` on every render; **memoise callbacks** (e.g. with `useCallback`) to avoid unnecessary re-renders
139
+ - Settings are persisted per-user per-app by the Fusion platform
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-react-app",
3
- "version": "10.0.6",
3
+ "version": "10.0.8",
4
4
  "description": "",
5
5
  "main": "./dist/esm/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -117,14 +117,14 @@
117
117
  "directory": "packages/react"
118
118
  },
119
119
  "dependencies": {
120
- "@equinor/fusion-framework-app": "11.0.4",
121
- "@equinor/fusion-framework-module-app": "8.0.1",
122
120
  "@equinor/fusion-framework-module": "6.0.0",
123
- "@equinor/fusion-framework-module-http": "8.0.0",
124
- "@equinor/fusion-framework-module-navigation": "7.0.2",
121
+ "@equinor/fusion-framework-module-app": "8.0.2",
122
+ "@equinor/fusion-framework-app": "11.0.6",
123
+ "@equinor/fusion-framework-module-http": "8.0.1",
124
+ "@equinor/fusion-framework-react": "8.0.0",
125
125
  "@equinor/fusion-framework-react-module": "4.0.0",
126
126
  "@equinor/fusion-framework-react-module-http": "11.0.0",
127
- "@equinor/fusion-framework-react": "8.0.0"
127
+ "@equinor/fusion-framework-module-navigation": "7.0.3"
128
128
  },
129
129
  "devDependencies": {
130
130
  "@types/react": "^19.2.7",
@@ -133,23 +133,23 @@
133
133
  "react": "^19.2.1",
134
134
  "react-dom": "^19.2.1",
135
135
  "rxjs": "^7.8.1",
136
- "typescript": "^5.9.3",
136
+ "typescript": "^6.0.3",
137
137
  "vitest": "^4.1.0",
138
- "@equinor/fusion-framework-module-ag-grid": "36.0.0",
139
- "@equinor/fusion-framework-module-analytics": "2.0.3",
138
+ "@equinor/fusion-framework-module-ag-grid": "36.0.1",
140
139
  "@equinor/fusion-framework-module-event": "6.0.0",
140
+ "@equinor/fusion-framework-module-analytics": "2.0.5",
141
141
  "@equinor/fusion-framework-module-feature-flag": "2.0.1",
142
- "@equinor/fusion-framework-module-msal": "8.0.3",
142
+ "@equinor/fusion-framework-module-msal": "8.0.5",
143
143
  "@equinor/fusion-framework-react-module-bookmark": "6.0.0",
144
- "@equinor/fusion-framework-react-module-context": "7.0.0",
145
- "@equinor/fusion-observable": "9.0.1"
144
+ "@equinor/fusion-observable": "9.0.1",
145
+ "@equinor/fusion-framework-react-module-context": "7.0.0"
146
146
  },
147
147
  "peerDependencies": {
148
148
  "@types/react": "^18.0.0 || ^19.0.0",
149
149
  "react": "^18.0.0 || ^19.0.0",
150
150
  "react-dom": "^18.0.0 || ^19.0.0",
151
151
  "rxjs": "^7.0.0",
152
- "@equinor/fusion-framework-module-msal": "^8.0.3"
152
+ "@equinor/fusion-framework-module-msal": "^8.0.5"
153
153
  },
154
154
  "peerDependenciesMeta": {
155
155
  "@equinor/fusion-framework-react-module-ag-grid": {
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '10.0.6';
2
+ export const version = '10.0.8';