@equinor/fusion-framework-react-app 14.0.3 → 14.1.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.
- package/CHANGELOG.md +55 -0
- package/README.md +9 -2
- package/dist/esm/ag-grid/community.js +12 -0
- package/dist/esm/ag-grid/community.js.map +1 -0
- package/dist/esm/ag-grid/enterprise.js +12 -0
- package/dist/esm/ag-grid/enterprise.js.map +1 -0
- package/dist/esm/ag-grid/react.js +12 -0
- package/dist/esm/ag-grid/react.js.map +1 -0
- package/dist/esm/ag-grid/testing.js +19 -0
- package/dist/esm/ag-grid/testing.js.map +1 -0
- package/dist/esm/ag-grid/theme.js +13 -0
- package/dist/esm/ag-grid/theme.js.map +1 -0
- package/dist/esm/create-component.js +1 -1
- package/dist/esm/msal/useToken.js +12 -3
- package/dist/esm/msal/useToken.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ag-grid/community.d.ts +11 -0
- package/dist/types/ag-grid/enterprise.d.ts +11 -0
- package/dist/types/ag-grid/react.d.ts +11 -0
- package/dist/types/ag-grid/testing.d.ts +18 -0
- package/dist/types/ag-grid/theme.d.ts +13 -0
- package/dist/types/create-component.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/docs/bookmark.md +7 -0
- package/docs/context.md +3 -1
- package/docs/framework.md +2 -2
- package/docs/msal.md +3 -3
- package/package.json +77 -25
- package/src/__tests__/Apploader.test.tsx +51 -0
- package/src/__tests__/fixtures/apploader-child-script.ts +9 -0
- package/src/__tests__/testApp.test.tsx +76 -0
- package/src/__tests__/useAccessToken.test.tsx +51 -0
- package/src/__tests__/useAppSetting.test.tsx +133 -0
- package/src/__tests__/useAppSettings.test.tsx +147 -0
- package/src/__tests__/useCurrentAccount.test.tsx +32 -0
- package/src/__tests__/useCurrentBookmark.test.tsx +108 -0
- package/src/__tests__/useCurrentContext.test.tsx +72 -0
- package/src/__tests__/useFeature.test.tsx +104 -0
- package/src/__tests__/useHelpCenter.test.tsx +64 -0
- package/src/__tests__/useStateSyncEvents.test.ts +12 -11
- package/src/__tests__/useToken.test.tsx +71 -0
- package/src/__tests__/useTrackFeature.test.tsx +83 -0
- package/src/ag-grid/community.ts +11 -0
- package/src/ag-grid/enterprise.ts +11 -0
- package/src/ag-grid/react.ts +11 -0
- package/src/ag-grid/testing.ts +19 -0
- package/src/ag-grid/theme.ts +17 -0
- package/src/create-component.tsx +1 -1
- package/src/msal/useToken.ts +14 -3
- package/src/version.ts +1 -1
- package/tsconfig.json +6 -0
- package/vitest.config.ts +2 -8
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mockFramework } from '@equinor/fusion-framework/mock';
|
|
4
|
+
import { enableAppManifestMock } from '@equinor/fusion-framework-app/mock';
|
|
5
|
+
import type { AppModule } from '@equinor/fusion-framework-module-app';
|
|
6
|
+
import { enableAnalytics, type AnalyticsModule } from '@equinor/fusion-framework-module-analytics';
|
|
7
|
+
import { MockAnalyticsAdapter } from '@equinor/fusion-framework-module-analytics/mock';
|
|
8
|
+
import type { MockTelemetryAdapter } from '@equinor/fusion-framework-module-telemetry/mock';
|
|
9
|
+
|
|
10
|
+
import { useTrackFeature } from '../analytics/useTrackFeature';
|
|
11
|
+
import { renderAppHook } from '@equinor/fusion-framework-vitest-plugin-react-app/test';
|
|
12
|
+
|
|
13
|
+
const env = {
|
|
14
|
+
manifest: {
|
|
15
|
+
appKey: 'test-app',
|
|
16
|
+
displayName: 'Test App',
|
|
17
|
+
description: 'A test application',
|
|
18
|
+
type: 'standalone' as const,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('useTrackFeature', () => {
|
|
23
|
+
it('tracks an app-feature analytics event carrying the current app’s key, leaving telemetry untouched', async () => {
|
|
24
|
+
const recorder = new MockAnalyticsAdapter();
|
|
25
|
+
|
|
26
|
+
const fusion = await mockFramework<[AppModule, AnalyticsModule]>((configurator) => {
|
|
27
|
+
enableAppManifestMock(configurator, env);
|
|
28
|
+
enableAnalytics(configurator, (builder) => {
|
|
29
|
+
builder.setAdapter('mock', async () => recorder);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
fusion.modules.app.setCurrentApp('test-app');
|
|
33
|
+
|
|
34
|
+
const { result } = await renderAppHook(() => useTrackFeature(), { env, fusion });
|
|
35
|
+
result.current('button-click', { section: 'header' });
|
|
36
|
+
|
|
37
|
+
// analytics is the only channel a successful call should reach
|
|
38
|
+
expect(recorder.getAnalytics()).toMatchObject([
|
|
39
|
+
{
|
|
40
|
+
name: 'app-feature',
|
|
41
|
+
value: { feature: 'button-click', data: { section: 'header' } },
|
|
42
|
+
attributes: { appKey: 'test-app', context: undefined },
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
// the framework logs its own telemetry regardless; the fallback diagnostic
|
|
46
|
+
// specifically must not fire when analytics was reached successfully
|
|
47
|
+
const telemetry = fusion.modules.telemetry.getAdapter('mock') as MockTelemetryAdapter;
|
|
48
|
+
expect(telemetry.getItems('AnalyticsProviderNotFound')).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('includes the current context alongside the app key', async () => {
|
|
52
|
+
const recorder = new MockAnalyticsAdapter();
|
|
53
|
+
const project = { id: 'ctx-1', title: 'My project', type: { id: 'ProjectMaster' }, value: {} };
|
|
54
|
+
|
|
55
|
+
const fusion = await mockFramework<[AppModule, AnalyticsModule]>((configurator) => {
|
|
56
|
+
enableAppManifestMock(configurator, env);
|
|
57
|
+
enableAnalytics(configurator, (builder) => {
|
|
58
|
+
builder.setAdapter('mock', async () => recorder);
|
|
59
|
+
});
|
|
60
|
+
configurator.context.setCurrentContext(project);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const { result } = await renderAppHook(() => useTrackFeature(), { env, fusion });
|
|
64
|
+
result.current('button-click');
|
|
65
|
+
|
|
66
|
+
const [event] = recorder.getAnalytics('app-feature');
|
|
67
|
+
expect(event.attributes?.context).toMatchObject({ id: project.id, type: 'ProjectMaster' });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('reports to telemetry instead of throwing when no analytics provider is registered', async () => {
|
|
71
|
+
const fusion = await mockFramework<[AppModule]>((configurator) => {
|
|
72
|
+
enableAppManifestMock(configurator, env);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const { result } = await renderAppHook(() => useTrackFeature(), { env, fusion });
|
|
76
|
+
|
|
77
|
+
// the missing-analytics diagnostic is a telemetry concern, not an analytics one —
|
|
78
|
+
// it must not throw and must not fabricate an analytics event of its own
|
|
79
|
+
expect(() => result.current('button-click')).not.toThrow();
|
|
80
|
+
const telemetry = fusion.modules.telemetry.getAdapter('mock') as MockTelemetryAdapter;
|
|
81
|
+
expect(telemetry.getItems('AnalyticsProviderNotFound')).toHaveLength(1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG Grid community-tier sub-path entry-point.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Re-exports every public symbol from
|
|
6
|
+
* `@equinor/fusion-framework-react-ag-grid/community` so the application
|
|
7
|
+
* resolves a single shared copy of `ag-grid-community`.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
export * from '@equinor/fusion-framework-react-ag-grid/community';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG Grid enterprise-tier sub-path entry-point.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Re-exports every public symbol from
|
|
6
|
+
* `@equinor/fusion-framework-react-ag-grid/enterprise` so the application
|
|
7
|
+
* resolves a single shared copy of `ag-grid-enterprise`.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
export * from '@equinor/fusion-framework-react-ag-grid/enterprise';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG Grid React component sub-path entry-point.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Re-exports the AG Grid React bindings from
|
|
6
|
+
* `@equinor/fusion-framework-react-ag-grid/react`, including the
|
|
7
|
+
* `AgGridReact` component, `enableAgGrid`, and their associated types.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
export * from '@equinor/fusion-framework-react-ag-grid/react';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG Grid test helpers sub-path entry-point.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Re-exports the AG Grid test helpers from
|
|
6
|
+
* `@equinor/fusion-framework-module-ag-grid/testing`. Import from test setup
|
|
7
|
+
* files only — these helpers patch global state and are not meant for
|
|
8
|
+
* application runtime code.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Silences AG Grid Enterprise's unlicensed "License Key Not Found" banner on
|
|
15
|
+
* `console.error` so it doesn't bury real failures in test output.
|
|
16
|
+
*
|
|
17
|
+
* @returns A function that restores the original `console.error`.
|
|
18
|
+
*/
|
|
19
|
+
export { suppressAgGridLicenseBanner } from '@equinor/fusion-framework-module-ag-grid/testing';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AG Grid theme sub-path entry-point.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Re-exports the Fusion AG Grid theme utilities from
|
|
6
|
+
* `@equinor/fusion-framework-module-ag-grid/themes`, together with the
|
|
7
|
+
* application-scoped {@link useTheme} hook.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
fusionTheme,
|
|
13
|
+
createThemeFromTheme,
|
|
14
|
+
createTheme,
|
|
15
|
+
} from '@equinor/fusion-framework-module-ag-grid/themes';
|
|
16
|
+
export type { Theme } from '@equinor/fusion-framework-module-ag-grid/themes';
|
|
17
|
+
export { useTheme } from './useTheme';
|
package/src/create-component.tsx
CHANGED
|
@@ -41,7 +41,7 @@ export type ComponentRenderer<TFusion extends Fusion = Fusion, TEnv = AppEnv> =
|
|
|
41
41
|
* @example
|
|
42
42
|
* ```tsx
|
|
43
43
|
* const configCallback: AppConfigurator = (configurator) => {
|
|
44
|
-
* configurator.
|
|
44
|
+
* configurator.configureHttpClient(
|
|
45
45
|
* 'bar', {
|
|
46
46
|
* baseUri: 'https://somewhere-test.com',
|
|
47
47
|
* defaultScopes: ['foo/.default']
|
package/src/msal/useToken.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { AuthenticationResult } from '@equinor/fusion-framework-module-msal';
|
|
4
4
|
|
|
@@ -32,11 +32,22 @@ export const useToken = (req: {
|
|
|
32
32
|
const [token, setToken] = useState<AuthenticationResult | undefined>(undefined);
|
|
33
33
|
const [pending, setPending] = useState<boolean>(false);
|
|
34
34
|
const [error, setError] = useState<unknown>(null);
|
|
35
|
+
|
|
36
|
+
// `req` is typically a fresh object literal each render; key the effect on its
|
|
37
|
+
// scopes' content instead of identity, so it doesn't re-run (and reset `pending`
|
|
38
|
+
// to `true` forever) every time the caller re-renders. `JSON.stringify` (rather than
|
|
39
|
+
// `.join(',')`) keeps scopes with different array boundaries (e.g. `['a,b']` vs.
|
|
40
|
+
// `['a', 'b']`) from colliding on the same key.
|
|
41
|
+
const scopesKey = JSON.stringify(req.scopes);
|
|
42
|
+
const reqRef = useRef(req);
|
|
43
|
+
reqRef.current = req;
|
|
44
|
+
|
|
45
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: scopesKey is a re-run trigger, not read in the body
|
|
35
46
|
useEffect(() => {
|
|
36
47
|
setPending(true);
|
|
37
48
|
setToken(undefined);
|
|
38
49
|
msalProvider
|
|
39
|
-
.acquireToken({ request:
|
|
50
|
+
.acquireToken({ request: reqRef.current })
|
|
40
51
|
.then((result) => {
|
|
41
52
|
// Only update state when a token was actually acquired
|
|
42
53
|
if (result) {
|
|
@@ -45,7 +56,7 @@ export const useToken = (req: {
|
|
|
45
56
|
})
|
|
46
57
|
.catch(setError)
|
|
47
58
|
.finally(() => setPending(false));
|
|
48
|
-
}, [msalProvider,
|
|
59
|
+
}, [msalProvider, scopesKey]);
|
|
49
60
|
return { token, pending, error };
|
|
50
61
|
};
|
|
51
62
|
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '14.0
|
|
2
|
+
export const version = '14.1.0';
|
package/tsconfig.json
CHANGED
package/vitest.config.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { defineProject } from 'vitest/config';
|
|
1
|
+
import { defineProject } from '@equinor/fusion-framework-vitest-plugin-react-app/config';
|
|
2
2
|
|
|
3
|
-
import { name, version } from './package.json';
|
|
3
|
+
import { name, version } from './package.json' with { type: 'json' };
|
|
4
4
|
|
|
5
5
|
export default defineProject({
|
|
6
|
-
resolve: {
|
|
7
|
-
// @ts-expect-error -- tsconfigPaths is a Vite 8 option; vitest 4.x ships Vite 7 types
|
|
8
|
-
tsconfigPaths: true,
|
|
9
|
-
},
|
|
10
6
|
test: {
|
|
11
|
-
include: ['src/__tests__/**'],
|
|
12
7
|
name: `${name}@${version}`,
|
|
13
|
-
environment: 'happy-dom',
|
|
14
8
|
},
|
|
15
9
|
});
|