@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +9 -2
  3. package/dist/esm/ag-grid/community.js +12 -0
  4. package/dist/esm/ag-grid/community.js.map +1 -0
  5. package/dist/esm/ag-grid/enterprise.js +12 -0
  6. package/dist/esm/ag-grid/enterprise.js.map +1 -0
  7. package/dist/esm/ag-grid/react.js +12 -0
  8. package/dist/esm/ag-grid/react.js.map +1 -0
  9. package/dist/esm/ag-grid/testing.js +19 -0
  10. package/dist/esm/ag-grid/testing.js.map +1 -0
  11. package/dist/esm/ag-grid/theme.js +13 -0
  12. package/dist/esm/ag-grid/theme.js.map +1 -0
  13. package/dist/esm/create-component.js +1 -1
  14. package/dist/esm/msal/useToken.js +12 -3
  15. package/dist/esm/msal/useToken.js.map +1 -1
  16. package/dist/esm/version.js +1 -1
  17. package/dist/tsconfig.tsbuildinfo +1 -1
  18. package/dist/types/ag-grid/community.d.ts +11 -0
  19. package/dist/types/ag-grid/enterprise.d.ts +11 -0
  20. package/dist/types/ag-grid/react.d.ts +11 -0
  21. package/dist/types/ag-grid/testing.d.ts +18 -0
  22. package/dist/types/ag-grid/theme.d.ts +13 -0
  23. package/dist/types/create-component.d.ts +1 -1
  24. package/dist/types/version.d.ts +1 -1
  25. package/docs/bookmark.md +7 -0
  26. package/docs/context.md +3 -1
  27. package/docs/framework.md +2 -2
  28. package/docs/msal.md +3 -3
  29. package/package.json +77 -25
  30. package/src/__tests__/Apploader.test.tsx +51 -0
  31. package/src/__tests__/fixtures/apploader-child-script.ts +9 -0
  32. package/src/__tests__/testApp.test.tsx +76 -0
  33. package/src/__tests__/useAccessToken.test.tsx +51 -0
  34. package/src/__tests__/useAppSetting.test.tsx +133 -0
  35. package/src/__tests__/useAppSettings.test.tsx +147 -0
  36. package/src/__tests__/useCurrentAccount.test.tsx +32 -0
  37. package/src/__tests__/useCurrentBookmark.test.tsx +108 -0
  38. package/src/__tests__/useCurrentContext.test.tsx +72 -0
  39. package/src/__tests__/useFeature.test.tsx +104 -0
  40. package/src/__tests__/useHelpCenter.test.tsx +64 -0
  41. package/src/__tests__/useStateSyncEvents.test.ts +12 -11
  42. package/src/__tests__/useToken.test.tsx +71 -0
  43. package/src/__tests__/useTrackFeature.test.tsx +83 -0
  44. package/src/ag-grid/community.ts +11 -0
  45. package/src/ag-grid/enterprise.ts +11 -0
  46. package/src/ag-grid/react.ts +11 -0
  47. package/src/ag-grid/testing.ts +19 -0
  48. package/src/ag-grid/theme.ts +17 -0
  49. package/src/create-component.tsx +1 -1
  50. package/src/msal/useToken.ts +14 -3
  51. package/src/version.ts +1 -1
  52. package/tsconfig.json +6 -0
  53. 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';
@@ -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.http.configureClient(
44
+ * configurator.configureHttpClient(
45
45
  * 'bar', {
46
46
  * baseUri: 'https://somewhere-test.com',
47
47
  * defaultScopes: ['foo/.default']
@@ -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: req })
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, req]);
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.3';
2
+ export const version = '14.1.0';
package/tsconfig.json CHANGED
@@ -12,12 +12,18 @@
12
12
  {
13
13
  "path": "../../app"
14
14
  },
15
+ {
16
+ "path": "../../framework"
17
+ },
15
18
  {
16
19
  "path": "../framework"
17
20
  },
18
21
  {
19
22
  "path": "../router"
20
23
  },
24
+ {
25
+ "path": "../ag-grid"
26
+ },
21
27
  {
22
28
  "path": "../../modules/analytics"
23
29
  },
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
  });