@gooddata/sdk-ui-pluggable-host 11.59.0-alpha.0 → 11.59.0-alpha.1
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/esm/loader/routing.d.ts +0 -7
- package/esm/loader/routing.d.ts.map +1 -1
- package/esm/loader/routing.js +2 -35
- package/esm/platformContext/useLoadPlatformContext.d.ts.map +1 -1
- package/esm/platformContext/useLoadPlatformContext.js +21 -12
- package/esm/registry/pluggableApplicationsRegistry.d.ts +26 -2
- package/esm/registry/pluggableApplicationsRegistry.d.ts.map +1 -1
- package/esm/registry/pluggableApplicationsRegistry.js +32 -32
- package/esm/ui/HostChrome.d.ts.map +1 -1
- package/esm/ui/HostChrome.js +5 -2
- package/esm/ui/appMenuItems.d.ts +0 -1
- package/esm/ui/appMenuItems.d.ts.map +1 -1
- package/esm/ui/appMenuItems.js +2 -11
- package/package.json +17 -17
package/esm/loader/routing.d.ts
CHANGED
|
@@ -12,13 +12,6 @@ export declare function getApplicationScopeFromPath(path: string): ApplicationSc
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function getWorkspaceIdFromPath(pathname: string | undefined): string | undefined;
|
|
14
14
|
export declare function normalizePath(path: string): string;
|
|
15
|
-
/**
|
|
16
|
-
* Substitutes `{workspaceId}` placeholders in an external app URL with the
|
|
17
|
-
* currently resolved workspace id. URLs with no placeholder pass through
|
|
18
|
-
* unchanged, so apps that don't care about workspace context can keep using
|
|
19
|
-
* a plain literal URL (e.g. `https://docs.example.com`).
|
|
20
|
-
*/
|
|
21
|
-
export declare function substituteExternalUrlPlaceholders(url: string, ctx: IPlatformContext, pathname?: string): string;
|
|
22
15
|
export declare function getApplicationHref(app: PluggableApplicationRegistryItem, ctx: IPlatformContext, pathname?: string): string;
|
|
23
16
|
export declare function isInternalAppRouteActive(app: PluggableApplicationRegistryItem, ctx: IPlatformContext, pathname: string): boolean;
|
|
24
17
|
export declare function getActiveInternalApplication(apps: PluggableApplicationRegistryItem[], ctx: IPlatformContext, pathname: string): PluggableApplicationRegistryItem | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/loader/routing.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/loader/routing.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,gCAAgC,EAGxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAIlF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAYtF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEvF;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAUD,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,gCAAgC,EACrC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,CAER;AAED,wBAAgB,wBAAwB,CACpC,GAAG,EAAE,gCAAgC,EACrC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAQT;AAED,wBAAgB,4BAA4B,CACxC,IAAI,EAAE,gCAAgC,EAAE,EACxC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,GACjB,gCAAgC,GAAG,SAAS,CAK9C"}
|
package/esm/loader/routing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
|
-
import {
|
|
2
|
+
import { getPluggableApplicationHref, isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
|
|
3
3
|
const WORKSPACE_PATH_PATTERN = /^\/workspace\/(?<workspaceId>[^/]+)(?:\/|$)/;
|
|
4
4
|
export function stripEmbedPrefix(pathname) {
|
|
5
5
|
return pathname.startsWith("/embedded/") ? pathname.slice("/embedded".length) : pathname;
|
|
@@ -42,41 +42,8 @@ function resolveWorkspaceId(ctx, pathname) {
|
|
|
42
42
|
}
|
|
43
43
|
return getWorkspaceIdFromPath(pathname);
|
|
44
44
|
}
|
|
45
|
-
function composeInternalAppPath(app, routeBase, ctx, pathname) {
|
|
46
|
-
const scopeBase = app.applicationScope === "organization"
|
|
47
|
-
? "/organization"
|
|
48
|
-
: app.applicationScope === "workspace"
|
|
49
|
-
? `/workspace/${resolveWorkspaceId(ctx, pathname) ?? ""}`
|
|
50
|
-
: undefined;
|
|
51
|
-
if (!scopeBase) {
|
|
52
|
-
throw new Error(`[host-runtime/routing] Unsupported application scope "${app.applicationScope}" for app "${app.id}".`);
|
|
53
|
-
}
|
|
54
|
-
return normalizePath(`${normalizePath(scopeBase)}${ensureLeadingSlash(routeBase)}`);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Substitutes `{workspaceId}` placeholders in an external app URL with the
|
|
58
|
-
* currently resolved workspace id. URLs with no placeholder pass through
|
|
59
|
-
* unchanged, so apps that don't care about workspace context can keep using
|
|
60
|
-
* a plain literal URL (e.g. `https://docs.example.com`).
|
|
61
|
-
*/
|
|
62
|
-
export function substituteExternalUrlPlaceholders(url, ctx, pathname) {
|
|
63
|
-
if (!url.includes("{workspaceId}")) {
|
|
64
|
-
return url;
|
|
65
|
-
}
|
|
66
|
-
const workspaceId = resolveWorkspaceId(ctx, pathname) ?? "";
|
|
67
|
-
return url.replaceAll("{workspaceId}", workspaceId);
|
|
68
|
-
}
|
|
69
45
|
export function getApplicationHref(app, ctx, pathname) {
|
|
70
|
-
|
|
71
|
-
return substituteExternalUrlPlaceholders(app.external.url, ctx, pathname);
|
|
72
|
-
}
|
|
73
|
-
if (isLocalPluggableApplicationRegistryItem(app)) {
|
|
74
|
-
return composeInternalAppPath(app, app.local.routeBase, ctx, pathname);
|
|
75
|
-
}
|
|
76
|
-
if (isRemotePluggableApplicationRegistryItem(app)) {
|
|
77
|
-
return composeInternalAppPath(app, app.remote.routeBase, ctx, pathname);
|
|
78
|
-
}
|
|
79
|
-
return "#";
|
|
46
|
+
return getPluggableApplicationHref(app, resolveWorkspaceId(ctx, pathname));
|
|
80
47
|
}
|
|
81
48
|
export function isInternalAppRouteActive(app, ctx, pathname) {
|
|
82
49
|
if (isExternalPluggableApplicationRegistryItem(app)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLoadPlatformContext.d.ts","sourceRoot":"","sources":["../../src/platformContext/useLoadPlatformContext.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"useLoadPlatformContext.d.ts","sourceRoot":"","sources":["../../src/platformContext/useLoadPlatformContext.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAO3G,OAAO,EACH,KAAK,6BAA6B,EAGrC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACH,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAElC,MAAM,YAAY,CAAC;AAYpB;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,0BAA0B,CAAC,gBAAgB,CAAC,CA6JrF;AAED;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC5C,YAAY,CAAC,SAAS,EAAE,6BAA6B,GAAG,IAAI,CAAC;IAC7D,SAAS,EAAE,MAAM,QAAQ,CAAC,0BAA0B,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAC/E,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IAChD,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAmED;;GAEG;AACH,eAAO,MAAM,8BAA8B,EAAE,+BACA,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,6BAA6B,GAAG,IAAI,CAE/F"}
|
|
@@ -4,6 +4,7 @@ import { useLocation } from "react-router";
|
|
|
4
4
|
import { isLocale } from "@gooddata/sdk-model";
|
|
5
5
|
import { isProduction } from "../lib/isProduction.js";
|
|
6
6
|
import { getApplicationScopeFromPath, getWorkspaceIdFromPath } from "../loader/routing.js";
|
|
7
|
+
import { resolveAvailableWorkspaceApplications } from "../registry/pluggableApplicationsRegistry.js";
|
|
7
8
|
import { getBackend } from "./backend.js";
|
|
8
9
|
import { HostApplicationDisabledError, loadPlatformContext, } from "./loadPlatformContext.js";
|
|
9
10
|
import { useWorkspaceColorPalette } from "./useWorkspaceColorPalette.js";
|
|
@@ -74,7 +75,11 @@ export function useLoadPlatformContext() {
|
|
|
74
75
|
settings,
|
|
75
76
|
preferredLocale,
|
|
76
77
|
};
|
|
77
|
-
|
|
78
|
+
const ctx = { ...backendContext.ctx, ...routeCtx, theme: effectiveTheme };
|
|
79
|
+
return {
|
|
80
|
+
state: "ready",
|
|
81
|
+
ctx: { ...ctx, availableApplications: resolveAvailableWorkspaceApplications(ctx) },
|
|
82
|
+
};
|
|
78
83
|
}, [
|
|
79
84
|
backendContext,
|
|
80
85
|
applicationScope,
|
|
@@ -110,19 +115,23 @@ export function useLoadPlatformContext() {
|
|
|
110
115
|
return undefined;
|
|
111
116
|
}
|
|
112
117
|
const { currentApplicationScope, currentWorkspaceId, workspacePermissions, workspaceSettings, colorPalette, settings, preferredLocale, theme, } = lastReady.result.ctx;
|
|
118
|
+
// Resolved again rather than carried over: the applications are derived from the CURRENT
|
|
119
|
+
// backend context (registry, entitlements, organization permissions), which a refresh may
|
|
120
|
+
// have changed even though the workspace-scoped values below are still the previous ones.
|
|
121
|
+
const ctx = {
|
|
122
|
+
...backendContext.ctx,
|
|
123
|
+
currentApplicationScope,
|
|
124
|
+
currentWorkspaceId,
|
|
125
|
+
workspacePermissions,
|
|
126
|
+
workspaceSettings,
|
|
127
|
+
colorPalette,
|
|
128
|
+
settings,
|
|
129
|
+
preferredLocale,
|
|
130
|
+
theme,
|
|
131
|
+
};
|
|
113
132
|
return {
|
|
114
133
|
state: "ready",
|
|
115
|
-
ctx: {
|
|
116
|
-
...backendContext.ctx,
|
|
117
|
-
currentApplicationScope,
|
|
118
|
-
currentWorkspaceId,
|
|
119
|
-
workspacePermissions,
|
|
120
|
-
workspaceSettings,
|
|
121
|
-
colorPalette,
|
|
122
|
-
settings,
|
|
123
|
-
preferredLocale,
|
|
124
|
-
theme,
|
|
125
|
-
},
|
|
134
|
+
ctx: { ...ctx, availableApplications: resolveAvailableWorkspaceApplications(ctx) },
|
|
126
135
|
};
|
|
127
136
|
}, [backendContext, lastReady, workspaceId]);
|
|
128
137
|
// Keep the previously rendered context while the same workspace's data refreshes (backend
|
|
@@ -28,6 +28,16 @@ interface IResolveApplicationsOptions {
|
|
|
28
28
|
* Application scope to filter by; if undefined, no apps are returned
|
|
29
29
|
*/
|
|
30
30
|
scope: ApplicationScope | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Leaves `requiredWorkspacePermissions` unevaluated, for callers that decide per workspace
|
|
33
|
+
* rather than for the workspace in `ctx`.
|
|
34
|
+
*/
|
|
35
|
+
skipWorkspacePermissions?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Leaves `requiredSettings` unevaluated, for callers that decide against another workspace's
|
|
38
|
+
* settings rather than those of the active scope.
|
|
39
|
+
*/
|
|
40
|
+
skipSettings?: boolean;
|
|
31
41
|
}
|
|
32
42
|
/**
|
|
33
43
|
* Resolves the final list of pluggable applications from the local and remote registries.
|
|
@@ -39,7 +49,7 @@ interface IResolveApplicationsOptions {
|
|
|
39
49
|
* 4. Apply overrides from the remote registry to the merged list
|
|
40
50
|
* 5. Filter out disabled applications (isEnabled: false)
|
|
41
51
|
* 6. Filter by application scope - keep only apps whose applicationScope matches scope; if scope is undefined, no apps pass through
|
|
42
|
-
* 7. Filter by requirements - check requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, and requiredEntitlements (embedded and export modes force the shell application flags on)
|
|
52
|
+
* 7. Filter by requirements - check requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, and requiredEntitlements (embedded and export modes force the shell application flags on; `skipWorkspacePermissions` leaves requiredWorkspacePermissions to the caller)
|
|
43
53
|
* 8. Sort by menuOrder (ascending)
|
|
44
54
|
*
|
|
45
55
|
* @param options - Resolution options; see {@link IResolveApplicationsOptions}
|
|
@@ -47,7 +57,21 @@ interface IResolveApplicationsOptions {
|
|
|
47
57
|
*
|
|
48
58
|
* @internal - exported for testing
|
|
49
59
|
*/
|
|
50
|
-
export declare function resolveApplications({ localApps, remoteRegistry, ctx, scope, }: IResolveApplicationsOptions): PluggableApplicationRegistryItem[];
|
|
60
|
+
export declare function resolveApplications({ localApps, remoteRegistry, ctx, scope, skipWorkspacePermissions, skipSettings, }: IResolveApplicationsOptions): PluggableApplicationRegistryItem[];
|
|
61
|
+
/**
|
|
62
|
+
* Resolves the workspace-scoped applications published to pluggable applications through
|
|
63
|
+
* {@link @gooddata/sdk-pluggable-application-model#IPlatformContextV1.availableApplications}.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* Applies every requirement that holds organization-wide and leaves the two that a single workspace
|
|
67
|
+
* answers — `requiredSettings` and `requiredWorkspacePermissions` — to the consumer, which evaluates
|
|
68
|
+
* them against each workspace it lists. A feature flag can be turned on for one workspace alone (early
|
|
69
|
+
* access values resolve into its settings), so deciding here would hide such an application from every
|
|
70
|
+
* workspace that has it.
|
|
71
|
+
*
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveAvailableWorkspaceApplications(ctx: IPlatformContext): PluggableApplicationRegistryItem[];
|
|
51
75
|
/**
|
|
52
76
|
* Builds the resolved list of pluggable applications from the local and remote registries.
|
|
53
77
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pluggableApplicationsRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/pluggableApplicationsRegistry.tsx"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"pluggableApplicationsRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/pluggableApplicationsRegistry.tsx"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,gBAAgB,EAErB,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,mCAAmC,EAG3C,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEH,KAAK,gBAAgB,EACxB,MAAM,2CAA2C,CAAC;AAMnD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,kCAAkC,GAAG,IAAI,CAE5F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,mCAAmC,GAAG,SAAS,CAaxG;AAsMD,UAAU,2BAA2B;IACjC;;OAEG;IACH,SAAS,EAAE,gCAAgC,EAAE,CAAC;IAC9C;;OAEG;IACH,cAAc,EAAE,mCAAmC,GAAG,SAAS,CAAC;IAChE;;OAEG;IACH,GAAG,EAAE,gBAAgB,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,EAChC,SAAS,EACT,cAAc,EACd,GAAG,EACH,KAAK,EACL,wBAAgC,EAChC,YAAoB,GACvB,EAAE,2BAA2B,GAAG,gCAAgC,EAAE,CAiBlE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qCAAqC,CACjD,GAAG,EAAE,gBAAgB,GACtB,gCAAgC,EAAE,CASpC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,GAAG,gCAAgC,EAAE,CAWlG"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { toPluggableApplicationWorkspacePermissions, } from "@gooddata/sdk-model";
|
|
3
|
+
import { evaluateCondition, toPluggableApplicationWorkspacePermissions, } from "@gooddata/sdk-model";
|
|
4
4
|
const SUPPORTED_API_VERSION = "1.0";
|
|
5
5
|
let registeredLocalApplications;
|
|
6
6
|
/**
|
|
@@ -86,30 +86,6 @@ function filterDisabled(apps) {
|
|
|
86
86
|
// isEnabled is optional but "true" is the default value
|
|
87
87
|
return apps.filter((app) => app.isEnabled === undefined || app.isEnabled);
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Evaluates a Condition<T> against an actual value object.
|
|
91
|
-
*
|
|
92
|
-
* Supports plain object (implicit AND), $or, and $and forms.
|
|
93
|
-
*/
|
|
94
|
-
function evaluateCondition(condition, actual) {
|
|
95
|
-
if (typeof condition === "object" && condition !== null && "$or" in condition) {
|
|
96
|
-
return condition.$or.some((c) => evaluateCondition(c, actual));
|
|
97
|
-
}
|
|
98
|
-
if (typeof condition === "object" && condition !== null && "$and" in condition) {
|
|
99
|
-
return condition.$and.every((c) => evaluateCondition(c, actual));
|
|
100
|
-
}
|
|
101
|
-
if (actual === undefined) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
const plain = condition;
|
|
105
|
-
const actualRecord = actual;
|
|
106
|
-
return Object.keys(plain).every((key) => {
|
|
107
|
-
const required = plain[key];
|
|
108
|
-
const actual = actualRecord[key];
|
|
109
|
-
// A missing setting is treated as falsy: undefined satisfies a requirement of false.
|
|
110
|
-
return actual === required || (actual === undefined && required === false);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
89
|
function entitlementsToRecord(entitlements) {
|
|
114
90
|
return Object.fromEntries((entitlements ?? []).map((e) => [e.name, e.value === undefined ? true : e.value]));
|
|
115
91
|
}
|
|
@@ -133,12 +109,13 @@ function settingsForRequirements(ctx) {
|
|
|
133
109
|
enableShellApplication_analyticalDesigner: true,
|
|
134
110
|
};
|
|
135
111
|
}
|
|
136
|
-
function appMeetsRequirements({ requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, requiredEntitlements, }, ctx) {
|
|
112
|
+
function appMeetsRequirements({ requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, requiredEntitlements, }, ctx, skipWorkspacePermissions, skipSettings) {
|
|
137
113
|
if (requiredSettings !== undefined &&
|
|
114
|
+
!skipSettings &&
|
|
138
115
|
!evaluateCondition(requiredSettings, settingsForRequirements(ctx))) {
|
|
139
116
|
return false;
|
|
140
117
|
}
|
|
141
|
-
if (requiredWorkspacePermissions !== undefined) {
|
|
118
|
+
if (requiredWorkspacePermissions !== undefined && !skipWorkspacePermissions) {
|
|
142
119
|
const wsPerms = ctx.workspacePermissions
|
|
143
120
|
? toPluggableApplicationWorkspacePermissions(ctx.workspacePermissions)
|
|
144
121
|
: undefined;
|
|
@@ -158,8 +135,8 @@ function appMeetsRequirements({ requiredSettings, requiredWorkspacePermissions,
|
|
|
158
135
|
}
|
|
159
136
|
return true;
|
|
160
137
|
}
|
|
161
|
-
function filterByRequirements(apps, ctx) {
|
|
162
|
-
return apps.filter((app) => appMeetsRequirements(app, ctx));
|
|
138
|
+
function filterByRequirements(apps, ctx, skipWorkspacePermissions, skipSettings) {
|
|
139
|
+
return apps.filter((app) => appMeetsRequirements(app, ctx, skipWorkspacePermissions, skipSettings));
|
|
163
140
|
}
|
|
164
141
|
function filterByScope(apps, scope) {
|
|
165
142
|
if (scope === undefined) {
|
|
@@ -191,7 +168,7 @@ function filterLocalByBaseUiAccess(localApps, ctx) {
|
|
|
191
168
|
* 4. Apply overrides from the remote registry to the merged list
|
|
192
169
|
* 5. Filter out disabled applications (isEnabled: false)
|
|
193
170
|
* 6. Filter by application scope - keep only apps whose applicationScope matches scope; if scope is undefined, no apps pass through
|
|
194
|
-
* 7. Filter by requirements - check requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, and requiredEntitlements (embedded and export modes force the shell application flags on)
|
|
171
|
+
* 7. Filter by requirements - check requiredSettings, requiredWorkspacePermissions, requiredOrganizationPermissions, and requiredEntitlements (embedded and export modes force the shell application flags on; `skipWorkspacePermissions` leaves requiredWorkspacePermissions to the caller)
|
|
195
172
|
* 8. Sort by menuOrder (ascending)
|
|
196
173
|
*
|
|
197
174
|
* @param options - Resolution options; see {@link IResolveApplicationsOptions}
|
|
@@ -199,10 +176,33 @@ function filterLocalByBaseUiAccess(localApps, ctx) {
|
|
|
199
176
|
*
|
|
200
177
|
* @internal - exported for testing
|
|
201
178
|
*/
|
|
202
|
-
export function resolveApplications({ localApps, remoteRegistry, ctx, scope, }) {
|
|
179
|
+
export function resolveApplications({ localApps, remoteRegistry, ctx, scope, skipWorkspacePermissions = false, skipSettings = false, }) {
|
|
203
180
|
const { applications: remoteApps, overrides, allowedStandardApplications } = remoteRegistry ?? {};
|
|
204
181
|
const filteredLocal = filterLocalByBaseUiAccess(filterByAllowedList(localApps, allowedStandardApplications), ctx);
|
|
205
|
-
return sortByMenuOrder(filterByRequirements(filterByScope(filterDisabled(applyOverrides(mergeApplications(filteredLocal, remoteApps ?? []), overrides)), scope), ctx));
|
|
182
|
+
return sortByMenuOrder(filterByRequirements(filterByScope(filterDisabled(applyOverrides(mergeApplications(filteredLocal, remoteApps ?? []), overrides)), scope), ctx, skipWorkspacePermissions, skipSettings));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Resolves the workspace-scoped applications published to pluggable applications through
|
|
186
|
+
* {@link @gooddata/sdk-pluggable-application-model#IPlatformContextV1.availableApplications}.
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* Applies every requirement that holds organization-wide and leaves the two that a single workspace
|
|
190
|
+
* answers — `requiredSettings` and `requiredWorkspacePermissions` — to the consumer, which evaluates
|
|
191
|
+
* them against each workspace it lists. A feature flag can be turned on for one workspace alone (early
|
|
192
|
+
* access values resolve into its settings), so deciding here would hide such an application from every
|
|
193
|
+
* workspace that has it.
|
|
194
|
+
*
|
|
195
|
+
* @internal
|
|
196
|
+
*/
|
|
197
|
+
export function resolveAvailableWorkspaceApplications(ctx) {
|
|
198
|
+
return resolveApplications({
|
|
199
|
+
localApps: getLocalApplications(),
|
|
200
|
+
remoteRegistry: getRemoteRegistry(ctx),
|
|
201
|
+
ctx,
|
|
202
|
+
scope: "workspace",
|
|
203
|
+
skipWorkspacePermissions: true,
|
|
204
|
+
skipSettings: true,
|
|
205
|
+
});
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
208
208
|
* Builds the resolved list of pluggable applications from the local and remote registries.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostChrome.d.ts","sourceRoot":"","sources":["../../src/ui/HostChrome.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAElG,OAAO,EACH,KAAK,iBAAiB,EAEtB,KAAK,gCAAgC,
|
|
1
|
+
{"version":3,"file":"HostChrome.d.ts","sourceRoot":"","sources":["../../src/ui/HostChrome.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AAElG,OAAO,EACH,KAAK,iBAAiB,EAEtB,KAAK,gCAAgC,EAExC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACxB,MAAM,2CAA2C,CAAC;AAGnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AA2B7D,OAAO,mBAAmB,CAAC;AAK3B,OAAO,0CAA0C,CAAC;AAClD,OAAO,6CAA6C,CAAC;AACrD,OAAO,sDAAsD,CAAC;AAC9D,OAAO,0DAA0D,CAAC;AAkBlE,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,gBAAgB,CAAC;IACtB,oBAAoB,EAAE,gCAAgC,EAAE,CAAC;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,YAAY,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,YAAY,CAAC,EAAE,OAAO,EACtB,kBAAkB,CAAC,EAAE,OAAO,KAC3B,IAAI,CAAC;IACV;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,UAAU,CAAC,EACvB,GAAG,EACH,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,SAAS,EAAE,UAAU,EACrB,aAAa,EACb,YAAmB,EACnB,YAAoB,EACpB,UAAkB,EAClB,QAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,QAAQ,GACX,EAAE,gBAAgB,2CAmQlB"}
|
package/esm/ui/HostChrome.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2026 GoodData Corporation
|
|
3
3
|
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { getPluggableApplicationLocalizedTitle, } from "@gooddata/sdk-model";
|
|
4
5
|
import { BackendProvider, resolveLocale } from "@gooddata/sdk-ui";
|
|
5
6
|
import { AppHeaderNotifications } from "@gooddata/sdk-ui-application-header";
|
|
6
7
|
import { AppHeader, DocumentHeader, ToastsCenterContextProvider, generateHeaderAccountMenuItems, generateHeaderCommonHelpMenuItems, generateHeaderStaticHelpMenuItems, } from "@gooddata/sdk-ui-kit";
|
|
@@ -9,7 +10,7 @@ import defaultLogoUrl from "../assets/logo-white.svg";
|
|
|
9
10
|
import { getAppLifecycleCallbacks } from "../loader/pluggableApplicationsLoader.js";
|
|
10
11
|
import { getActiveInternalApplication } from "../loader/routing.js";
|
|
11
12
|
import { getBackend } from "../platformContext/backend.js";
|
|
12
|
-
import { buildAppMenu
|
|
13
|
+
import { buildAppMenu } from "./appMenuItems.js";
|
|
13
14
|
import { getUserDisplayName, getWorkspaceSwitchPath, isPlainLeftClick } from "./chromeHelpers.js";
|
|
14
15
|
import { b, e } from "./hostChromeBem.js";
|
|
15
16
|
import { HostIntlProvider } from "./HostIntlProvider.js";
|
|
@@ -134,7 +135,9 @@ export function HostChrome({ ctx, resolvedApplications, pathname, onNavigate, on
|
|
|
134
135
|
// An app may report an empty page segment (e.g. no insight open); treat it the same as `undefined`
|
|
135
136
|
// (omitted) so the tab falls back to the active application's manifest title rather than going blank.
|
|
136
137
|
const documentPageTitle = appPageTitle ||
|
|
137
|
-
(activeApplication
|
|
138
|
+
(activeApplication
|
|
139
|
+
? getPluggableApplicationLocalizedTitle(activeApplication, ctx.preferredLocale)
|
|
140
|
+
: undefined);
|
|
138
141
|
const documentBrandTitle = ctx.isExportMode
|
|
139
142
|
? undefined
|
|
140
143
|
: ctx.whiteLabeling?.enabled
|
package/esm/ui/appMenuItems.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type ILocale, type PluggableApplicationRegistryItem } from "@gooddata/sdk-model";
|
|
2
2
|
import { type IPlatformContext } from "@gooddata/sdk-pluggable-application-model";
|
|
3
3
|
import { type IHeaderMenuItem } from "@gooddata/sdk-ui-kit";
|
|
4
|
-
export declare function getLocalizedTitle(app: PluggableApplicationRegistryItem, locale: ILocale | undefined): string;
|
|
5
4
|
export interface IAppMenuResult {
|
|
6
5
|
menuItemsGroups: IHeaderMenuItem[][];
|
|
7
6
|
messages: Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appMenuItems.d.ts","sourceRoot":"","sources":["../../src/ui/appMenuItems.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,gCAAgC,
|
|
1
|
+
{"version":3,"file":"appMenuItems.d.ts","sourceRoot":"","sources":["../../src/ui/appMenuItems.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,gCAAgC,EAGxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AA2B5D,MAAM,WAAW,cAAc;IAC3B,eAAe,EAAE,eAAe,EAAE,EAAE,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAeD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,gCAAgC,EAAE,EACxC,GAAG,EAAE,gBAAgB,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GAAG,SAAS,GAC5B,cAAc,CA4BhB"}
|
package/esm/ui/appMenuItems.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
|
-
import { isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
|
|
2
|
+
import { getPluggableApplicationLocalizedTitle, isExternalPluggableApplicationRegistryItem, } from "@gooddata/sdk-model";
|
|
3
3
|
import { DefaultApplicationId } from "@gooddata/sdk-pluggable-application-model";
|
|
4
4
|
import { getApplicationHref, isInternalAppRouteActive } from "../loader/routing.js";
|
|
5
5
|
import { isPlainLeftClick } from "./chromeHelpers.js";
|
|
@@ -21,15 +21,6 @@ const NAV_MSG_PREFIX = "shellApplication.menuItem.";
|
|
|
21
21
|
function navMessageKey(appId) {
|
|
22
22
|
return `${NAV_MSG_PREFIX}${appId}`;
|
|
23
23
|
}
|
|
24
|
-
export function getLocalizedTitle(app, locale) {
|
|
25
|
-
if (locale && app.localizedTitle) {
|
|
26
|
-
const localizedTitle = app.localizedTitle[locale];
|
|
27
|
-
if (localizedTitle) {
|
|
28
|
-
return localizedTitle;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return app.title;
|
|
32
|
-
}
|
|
33
24
|
// When `gdc-home-ui` is the only organization-scoped app, it is reachable via the logo
|
|
34
25
|
// and would otherwise appear as a redundant standalone "Organization" menu entry. We must
|
|
35
26
|
// have this shifty logic in place to emulate the old behavior of Panther Home UI app.
|
|
@@ -63,7 +54,7 @@ export function buildAppMenu(apps, ctx, pathname, locale) {
|
|
|
63
54
|
const href = getApplicationHref(app, ctx, pathname);
|
|
64
55
|
const isActive = !isExternal && isInternalAppRouteActive(app, ctx, pathname);
|
|
65
56
|
const key = navMessageKey(app.id);
|
|
66
|
-
messages[key] =
|
|
57
|
+
messages[key] = getPluggableApplicationLocalizedTitle(app, locale);
|
|
67
58
|
return {
|
|
68
59
|
key,
|
|
69
60
|
href,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-pluggable-host",
|
|
3
|
-
"version": "11.59.0-alpha.
|
|
3
|
+
"version": "11.59.0-alpha.1",
|
|
4
4
|
"description": "GoodData SDK runtime for hosting pluggable applications — registry, loader, routing, platform context, default UI chrome",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -29,20 +29,20 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@module-federation/runtime": "2.6.0",
|
|
31
31
|
"lodash-es": "^4.17.23",
|
|
32
|
-
"@gooddata/sdk-backend-
|
|
33
|
-
"@gooddata/sdk-backend-
|
|
34
|
-
"@gooddata/sdk-backend-
|
|
35
|
-
"@gooddata/sdk-
|
|
36
|
-
"@gooddata/sdk-
|
|
37
|
-
"@gooddata/sdk-
|
|
38
|
-
"@gooddata/sdk-ui": "11.59.0-alpha.
|
|
39
|
-
"@gooddata/sdk-
|
|
40
|
-
"@gooddata/sdk-ui-ext": "11.59.0-alpha.
|
|
41
|
-
"@gooddata/sdk-ui-
|
|
42
|
-
"@gooddata/sdk-ui-
|
|
43
|
-
"@gooddata/sdk-ui-
|
|
44
|
-
"@gooddata/
|
|
45
|
-
"@gooddata/
|
|
32
|
+
"@gooddata/sdk-backend-tiger": "11.59.0-alpha.1",
|
|
33
|
+
"@gooddata/sdk-backend-base": "11.59.0-alpha.1",
|
|
34
|
+
"@gooddata/sdk-backend-spi": "11.59.0-alpha.1",
|
|
35
|
+
"@gooddata/sdk-embedding": "11.59.0-alpha.1",
|
|
36
|
+
"@gooddata/sdk-model": "11.59.0-alpha.1",
|
|
37
|
+
"@gooddata/sdk-ui": "11.59.0-alpha.1",
|
|
38
|
+
"@gooddata/sdk-ui-application-header": "11.59.0-alpha.1",
|
|
39
|
+
"@gooddata/sdk-pluggable-application-model": "11.59.0-alpha.1",
|
|
40
|
+
"@gooddata/sdk-ui-ext": "11.59.0-alpha.1",
|
|
41
|
+
"@gooddata/sdk-ui-gen-ai": "11.59.0-alpha.1",
|
|
42
|
+
"@gooddata/sdk-ui-kit": "11.59.0-alpha.1",
|
|
43
|
+
"@gooddata/sdk-ui-semantic-search": "11.59.0-alpha.1",
|
|
44
|
+
"@gooddata/sdk-ui-theme-provider": "11.59.0-alpha.1",
|
|
45
|
+
"@gooddata/util": "11.59.0-alpha.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@microsoft/api-documenter": "7.30.10",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"vite": "8.0.16",
|
|
82
82
|
"vitest": "4.1.11",
|
|
83
83
|
"vitest-dom": "0.1.1",
|
|
84
|
-
"@gooddata/eslint-config": "11.59.0-alpha.
|
|
85
|
-
"@gooddata/oxlint-config": "11.59.0-alpha.
|
|
84
|
+
"@gooddata/eslint-config": "11.59.0-alpha.1",
|
|
85
|
+
"@gooddata/oxlint-config": "11.59.0-alpha.1"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
88
|
"react": ">=18.3.1",
|