@equinor/fusion-framework-dev-portal 11.0.3 → 11.0.5

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 (40) hide show
  1. package/dist/main.js +14779 -14675
  2. package/package.json +48 -43
  3. package/CHANGELOG.md +0 -1070
  4. package/dev-server.ts +0 -109
  5. package/src/AppLoader.tsx +0 -135
  6. package/src/BookmarkSideSheet.tsx +0 -47
  7. package/src/ContextSelector/ContextSelector.tsx +0 -85
  8. package/src/ContextSelector/index.ts +0 -1
  9. package/src/ContextSelector/useContextResolver.ts +0 -337
  10. package/src/EquinorLoader.tsx +0 -33
  11. package/src/ErrorViewer.tsx +0 -26
  12. package/src/FusionLogo.tsx +0 -76
  13. package/src/Header.tsx +0 -94
  14. package/src/HeaderActions.tsx +0 -47
  15. package/src/PersonSideSheet/index.tsx +0 -65
  16. package/src/PersonSideSheet/sheets/FeatureSheetContent.tsx +0 -52
  17. package/src/PersonSideSheet/sheets/FeatureTogglerApp.tsx +0 -39
  18. package/src/PersonSideSheet/sheets/FeatureTogglerPortal.tsx +0 -33
  19. package/src/PersonSideSheet/sheets/LandingSheetContent.tsx +0 -64
  20. package/src/PersonSideSheet/sheets/index.ts +0 -3
  21. package/src/PersonSideSheet/sheets/roles/ClaimableRole.test.tsx +0 -83
  22. package/src/PersonSideSheet/sheets/roles/ClaimableRole.tsx +0 -229
  23. package/src/PersonSideSheet/sheets/roles/RolesApi.test.ts +0 -62
  24. package/src/PersonSideSheet/sheets/roles/RolesApi.ts +0 -112
  25. package/src/PersonSideSheet/sheets/roles/RolesSheetContent.test.tsx +0 -86
  26. package/src/PersonSideSheet/sheets/roles/RolesSheetContent.tsx +0 -207
  27. package/src/PersonSideSheet/sheets/roles/index.ts +0 -1
  28. package/src/PersonSideSheet/sheets/styled.tsx +0 -33
  29. package/src/PersonSideSheet/sheets/types.ts +0 -14
  30. package/src/Router.tsx +0 -83
  31. package/src/configure.ts +0 -186
  32. package/src/get-app-tag-from-url.ts +0 -17
  33. package/src/globals.d.ts +0 -32
  34. package/src/main.tsx +0 -43
  35. package/src/resources/svg.ts +0 -7
  36. package/src/version.ts +0 -2
  37. package/tsconfig.json +0 -79
  38. package/tsconfig.tsbuildinfo +0 -1
  39. package/vite.config.ts +0 -15
  40. package/vitest.config.ts +0 -12
package/dev-server.ts DELETED
@@ -1,109 +0,0 @@
1
- import dotenv from 'dotenv';
2
- import { createDevServer, processServices } from '@equinor/fusion-framework-dev-server';
3
-
4
- import { name } from './package.json' with { type: 'json' };
5
-
6
- dotenv.config();
7
-
8
- const clientId = process.env.FUSION_MSAL_CLIENT_ID;
9
- const tenantId = process.env.FUSION_MSAL_TENANT_ID;
10
- const serviceDiscoveryUrl = process.env.FUSION_SERVICE_DISCOVERY_URL;
11
- const serviceScopes = JSON.parse(process.env.FUSION_SERVICE_SCOPE || '[]') as string[];
12
-
13
- // Fail fast when any required MSAL/service-discovery env var is missing
14
- if (!clientId) {
15
- throw new Error('FUSION_MSAL_CLIENT_ID is not set');
16
- }
17
- // tenantId is equally required for MSAL configuration
18
- if (!tenantId) {
19
- throw new Error('FUSION_MSAL_TENANT_ID is not set');
20
- }
21
- // Service discovery URL is required to resolve backend service URIs
22
- if (!serviceDiscoveryUrl) {
23
- throw new Error('SERVICE_DISCOVERY_URL is not set');
24
- }
25
- // At least one service scope must be configured for auth requests
26
- if (serviceScopes.length === 0) {
27
- throw new Error('SERVICE_SCOPES is not set');
28
- }
29
-
30
- (async () => {
31
- const devServer = await createDevServer({
32
- spa: {
33
- templateEnv: {
34
- portal: {
35
- id: name,
36
- },
37
- title: 'Fusion SPA',
38
- serviceDiscovery: {
39
- url: '/@fusion-services',
40
- scopes: serviceScopes,
41
- },
42
- msal: {
43
- clientId,
44
- tenantId,
45
- redirectUri: '/authentication/login-callback',
46
- requiresAuth: 'true',
47
- },
48
- serviceWorker: {
49
- resources: [
50
- {
51
- url: '/apps-proxy',
52
- rewrite: '/@fusion-api/apps',
53
- scopes: serviceScopes,
54
- },
55
- {
56
- url: '/@fusion-api/apps',
57
- scopes: serviceScopes,
58
- },
59
- {
60
- url: '/help-proxy',
61
- rewrite: '/@fusion-api/help',
62
- scopes: serviceScopes,
63
- },
64
- {
65
- url: '/portal-proxy',
66
- rewrite: '/@fusion-api/portals',
67
- scopes: serviceScopes,
68
- },
69
- ],
70
- },
71
- },
72
- },
73
- api: {
74
- serviceDiscoveryUrl,
75
- processServices: (dataResponse, route) => {
76
- const { data, routes } = processServices(dataResponse, route);
77
- return {
78
- data: data.concat({
79
- key: 'portals',
80
- name: 'Portal Service - MOCK',
81
- uri: '/@fusion-api/portals',
82
- }),
83
- routes,
84
- };
85
- },
86
- routes: [
87
- {
88
- match: `/portals/portals/${name}{@:tag}`,
89
- middleware: async (req, res) => {
90
- const url = new URL('/src/main.tsx', req.headers.referer);
91
- res.writeHead(200, {
92
- 'content-type': 'application/json',
93
- });
94
- res.end(
95
- JSON.stringify({
96
- build: {
97
- config: {},
98
- entrypoint: url,
99
- },
100
- }),
101
- );
102
- },
103
- },
104
- ],
105
- },
106
- });
107
- await devServer.listen();
108
- devServer.printUrls();
109
- })();
package/src/AppLoader.tsx DELETED
@@ -1,135 +0,0 @@
1
- import { useEffect, useMemo, useRef, useState } from 'react';
2
-
3
- import { Subscription } from 'rxjs';
4
- import { last } from 'rxjs/operators';
5
-
6
- import { useFramework } from '@equinor/fusion-framework-react';
7
-
8
- import { useObservableState } from '@equinor/fusion-observable/react';
9
-
10
- import { AppManifestError } from '@equinor/fusion-framework-module-app/errors.js';
11
-
12
- import { ErrorViewer } from './ErrorViewer';
13
- import type { AppModule } from '@equinor/fusion-framework-module-app';
14
- import EquinorLoader from './EquinorLoader';
15
- import { getAppTagFromUrl } from './get-app-tag-from-url';
16
-
17
- /**
18
- * Loads, initializes, and mounts a Fusion application by its key.
19
- *
20
- * Sets the current app on the framework's app module, observes initialization
21
- * progress, and renders the app's script output into a private DOM element.
22
- * Displays a loading spinner while the app initializes and an error view if
23
- * manifest resolution or initialization fails.
24
- *
25
- * @param props.appKey - Unique key identifying the Fusion application to load.
26
- */
27
- export const AppLoader = (props: { readonly appKey: string }) => {
28
- const { appKey } = props;
29
- const fusion = useFramework<[AppModule]>();
30
-
31
- /** reference of application section/container */
32
- const ref = useRef<HTMLElement>(null);
33
-
34
- const [loading, setLoading] = useState<boolean>(false);
35
- const [error, setError] = useState<Error | undefined>();
36
-
37
- // TODO(#5087): change to `useCurrentApp`
38
- /** observe and use the current selected application from framework */
39
- const { value: currentApp } = useObservableState(
40
- useMemo(() => fusion.modules.app.current$, [fusion.modules.app]),
41
- );
42
-
43
- useEffect(() => {
44
- const tag = getAppTagFromUrl();
45
-
46
- // A `$tag` URL param takes precedence over the plain appKey
47
- if (tag) {
48
- fusion.modules.app.setCurrentApp({ appKey, tag });
49
- return;
50
- }
51
- /** when appKey property change, assign it to current */
52
- fusion.modules.app.setCurrentApp(appKey);
53
- }, [appKey, fusion]);
54
-
55
- useEffect(() => {
56
- /** flag that application is loading */
57
- setLoading(true);
58
-
59
- /** clear previous errors */
60
- setError(undefined);
61
-
62
- /** create a teardown of load */
63
- const subscription = new Subscription();
64
-
65
- /** make sure that initialize is canceled and disposed if current app changes */
66
- subscription.add(
67
- currentApp
68
- ?.initialize()
69
- .pipe(last())
70
- .subscribe({
71
- next: ({ manifest, script, config }) => {
72
- /** generate basename for application */
73
- const [basename] = window.location.pathname.match(/\/?apps\/[a-z|-]+(\/)?/g) ?? [''];
74
-
75
- /** create a 'private' element for the application */
76
- const el = document.createElement('div');
77
- // Guard against rendering before the mounting ref has attached
78
- if (!ref.current) {
79
- throw Error('Missing application mounting point');
80
- }
81
-
82
- ref.current.appendChild(el);
83
-
84
- /** extract render callback function from javascript module */
85
- const render = script.renderApp ?? script.default;
86
-
87
- /** add application teardown to current render effect teardown */
88
- subscription.add(render(el, { fusion, env: { basename, config, manifest } }));
89
-
90
- /** remove app element when application unmounts */
91
- subscription.add(() => el.remove());
92
- },
93
- complete: () => {
94
- /** flag that application is no longer loading */
95
- setLoading(false);
96
- },
97
- error: (err) => {
98
- /** set error if initialization of application fails */
99
- setError(err);
100
- },
101
- }),
102
- );
103
-
104
- /** teardown application when hook unmounts */
105
- return () => subscription.unsubscribe();
106
- }, [fusion, currentApp]);
107
-
108
- // Render an error view when initialization or manifest resolution failed
109
- if (error) {
110
- // Render a dedicated view for manifest-resolution failures
111
- if (error.cause instanceof AppManifestError) {
112
- return (
113
- <div>
114
- <h2>🔥 Failed to load application manifest 🤬</h2>
115
- <h3>{error.cause.type}</h3>
116
- <ErrorViewer error={error} />
117
- </div>
118
- );
119
- }
120
- return (
121
- <div>
122
- <h2>🔥 Failed to load application 🤬</h2>
123
- <ErrorViewer error={error} />
124
- </div>
125
- );
126
- }
127
-
128
- return (
129
- <section id="app-section" ref={ref} style={{ display: 'contents' }}>
130
- {loading && <EquinorLoader text="Loading Application" />}
131
- </section>
132
- );
133
- };
134
-
135
- export default AppLoader;
@@ -1,47 +0,0 @@
1
- import { Button, Icon } from '@equinor/eds-core-react';
2
- import { Bookmark } from '@equinor/fusion-framework-react-components-bookmark';
3
- import { useBookmarkComponentContext } from '@equinor/fusion-framework-react-components-bookmark';
4
- import { SideSheet } from '@equinor/fusion-react-side-sheet';
5
-
6
- /** Props for the {@link BookmarkSideSheet} component. */
7
- type BookmarkSideSheetProps = {
8
- /** Whether the side sheet is currently visible. */
9
- readonly isOpen: boolean;
10
- /** Callback invoked when the user dismisses the side sheet. */
11
- readonly onClose: VoidFunction;
12
- };
13
-
14
- /**
15
- * Side sheet overlay for browsing and creating application bookmarks.
16
- *
17
- * Wraps the `Bookmark` component from `@equinor/fusion-framework-react-components-bookmark`
18
- * in a dismissible side sheet with an "Add Bookmark" action button.
19
- *
20
- * @param props - {@link BookmarkSideSheetProps}
21
- */
22
- export const BookmarkSideSheet = ({ isOpen, onClose }: BookmarkSideSheetProps) => {
23
- const { provider, showCreateBookmark } = useBookmarkComponentContext();
24
-
25
- return (
26
- <SideSheet isOpen={isOpen} onClose={onClose} isDismissable={true} enableFullscreen={true}>
27
- <SideSheet.Indicator color={'#258800'} />
28
- <SideSheet.Title title="Bookmarks" />
29
- <SideSheet.SubTitle subTitle={'Application bookmarks'} />
30
- <SideSheet.Actions>
31
- <Button
32
- disabled={!provider?.canCreateBookmarks}
33
- variant="ghost"
34
- onClick={() => {
35
- showCreateBookmark();
36
- onClose();
37
- }}
38
- >
39
- <Icon name="add" /> Add Bookmark
40
- </Button>
41
- </SideSheet.Actions>
42
- <SideSheet.Content>
43
- <Bookmark />
44
- </SideSheet.Content>
45
- </SideSheet>
46
- );
47
- };
@@ -1,85 +0,0 @@
1
- import { useCallback, useEffect, useId, useMemo, type ReactElement } from 'react';
2
- import {
3
- ContextProvider,
4
- ContextSearch,
5
- type ContextSearchProps,
6
- type ContextSelectEvent,
7
- ContextClearEvent,
8
- } from '@equinor/fusion-react-context-selector';
9
- import { useContextResolver } from './useContextResolver';
10
-
11
- /**
12
- * Context selector component wired to the current application's context module.
13
- *
14
- * Renders a search input with dropdown results from the Fusion context service.
15
- * When the user selects a context item, it is set as the current context on the
16
- * application's context provider. Clearing the selector resets the current context.
17
- *
18
- * @see {@link https://equinor.github.io/fusion-react-components/?path=/docs/data-contextselector--component | ContextSelector Storybook}
19
- * @param props - Passthrough props for the underlying `ContextSearch` component.
20
- * @returns The context selector element, or `null` if no context resolver is available.
21
- */
22
- export const ContextSelector = (props: ContextSearchProps): ReactElement | null => {
23
- const contextSelectorId = useId();
24
- const {
25
- resolver,
26
- provider,
27
- currentContext: [selectedContextItem],
28
- } = useContextResolver();
29
-
30
- /** callback handler for context selector, when context is changed or cleared */
31
- const onContextSelect = useCallback(
32
- (e: Event | ContextSelectEvent) => {
33
- // Only react to selection/clear events when a provider is available to update
34
- if (provider) {
35
- // 'select' carries a chosen context item; anything else means the selector was cleared
36
- if (e.type === 'select') {
37
- // The 'select' event's `Event` type doesn't reflect the CustomEvent detail payload
38
- // dispatched by the underlying context-selector element, so a cast is required here.
39
- const ev = e as unknown as ContextSelectEvent;
40
- // Only set a new context when the user actually chose an item
41
- if (ev.nativeEvent.detail.selected.length) {
42
- provider.contextClient.setCurrentContext(ev.nativeEvent.detail.selected[0].id);
43
- }
44
- } else {
45
- provider.clearCurrentContext();
46
- }
47
- }
48
- },
49
- [provider],
50
- );
51
-
52
- /**
53
- * Clears context when ctx has been cleared outside the selector.
54
- */
55
- const clearEvent = useMemo(() => new ContextClearEvent({ date: Date.now() }), []);
56
- useEffect(() => {
57
- // Notify listeners only when the context was cleared outside this selector
58
- if (!selectedContextItem) {
59
- document.dispatchEvent(clearEvent);
60
- }
61
- }, [clearEvent, selectedContextItem]);
62
-
63
- // Nothing to render until a context resolver has been resolved
64
- if (!resolver) return null;
65
-
66
- return (
67
- <div style={{ flex: 1, maxWidth: '480px' }}>
68
- <ContextProvider resolver={resolver}>
69
- <ContextSearch
70
- id={contextSelectorId}
71
- placeholder={props.placeholder ?? 'Search for context'}
72
- initialText={props.initialText ?? 'Start typing to search'}
73
- dropdownHeight={props.dropdownHeight ?? '300px'}
74
- variant={props.variant ?? 'header'}
75
- onSelect={(e: ContextSelectEvent) => onContextSelect(e)}
76
- selectTextOnFocus={true}
77
- previewItem={selectedContextItem}
78
- onClearContext={onContextSelect}
79
- />
80
- </ContextProvider>
81
- </div>
82
- );
83
- };
84
-
85
- export default ContextSelector;
@@ -1 +0,0 @@
1
- export { ContextSelector } from './ContextSelector';