@gooddata/sdk-ui-dashboard 11.40.0-alpha.0 → 11.40.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/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/model/commandHandlers/dashboard/exportToTabularHandler.js +5 -2
- package/esm/model/commandHandlers/dashboard/saveAsDashboardHandler.js +6 -0
- package/esm/model/commandHandlers/dashboard/saveDashboardHandler.js +30 -18
- package/esm/model/store/tabs/layout/layoutSelectors.d.ts +41 -1
- package/esm/model/store/tabs/layout/layoutSelectors.js +38 -0
- package/esm/model/store/tabs/parameters/parametersHelpers.d.ts +86 -0
- package/esm/model/store/tabs/parameters/parametersHelpers.js +208 -0
- package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +21 -16
- package/esm/model/store/tabs/parameters/parametersSelectors.js +45 -127
- package/esm/sdk-ui-dashboard.d.ts +25 -0
- package/package.json +20 -20
package/esm/__version.d.ts
CHANGED
package/esm/__version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// (C) 2021 GoodData Corporation
|
|
2
2
|
// DO NOT CHANGE THIS FILE, IT IS RE-GENERATED ON EVERY BUILD
|
|
3
|
-
export const LIB_VERSION = "11.40.0-alpha.
|
|
3
|
+
export const LIB_VERSION = "11.40.0-alpha.1";
|
|
4
4
|
export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
|
|
5
5
|
export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
|
package/esm/index.d.ts
CHANGED
|
@@ -140,7 +140,7 @@ export { catalogActions } from "./model/store/catalog/index.js";
|
|
|
140
140
|
export type { SetCatalogMeasuresAndFactsPayload, SetCatalogItemsPayload, } from "./model/store/catalog/catalogReducers.js";
|
|
141
141
|
export type { IAddParameterPayload, IRemoveParameterPayload, ISetParameterRuntimeValuePayload, ISetParameterRuntimeValuesPayload, } from "./model/store/tabs/parameters/parametersReducers.js";
|
|
142
142
|
export type { IDashboardParameterEntry, IParametersState, } from "./model/store/tabs/parameters/parametersState.js";
|
|
143
|
-
export { selectDashboardParameterEntries, selectDashboardParameters, selectEffectiveParameterValuesForWidget, selectHasAnyResettableParameterOnActiveTab, selectIsParametersChanged, selectParameterRuntimeOverrideByRef, } from "./model/store/tabs/parameters/parametersSelectors.js";
|
|
143
|
+
export { selectDashboardParameterEntries, selectDashboardParameters, selectEffectiveParameterValuesForWidget, selectExportEffectiveParameters, selectHasAnyResettableParameterOnActiveTab, selectIsParametersChanged, selectParameterRuntimeOverrideByRef, } from "./model/store/tabs/parameters/parametersSelectors.js";
|
|
144
144
|
export { drillActions } from "./model/store/drill/index.js";
|
|
145
145
|
export { selectDrillableItems, selectIsCrossFiltering, selectCrossFilteringItems, selectCrossFilteringItemByWidgetRef, selectCrossFilteringFiltersLocalIdentifiers, selectCrossFilteringFiltersLocalIdentifiersByWidgetRef, selectCrossFilteringSelectedPointsByWidgetRef, selectIsFilterFromCrossFilteringByLocalIdentifier, } from "./model/store/drill/drillSelectors.js";
|
|
146
146
|
export type { IDrillState } from "./model/store/drill/drillState.js";
|
package/esm/index.js
CHANGED
|
@@ -101,7 +101,7 @@ export { selectDateFilterConfigsOverrides, selectDateFilterConfigsOverridesByTab
|
|
|
101
101
|
export { selectInsights, selectInsightRefs, selectInsightsMap, selectInsightByRef, selectInsightByWidgetRef, selectRawExportOverridesForInsightByRef, } from "./model/store/insights/insightsSelectors.js";
|
|
102
102
|
export { selectCatalogIsLoaded, selectAttributesWithDrillDown, selectCatalogAttributes, selectCatalogAttributeDisplayForms, selectCatalogDateDatasets, selectCatalogFacts, selectCatalogMeasures, selectAllCatalogAttributesMap, selectAllCatalogDisplayFormsMap, selectAllCatalogDateDatasetsMap, selectAllCatalogMeasuresMap, selectHasCatalogAttributes, selectHasCatalogMeasures, selectHasCatalogDateDatasets, selectHasCatalogFacts, selectCatalogAttributeHierarchies, selectCatalogDateAttributes, selectDateHierarchyTemplates, selectAdhocDateHierarchies, selectAllCatalogAttributeHierarchies, selectCatalogAttributeDisplayFormsById, selectCatalogParameters, selectCatalogParametersStatus, selectCatalogParametersIsLoaded, selectCatalogMeasureParameters, selectCatalogMeasureParametersStatus, } from "./model/store/catalog/catalogSelectors.js";
|
|
103
103
|
export { catalogActions } from "./model/store/catalog/index.js";
|
|
104
|
-
export { selectDashboardParameterEntries, selectDashboardParameters, selectEffectiveParameterValuesForWidget, selectHasAnyResettableParameterOnActiveTab, selectIsParametersChanged, selectParameterRuntimeOverrideByRef, } from "./model/store/tabs/parameters/parametersSelectors.js";
|
|
104
|
+
export { selectDashboardParameterEntries, selectDashboardParameters, selectEffectiveParameterValuesForWidget, selectExportEffectiveParameters, selectHasAnyResettableParameterOnActiveTab, selectIsParametersChanged, selectParameterRuntimeOverrideByRef, } from "./model/store/tabs/parameters/parametersSelectors.js";
|
|
105
105
|
export { drillActions } from "./model/store/drill/index.js";
|
|
106
106
|
export { selectDrillableItems, selectIsCrossFiltering, selectCrossFilteringItems, selectCrossFilteringItemByWidgetRef, selectCrossFilteringFiltersLocalIdentifiers, selectCrossFilteringFiltersLocalIdentifiersByWidgetRef, selectCrossFilteringSelectedPointsByWidgetRef, selectIsFilterFromCrossFilteringByLocalIdentifier, } from "./model/store/drill/drillSelectors.js";
|
|
107
107
|
export { selectCurrentUser, selectCurrentUserRef } from "./model/store/user/userSelectors.js";
|
|
@@ -6,7 +6,8 @@ import { selectExportResultPollingTimeout } from "../../store/config/configSelec
|
|
|
6
6
|
import { selectFilterViews } from "../../store/filterViews/filterViewsReducersSelectors.js";
|
|
7
7
|
import { selectDashboardRef, selectIsFiltersChanged } from "../../store/meta/metaSelectors.js";
|
|
8
8
|
import { selectFilterContextFilters, selectFiltersByTab, } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
9
|
-
|
|
9
|
+
import { selectExportEffectiveParameters } from "../../store/tabs/parameters/parametersSelectors.js";
|
|
10
|
+
function exportDashboardToTabular(ctx, dashboardRef, title, mergeHeaders, exportInfo, widgetIds, dashboardFiltersOverride, filtersByTab, dashboardTabsParametersOverrides, format, pdfConfiguration, timeout) {
|
|
10
11
|
const { backend, workspace } = ctx;
|
|
11
12
|
return backend.workspace(workspace).dashboards().exportDashboardToTabular(dashboardRef, {
|
|
12
13
|
title,
|
|
@@ -15,6 +16,7 @@ function exportDashboardToTabular(ctx, dashboardRef, title, mergeHeaders, export
|
|
|
15
16
|
exportInfo,
|
|
16
17
|
dashboardFiltersOverride,
|
|
17
18
|
dashboardTabsFiltersOverrides: filtersByTab,
|
|
19
|
+
dashboardTabsParametersOverrides,
|
|
18
20
|
widgetIds,
|
|
19
21
|
pdfConfiguration,
|
|
20
22
|
timeout,
|
|
@@ -32,8 +34,9 @@ export function* exportToTabularHandler(ctx, cmd) {
|
|
|
32
34
|
const hasDefaultFilterViewApplied = filterViews.some((filterView) => filterView.isDefault);
|
|
33
35
|
const filterContextFilters = yield select(selectFilterContextFilters);
|
|
34
36
|
const filtersByTab = yield select(selectFiltersByTab);
|
|
37
|
+
const parametersByTab = yield select(selectExportEffectiveParameters(widgetIds));
|
|
35
38
|
const timeout = yield select(selectExportResultPollingTimeout);
|
|
36
|
-
const result = yield call(exportDashboardToTabular, ctx, dashboardRef, fileName, mergeHeaders, exportInfo, widgetIds, isFilterContextChanged || hasDefaultFilterViewApplied ? filterContextFilters : undefined, isFilterContextChanged || hasDefaultFilterViewApplied ? filtersByTab : undefined, format, pdfConfiguration, timeout);
|
|
39
|
+
const result = yield call(exportDashboardToTabular, ctx, dashboardRef, fileName, mergeHeaders, exportInfo, widgetIds, isFilterContextChanged || hasDefaultFilterViewApplied ? filterContextFilters : undefined, isFilterContextChanged || hasDefaultFilterViewApplied ? filtersByTab : undefined, parametersByTab, format, pdfConfiguration, timeout);
|
|
37
40
|
// prepend hostname if provided so that the results are downloaded from there, not from where the app is hosted
|
|
38
41
|
const fullUri = ctx.backend.config.hostname
|
|
39
42
|
? new URL(result.uri, ctx.backend.config.hostname).href
|
|
@@ -50,6 +50,11 @@ function processExistingTabsForSaveAs(tabs, parametersByTab, useOriginalFilterCo
|
|
|
50
50
|
const measureValueFilterConfigsProp = measureValueFilterConfigs?.length
|
|
51
51
|
? { measureValueFilterConfigs }
|
|
52
52
|
: {};
|
|
53
|
+
const filterGroupsConfigProp = tab.filterGroupsConfig
|
|
54
|
+
? {
|
|
55
|
+
filterGroupsConfig: tab.filterGroupsConfig,
|
|
56
|
+
}
|
|
57
|
+
: {};
|
|
53
58
|
// Get this tab's specific layout, filter out custom widgets, then process
|
|
54
59
|
const tabLayout = tab.layout?.layout
|
|
55
60
|
? processLayout(filterOutCustomWidgets(tab.layout.layout))
|
|
@@ -73,6 +78,7 @@ function processExistingTabsForSaveAs(tabs, parametersByTab, useOriginalFilterCo
|
|
|
73
78
|
...dateFilterConfigsProp,
|
|
74
79
|
...attributeFilterConfigsProp,
|
|
75
80
|
...measureValueFilterConfigsProp,
|
|
81
|
+
...filterGroupsConfigProp,
|
|
76
82
|
// Always persist `parameters` (incl. `[]`) so V1 root fallback never re-hydrates stale
|
|
77
83
|
// root parameters when every tab has been emptied.
|
|
78
84
|
parameters: tabParameters,
|
|
@@ -20,13 +20,13 @@ import { metaActions } from "../../store/meta/index.js";
|
|
|
20
20
|
import { selectDashboardDescriptor, selectPersistedDashboard } from "../../store/meta/metaSelectors.js";
|
|
21
21
|
import { selectIsInViewMode } from "../../store/renderMode/renderModeSelectors.js";
|
|
22
22
|
import { savingActions } from "../../store/saving/index.js";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
23
|
+
import { selectAttributeFilterConfigsOverridesByTab } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
24
|
+
import { selectDateFilterConfigOverridesByTab } from "../../store/tabs/dateFilterConfig/dateFilterConfigSelectors.js";
|
|
25
|
+
import { selectDateFilterConfigsOverridesByTab } from "../../store/tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
|
|
26
|
+
import { selectFilterContextStatesByTab } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
27
27
|
import { tabsActions } from "../../store/tabs/index.js";
|
|
28
|
-
import { filterOutCustomWidgets,
|
|
29
|
-
import {
|
|
28
|
+
import { filterOutCustomWidgets, selectBasicLayoutByTab } from "../../store/tabs/layout/layoutSelectors.js";
|
|
29
|
+
import { selectMeasureValueFilterConfigsOverridesByTab } from "../../store/tabs/measureValueFilterConfigs/measureValueFilterConfigsSelectors.js";
|
|
30
30
|
import { selectSmartPersistedTabsParameters } from "../../store/tabs/parameters/parametersSelectors.js";
|
|
31
31
|
import { selectTabs } from "../../store/tabs/tabsSelectors.js";
|
|
32
32
|
import { isTemporaryIdentity } from "../../utils/dashboardItemUtils.js";
|
|
@@ -156,14 +156,24 @@ function resolveProcessedTabs(tabs, rootFilterContext, layout, dateFilterConfig,
|
|
|
156
156
|
function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
157
157
|
const persistedDashboard = yield select(selectPersistedDashboard);
|
|
158
158
|
const dashboardDescriptor = yield select(selectDashboardDescriptor);
|
|
159
|
-
const filterContextDefinition = yield select(selectFilterContextDefinition);
|
|
160
|
-
const filterContextIdentity = yield select(selectFilterContextIdentity);
|
|
161
|
-
const layout = yield select(selectBasicLayout);
|
|
162
|
-
const dateFilterConfig = yield select(selectDateFilterConfigOverrides);
|
|
163
|
-
const attributeFilterConfigs = yield select(selectAttributeFilterConfigsOverrides);
|
|
164
|
-
const dateFilterConfigs = yield select(selectDateFilterConfigsOverrides);
|
|
165
|
-
const measureValueFilterConfigs = yield select(selectMeasureValueFilterConfigsOverrides);
|
|
166
159
|
const tabs = yield select(selectTabs);
|
|
160
|
+
// Root-level properties must always reflect the first tab, regardless of which tab is active.
|
|
161
|
+
// Using active-tab selectors here would persist the wrong data when saving from a non-first tab.
|
|
162
|
+
const firstTabId = tabs?.[0]?.localIdentifier;
|
|
163
|
+
const filterContextStatesByTab = yield select(selectFilterContextStatesByTab);
|
|
164
|
+
const basicLayoutByTab = yield select(selectBasicLayoutByTab);
|
|
165
|
+
const dateFilterConfigByTab = yield select(selectDateFilterConfigOverridesByTab);
|
|
166
|
+
const attributeFilterConfigsByTab = yield select(selectAttributeFilterConfigsOverridesByTab);
|
|
167
|
+
const dateFilterConfigsByTab = yield select(selectDateFilterConfigsOverridesByTab);
|
|
168
|
+
const measureValueFilterConfigsByTab = yield select(selectMeasureValueFilterConfigsOverridesByTab);
|
|
169
|
+
const firstTabFilterContextState = firstTabId ? filterContextStatesByTab[firstTabId] : undefined;
|
|
170
|
+
const filterContextDefinition = firstTabFilterContextState?.filterContextDefinition;
|
|
171
|
+
const filterContextIdentity = firstTabFilterContextState?.filterContextIdentity;
|
|
172
|
+
const layout = firstTabId ? basicLayoutByTab[firstTabId] : undefined;
|
|
173
|
+
const dateFilterConfig = firstTabId ? dateFilterConfigByTab[firstTabId] : undefined;
|
|
174
|
+
const attributeFilterConfigs = firstTabId ? attributeFilterConfigsByTab[firstTabId] : undefined;
|
|
175
|
+
const dateFilterConfigs = firstTabId ? dateFilterConfigsByTab[firstTabId] : undefined;
|
|
176
|
+
const measureValueFilterConfigs = firstTabId ? measureValueFilterConfigsByTab[firstTabId] : undefined;
|
|
167
177
|
const parametersByTab = yield select(selectSmartPersistedTabsParameters);
|
|
168
178
|
const capabilities = yield select(selectBackendCapabilities);
|
|
169
179
|
/*
|
|
@@ -195,10 +205,12 @@ function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
|
195
205
|
...dashboardDescriptor,
|
|
196
206
|
title,
|
|
197
207
|
...dashboardIdentity,
|
|
198
|
-
filterContext:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
filterContext: filterContextDefinition
|
|
209
|
+
? {
|
|
210
|
+
...filterContextIdentity,
|
|
211
|
+
...filterContextDefinition,
|
|
212
|
+
}
|
|
213
|
+
: undefined,
|
|
202
214
|
layout,
|
|
203
215
|
dateFilterConfig,
|
|
204
216
|
...buildOptionalFilterConfigsProps(attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs),
|
|
@@ -207,7 +219,7 @@ function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
|
207
219
|
};
|
|
208
220
|
const dashboardToSave = {
|
|
209
221
|
...dashboardFromState,
|
|
210
|
-
layout: dashboardLayoutRemoveIdentity(layout, isTemporaryIdentity),
|
|
222
|
+
layout: layout ? dashboardLayoutRemoveIdentity(layout, isTemporaryIdentity) : undefined,
|
|
211
223
|
};
|
|
212
224
|
return {
|
|
213
225
|
cmd,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type DrillDefinition, type IDashboardLayout, type IDrillDownReference, type IDrillToLegacyDashboard, type IKpiWidget, type IWidget, type InsightDrillDefinition, type ObjRef, type ScreenSize } from "@gooddata/sdk-model";
|
|
1
|
+
import { type DrillDefinition, type IDashboardLayout, type IDrillDownReference, type IDrillToLegacyDashboard, type IInsightWidget, type IKpiWidget, type IWidget, type InsightDrillDefinition, type ObjRef, type ScreenSize } from "@gooddata/sdk-model";
|
|
2
2
|
import { type ObjRefMap } from "../../../../_staging/metadata/objRefMap.js";
|
|
3
3
|
import { type IDashboardFilter, type ILayoutCoordinates, type ILayoutItemPath } from "../../../../types.js";
|
|
4
4
|
import { type DashboardLayoutCommands } from "../../../commands/layout.js";
|
|
5
5
|
import { type ExtendedDashboardWidget, type ICustomWidget } from "../../../types/layoutTypes.js";
|
|
6
6
|
import { type IUndoableCommand } from "../../_infra/undoEnhancer.js";
|
|
7
7
|
import { type DashboardSelector } from "../../types.js";
|
|
8
|
+
import { type ITabState } from "../tabsState.js";
|
|
8
9
|
import { type LayoutStash } from "./layoutState.js";
|
|
9
10
|
/**
|
|
10
11
|
* Returns layout keyed by tab identifier.
|
|
@@ -248,6 +249,45 @@ export declare const selectKpiWidgetPlaceholderCoordinates: DashboardSelector<IL
|
|
|
248
249
|
* @alpha
|
|
249
250
|
*/
|
|
250
251
|
export declare const selectWidgetIgnoreCrossFiltering: (ref: ObjRef) => DashboardSelector<boolean>;
|
|
252
|
+
/**
|
|
253
|
+
* Per-tab widget contexts. Each entry pairs a widget with its owning tab; nested layout widgets
|
|
254
|
+
* and visualization switcher children are flattened in. Used by cross-tab widget lookups.
|
|
255
|
+
*
|
|
256
|
+
* @internal
|
|
257
|
+
*/
|
|
258
|
+
export interface IWidgetTabContext {
|
|
259
|
+
tab: ITabState;
|
|
260
|
+
widget: ExtendedDashboardWidget;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* `IWidgetTabContext` narrowed to its insight-widget shape.
|
|
264
|
+
*
|
|
265
|
+
* @internal
|
|
266
|
+
*/
|
|
267
|
+
export interface IInsightWidgetTabContext extends IWidgetTabContext {
|
|
268
|
+
widget: IInsightWidget;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Cross-tab widget contexts. Walks every tab's layout (including nested layouts and visualization
|
|
272
|
+
* switcher children) once and pairs each widget with its owning tab. Consumers needing to resolve
|
|
273
|
+
* a widget's owning tab by ref or identifier should `.find()` against this array.
|
|
274
|
+
*
|
|
275
|
+
* @internal
|
|
276
|
+
*/
|
|
277
|
+
export declare const selectAllTabsWidgetContexts: DashboardSelector<IWidgetTabContext[]>;
|
|
278
|
+
/**
|
|
279
|
+
* `selectAllTabsWidgetContexts` pre-filtered to insight-widget contexts so consumers can `.find()`
|
|
280
|
+
* against an already-narrowed array.
|
|
281
|
+
*
|
|
282
|
+
* @internal
|
|
283
|
+
*/
|
|
284
|
+
export declare const selectAllTabsInsightWidgetContexts: DashboardSelector<IInsightWidgetTabContext[]>;
|
|
285
|
+
/**
|
|
286
|
+
* Type guard for narrowing an `IWidgetTabContext` to `IInsightWidgetTabContext`.
|
|
287
|
+
*
|
|
288
|
+
* @internal
|
|
289
|
+
*/
|
|
290
|
+
export declare function isInsightWidgetTabContext(context: IWidgetTabContext): context is IInsightWidgetTabContext;
|
|
251
291
|
/**
|
|
252
292
|
* Selects the local identifier of the tab that contains the specified widget.
|
|
253
293
|
* Returns undefined if the widget is not found in any tab.
|
|
@@ -452,6 +452,44 @@ export const selectKpiWidgetPlaceholderCoordinates = createSelector(selectKpiWid
|
|
|
452
452
|
export const selectWidgetIgnoreCrossFiltering = createMemoizedSelector((ref) => createSelector(selectAnalyticalWidgetByRef(ref), (widget) => {
|
|
453
453
|
return widget?.ignoreCrossFiltering ?? false;
|
|
454
454
|
}));
|
|
455
|
+
/**
|
|
456
|
+
* Cross-tab widget contexts. Walks every tab's layout (including nested layouts and visualization
|
|
457
|
+
* switcher children) once and pairs each widget with its owning tab. Consumers needing to resolve
|
|
458
|
+
* a widget's owning tab by ref or identifier should `.find()` against this array.
|
|
459
|
+
*
|
|
460
|
+
* @internal
|
|
461
|
+
*/
|
|
462
|
+
export const selectAllTabsWidgetContexts = createSelector(selectTabs, (tabs) => {
|
|
463
|
+
if (!tabs) {
|
|
464
|
+
return [];
|
|
465
|
+
}
|
|
466
|
+
const result = [];
|
|
467
|
+
for (const tab of tabs) {
|
|
468
|
+
const layout = tab.layout?.layout;
|
|
469
|
+
if (!layout) {
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
for (const widget of getLayoutWidgets(layout)) {
|
|
473
|
+
result.push({ tab, widget });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return result;
|
|
477
|
+
});
|
|
478
|
+
/**
|
|
479
|
+
* `selectAllTabsWidgetContexts` pre-filtered to insight-widget contexts so consumers can `.find()`
|
|
480
|
+
* against an already-narrowed array.
|
|
481
|
+
*
|
|
482
|
+
* @internal
|
|
483
|
+
*/
|
|
484
|
+
export const selectAllTabsInsightWidgetContexts = createSelector(selectAllTabsWidgetContexts, (contexts) => contexts.filter(isInsightWidgetTabContext));
|
|
485
|
+
/**
|
|
486
|
+
* Type guard for narrowing an `IWidgetTabContext` to `IInsightWidgetTabContext`.
|
|
487
|
+
*
|
|
488
|
+
* @internal
|
|
489
|
+
*/
|
|
490
|
+
export function isInsightWidgetTabContext(context) {
|
|
491
|
+
return isInsightWidget(context.widget);
|
|
492
|
+
}
|
|
455
493
|
/**
|
|
456
494
|
* Selects the local identifier of the tab that contains the specified widget.
|
|
457
495
|
* Returns undefined if the widget is not found in any tab.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type IDashboardParameterValueOverride } from "@gooddata/sdk-backend-spi";
|
|
2
|
+
import { type IDashboardParameter, type IDashboardTab, type IInsight, type IInsightDefinition, type IParameterMetadataObject, type IdentifierRef } from "@gooddata/sdk-model";
|
|
3
|
+
import { type ObjRefMap } from "../../../../_staging/metadata/objRefMap.js";
|
|
4
|
+
import { type IInsightWidgetTabContext } from "../layout/layoutSelectors.js";
|
|
5
|
+
import { type ITabState } from "../tabsState.js";
|
|
6
|
+
import { type IDashboardParameterEntry } from "./parametersState.js";
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare const EMPTY_EXPORT_PARAMETERS: Record<string, IDashboardParameterValueOverride[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Walks the insight's metric buckets and returns the parameter refs they reference,
|
|
13
|
+
* deduped by ref string. The order matches the first occurrence in the measure traversal.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function collectReferencedParameterRefs(insight: IInsightDefinition, measureParameters: Record<string, IdentifierRef[]>): IdentifierRef[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolves a single parameter entry to the export wire shape. Returns `undefined` for entries with
|
|
20
|
+
* no `runtimeOverride` — the dashboard's persisted parameter state and the workspace default are
|
|
21
|
+
* applied by the backend on its own, and insight-level `insight.parameters` are applied per-insight
|
|
22
|
+
* by the backend; sending a row in those cases would override the backend's resolution with a stale
|
|
23
|
+
* FE snapshot (matches the live-render path in `selectEffectiveParameterValuesForWidget`).
|
|
24
|
+
*
|
|
25
|
+
* Title precedence (when a row is emitted): `parameter.label` → workspace title → `ref.identifier`.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatDashboardParameter(entry: IDashboardParameterEntry, workspaceParameter: IParameterMetadataObject | undefined): IDashboardParameterValueOverride | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the persisted shape of a parameter entry against its workspace catalog parameter.
|
|
32
|
+
* Drops `value` when it equals the workspace default; drops `label` when it equals the workspace title.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export declare function smartPersistResolvedEntry(entry: IDashboardParameterEntry, workspaceParameter: IParameterMetadataObject): IDashboardParameter;
|
|
37
|
+
/**
|
|
38
|
+
* Builds the persisted-parameter lookup keyed by tab and then by ref string. Honors V1 → per-tab
|
|
39
|
+
* migration: when no tab in the persisted dashboard carries `parameters`, the persisted root
|
|
40
|
+
* `parameters` is used as fallback for every tab.
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildPersistedByTabAndRef(persistedTabs: ReadonlyArray<IDashboardTab>, rootPersistedParameters: IDashboardParameter[]): Map<string, Map<string, IDashboardParameter>>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns `undefined` when reset would be a no-op: missing/non-number workspace parameter, or
|
|
47
|
+
* in edit mode when `parameter.value` is unset / already equals the workspace default.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export declare function computeParameterResetValue(entry: IDashboardParameterEntry, workspaceParameter: IParameterMetadataObject | undefined, isInEditMode: boolean): number | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Only `mode: "active"` entries with a defined `runtimeOverride` are considered resettable;
|
|
54
|
+
* HIDDEN and READONLY entries are skipped, and entries with `runtimeOverride === undefined`
|
|
55
|
+
* (chip hidden, execution falls back to `insight.parameters`) are preserved as-is — symmetric
|
|
56
|
+
* with per-chip behavior in `DashboardParameterFilter`.
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export declare function computeParameterResetTargets(entries: IDashboardParameterEntry[], workspaceParameters: IParameterMetadataObject[], isInEditMode: boolean): {
|
|
61
|
+
ref: IDashboardParameterEntry["parameter"]["ref"];
|
|
62
|
+
value: number | undefined;
|
|
63
|
+
}[];
|
|
64
|
+
/**
|
|
65
|
+
* Folds per-tab ref selections into the wire shape, dropping entries without a
|
|
66
|
+
* `runtimeOverride`. Each selection pairs a tab with an optional ref restriction:
|
|
67
|
+
* `allowedRefs === undefined` selects all refs (whole-dashboard scope); a defined set restricts
|
|
68
|
+
* entries to those refs (widget scope). Tabs that yield no rows are omitted from the result.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export declare function collectExportOverrides(tabRefSelections: ReadonlyArray<{
|
|
73
|
+
tab: ITabState;
|
|
74
|
+
allowedRefs?: Set<string>;
|
|
75
|
+
}>, workspaceByRef: Map<string, IParameterMetadataObject>): Record<string, IDashboardParameterValueOverride[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Builds per-tab ref selections for a widget-scope export: each widget's owning tab is restricted
|
|
78
|
+
* to refs referenced by the widget's insight metrics. Multiple widgets on the same tab union their
|
|
79
|
+
* refs.
|
|
80
|
+
*
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildWidgetScopeTabRefSelections(widgetContexts: ReadonlyArray<IInsightWidgetTabContext>, widgetIds: ReadonlyArray<string>, insights: ObjRefMap<IInsight>, measureParameters: Record<string, IdentifierRef[]>): {
|
|
84
|
+
tab: ITabState;
|
|
85
|
+
allowedRefs: Set<string>;
|
|
86
|
+
}[];
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { DashboardParameterModeValues, insightMeasures, isMeasureDefinition, isNumberParameterDefinition, objRefToString, } from "@gooddata/sdk-model";
|
|
3
|
+
import { parametersInitialState, pickTabParametersSource, } from "./parametersState.js";
|
|
4
|
+
const EMPTY_PARAMETERS = [];
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export const EMPTY_EXPORT_PARAMETERS = {};
|
|
9
|
+
/**
|
|
10
|
+
* Walks the insight's metric buckets and returns the parameter refs they reference,
|
|
11
|
+
* deduped by ref string. The order matches the first occurrence in the measure traversal.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export function collectReferencedParameterRefs(insight, measureParameters) {
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
const result = [];
|
|
18
|
+
for (const measure of insightMeasures(insight)) {
|
|
19
|
+
const def = measure.measure.definition;
|
|
20
|
+
if (!isMeasureDefinition(def)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const refs = measureParameters[objRefToString(def.measureDefinition.item)] ?? [];
|
|
24
|
+
for (const ref of refs) {
|
|
25
|
+
const key = objRefToString(ref);
|
|
26
|
+
if (seen.has(key)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
seen.add(key);
|
|
30
|
+
result.push(ref);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a single parameter entry to the export wire shape. Returns `undefined` for entries with
|
|
37
|
+
* no `runtimeOverride` — the dashboard's persisted parameter state and the workspace default are
|
|
38
|
+
* applied by the backend on its own, and insight-level `insight.parameters` are applied per-insight
|
|
39
|
+
* by the backend; sending a row in those cases would override the backend's resolution with a stale
|
|
40
|
+
* FE snapshot (matches the live-render path in `selectEffectiveParameterValuesForWidget`).
|
|
41
|
+
*
|
|
42
|
+
* Title precedence (when a row is emitted): `parameter.label` → workspace title → `ref.identifier`.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export function formatDashboardParameter(entry, workspaceParameter) {
|
|
47
|
+
if (entry.runtimeOverride === undefined) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const ref = entry.parameter.ref;
|
|
51
|
+
return {
|
|
52
|
+
id: ref.identifier,
|
|
53
|
+
value: String(entry.runtimeOverride),
|
|
54
|
+
title: entry.parameter.label || workspaceParameter?.title || ref.identifier,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolves the persisted shape of a parameter entry against its workspace catalog parameter.
|
|
59
|
+
* Drops `value` when it equals the workspace default; drops `label` when it equals the workspace title.
|
|
60
|
+
*
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
export function smartPersistResolvedEntry(entry, workspaceParameter) {
|
|
64
|
+
const workspaceDefault = isNumberParameterDefinition(workspaceParameter.definition)
|
|
65
|
+
? workspaceParameter.definition.defaultValue
|
|
66
|
+
: undefined;
|
|
67
|
+
const result = {
|
|
68
|
+
ref: entry.parameter.ref,
|
|
69
|
+
parameterType: entry.parameter.parameterType,
|
|
70
|
+
mode: entry.parameter.mode,
|
|
71
|
+
...labelOverride(entry, workspaceParameter),
|
|
72
|
+
};
|
|
73
|
+
if (entry.runtimeOverride === undefined || entry.runtimeOverride === workspaceDefault) {
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
return { ...result, value: entry.runtimeOverride };
|
|
77
|
+
}
|
|
78
|
+
function labelOverride(entry, workspaceParameter) {
|
|
79
|
+
if (entry.parameter.label && entry.parameter.label !== workspaceParameter.title) {
|
|
80
|
+
return { label: entry.parameter.label };
|
|
81
|
+
}
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Builds the persisted-parameter lookup keyed by tab and then by ref string. Honors V1 → per-tab
|
|
86
|
+
* migration: when no tab in the persisted dashboard carries `parameters`, the persisted root
|
|
87
|
+
* `parameters` is used as fallback for every tab.
|
|
88
|
+
*
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
export function buildPersistedByTabAndRef(persistedTabs, rootPersistedParameters) {
|
|
92
|
+
const result = new Map();
|
|
93
|
+
for (const tab of persistedTabs) {
|
|
94
|
+
const sourceParameters = pickTabParametersSource(tab, persistedTabs, rootPersistedParameters) ?? EMPTY_PARAMETERS;
|
|
95
|
+
result.set(tab.localIdentifier, new Map(sourceParameters.map((parameter) => [objRefToString(parameter.ref), parameter])));
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Returns `undefined` when reset would be a no-op: missing/non-number workspace parameter, or
|
|
101
|
+
* in edit mode when `parameter.value` is unset / already equals the workspace default.
|
|
102
|
+
*
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
export function computeParameterResetValue(entry, workspaceParameter, isInEditMode) {
|
|
106
|
+
if (!workspaceParameter || !isNumberParameterDefinition(workspaceParameter.definition)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const workspaceDefault = workspaceParameter.definition.defaultValue;
|
|
110
|
+
const dashboardOverride = entry.parameter.value;
|
|
111
|
+
if (isInEditMode) {
|
|
112
|
+
if (dashboardOverride === undefined || dashboardOverride === workspaceDefault) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return workspaceDefault;
|
|
116
|
+
}
|
|
117
|
+
return dashboardOverride ?? workspaceDefault;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Only `mode: "active"` entries with a defined `runtimeOverride` are considered resettable;
|
|
121
|
+
* HIDDEN and READONLY entries are skipped, and entries with `runtimeOverride === undefined`
|
|
122
|
+
* (chip hidden, execution falls back to `insight.parameters`) are preserved as-is — symmetric
|
|
123
|
+
* with per-chip behavior in `DashboardParameterFilter`.
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
export function computeParameterResetTargets(entries, workspaceParameters, isInEditMode) {
|
|
128
|
+
const workspaceByRef = new Map(workspaceParameters.map((wp) => [objRefToString(wp.ref), wp]));
|
|
129
|
+
const result = [];
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
if (entry.parameter.mode !== DashboardParameterModeValues.ACTIVE) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (entry.runtimeOverride === undefined) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const workspaceParameter = workspaceByRef.get(objRefToString(entry.parameter.ref));
|
|
138
|
+
const resetValue = computeParameterResetValue(entry, workspaceParameter, isInEditMode);
|
|
139
|
+
if (resetValue !== undefined && resetValue !== entry.runtimeOverride) {
|
|
140
|
+
result.push({ ref: entry.parameter.ref, value: resetValue });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Folds per-tab ref selections into the wire shape, dropping entries without a
|
|
147
|
+
* `runtimeOverride`. Each selection pairs a tab with an optional ref restriction:
|
|
148
|
+
* `allowedRefs === undefined` selects all refs (whole-dashboard scope); a defined set restricts
|
|
149
|
+
* entries to those refs (widget scope). Tabs that yield no rows are omitted from the result.
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export function collectExportOverrides(tabRefSelections, workspaceByRef) {
|
|
154
|
+
const result = {};
|
|
155
|
+
for (const { tab, allowedRefs } of tabRefSelections) {
|
|
156
|
+
const entries = tab.parameters?.parameters ?? parametersInitialState.parameters;
|
|
157
|
+
const scoped = allowedRefs
|
|
158
|
+
? entries.filter((entry) => allowedRefs.has(objRefToString(entry.parameter.ref)))
|
|
159
|
+
: entries;
|
|
160
|
+
const rows = formatEntries(scoped, workspaceByRef);
|
|
161
|
+
if (rows.length > 0) {
|
|
162
|
+
result[tab.localIdentifier] = rows;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return Object.keys(result).length === 0 ? EMPTY_EXPORT_PARAMETERS : result;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Builds per-tab ref selections for a widget-scope export: each widget's owning tab is restricted
|
|
169
|
+
* to refs referenced by the widget's insight metrics. Multiple widgets on the same tab union their
|
|
170
|
+
* refs.
|
|
171
|
+
*
|
|
172
|
+
* @internal
|
|
173
|
+
*/
|
|
174
|
+
export function buildWidgetScopeTabRefSelections(widgetContexts, widgetIds, insights, measureParameters) {
|
|
175
|
+
const byTab = new Map();
|
|
176
|
+
for (const widgetId of widgetIds) {
|
|
177
|
+
const context = widgetContexts.find((ctx) => ctx.widget.identifier === widgetId);
|
|
178
|
+
if (!context) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const insight = insights.get(context.widget.insight);
|
|
182
|
+
if (!insight) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const referencedRefs = collectReferencedParameterRefs(insight, measureParameters);
|
|
186
|
+
if (referencedRefs.length === 0) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const tabId = context.tab.localIdentifier;
|
|
190
|
+
const existing = byTab.get(tabId) ?? { tab: context.tab, allowedRefs: new Set() };
|
|
191
|
+
for (const ref of referencedRefs) {
|
|
192
|
+
existing.allowedRefs.add(objRefToString(ref));
|
|
193
|
+
}
|
|
194
|
+
byTab.set(tabId, existing);
|
|
195
|
+
}
|
|
196
|
+
return [...byTab.values()];
|
|
197
|
+
}
|
|
198
|
+
function formatEntries(entries, workspaceByRef) {
|
|
199
|
+
const rows = [];
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
const workspaceParameter = workspaceByRef.get(objRefToString(entry.parameter.ref));
|
|
202
|
+
const row = formatDashboardParameter(entry, workspaceParameter);
|
|
203
|
+
if (row !== undefined) {
|
|
204
|
+
rows.push(row);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return rows;
|
|
208
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type IDashboardParameterValueOverride } from "@gooddata/sdk-backend-spi";
|
|
2
|
+
import { type IDashboardParameter, type IInsightParameterValue, type ObjRef } from "@gooddata/sdk-model";
|
|
2
3
|
import { type DashboardSelector } from "../../types.js";
|
|
3
4
|
import { type IDashboardParameterEntry } from "./parametersState.js";
|
|
4
5
|
/**
|
|
@@ -108,21 +109,25 @@ export declare const selectHasAnyResettableParameterOnActiveTab: DashboardSelect
|
|
|
108
109
|
*/
|
|
109
110
|
export declare const selectEffectiveParameterValuesForWidget: (ref: ObjRef | undefined) => DashboardSelector<IInsightParameterValue[]>;
|
|
110
111
|
/**
|
|
111
|
-
* Returns
|
|
112
|
-
*
|
|
112
|
+
* Returns the per-tab parameter overrides to send on dashboard tabular export, keyed by
|
|
113
|
+
* tab `localIdentifier`. The shape matches the backend's `dashboardTabsParametersOverrides`
|
|
114
|
+
* field directly.
|
|
113
115
|
*
|
|
114
|
-
* @
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
116
|
+
* @remarks
|
|
117
|
+
* Scope rules:
|
|
118
|
+
* - When `widgetIds` is empty/undefined (whole-dashboard export), every tab in scope contributes
|
|
119
|
+
* parameter entries with a `runtimeOverride`.
|
|
120
|
+
* - When `widgetIds` is non-empty, each widget's owning tab contributes only entries with a
|
|
121
|
+
* `runtimeOverride` whose ref is referenced by the widget's insight metrics via the dashboard-wide
|
|
122
|
+
* metric → parameter dependency map. Multi-widget across distinct owning tabs yields one
|
|
123
|
+
* map entry per owning tab; entries for the same tab are unioned and deduplicated by ref.
|
|
124
|
+
*
|
|
125
|
+
* Returns `{}` (signalling "omit the field on the wire") when:
|
|
126
|
+
* - `enableParameters` is off,
|
|
127
|
+
* - the workspace catalog parameters are not loaded,
|
|
128
|
+
* - (widget scope only) the `measureParameters` dependency map is not loaded,
|
|
129
|
+
* - or no in-scope parameter has a `runtimeOverride`.
|
|
122
130
|
*
|
|
123
|
-
* @
|
|
131
|
+
* @alpha
|
|
124
132
|
*/
|
|
125
|
-
export declare
|
|
126
|
-
ref: ObjRef;
|
|
127
|
-
value: number | undefined;
|
|
128
|
-
}[];
|
|
133
|
+
export declare const selectExportEffectiveParameters: (widgetIds: string[] | undefined) => DashboardSelector<Record<string, IDashboardParameterValueOverride[]>>;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { createSelector } from "@reduxjs/toolkit";
|
|
3
3
|
import { isEqual } from "lodash-es";
|
|
4
|
-
import {
|
|
4
|
+
import { areObjRefsEqual, insightParameters, objRefToString, } from "@gooddata/sdk-model";
|
|
5
5
|
import { createMemoizedSelector } from "../../_infra/selectors.js";
|
|
6
6
|
import { selectCatalogMeasureParameters, selectCatalogMeasureParametersStatus, selectCatalogParameterByRef, selectCatalogParameters, selectCatalogParametersIsLoaded, } from "../../catalog/catalogSelectors.js";
|
|
7
7
|
import { selectEnableParameters } from "../../config/configSelectors.js";
|
|
8
8
|
import { selectInsightsMap } from "../../insights/insightsSelectors.js";
|
|
9
9
|
import { selectIsInEditMode } from "../../renderMode/renderModeSelectors.js";
|
|
10
|
+
import { selectAllTabsInsightWidgetContexts } from "../layout/layoutSelectors.js";
|
|
10
11
|
import { selectActiveTab, selectTabs } from "../tabsSelectors.js";
|
|
11
12
|
import { DEFAULT_TAB_ID } from "../tabsState.js";
|
|
13
|
+
import { EMPTY_EXPORT_PARAMETERS, buildPersistedByTabAndRef, buildWidgetScopeTabRefSelections, collectExportOverrides, collectReferencedParameterRefs, computeParameterResetTargets, computeParameterResetValue, smartPersistResolvedEntry, } from "./parametersHelpers.js";
|
|
12
14
|
import { parametersInitialState, pickTabParametersSource, } from "./parametersState.js";
|
|
13
15
|
const EMPTY_PARAMETERS = [];
|
|
14
16
|
const EMPTY_PARAMETER_VALUES = [];
|
|
@@ -191,25 +193,19 @@ export const selectHasAnyResettableParameterOnActiveTab = createSelector(selectA
|
|
|
191
193
|
*
|
|
192
194
|
* @alpha
|
|
193
195
|
*/
|
|
194
|
-
export const selectEffectiveParameterValuesForWidget = createMemoizedSelector((ref) => createSelector(
|
|
195
|
-
if (!isEnabled || !
|
|
196
|
+
export const selectEffectiveParameterValuesForWidget = createMemoizedSelector((ref) => createSelector(selectAllTabsInsightWidgetContexts, selectInsightsMap, selectEnableParameters, selectCatalogMeasureParameters, selectCatalogMeasureParametersStatus, (contexts, insights, isEnabled, measureParameters, measureParametersStatus) => {
|
|
197
|
+
if (!isEnabled || !ref || measureParametersStatus !== "loaded") {
|
|
198
|
+
return EMPTY_PARAMETER_VALUES;
|
|
199
|
+
}
|
|
200
|
+
const context = contexts.find((ctx) => areObjRefsEqual(ctx.widget.ref, ref));
|
|
201
|
+
if (!context) {
|
|
196
202
|
return EMPTY_PARAMETER_VALUES;
|
|
197
203
|
}
|
|
198
204
|
const insight = insights.get(context.widget.insight);
|
|
199
205
|
if (!insight) {
|
|
200
206
|
return EMPTY_PARAMETER_VALUES;
|
|
201
207
|
}
|
|
202
|
-
const referencedRefs = new Set();
|
|
203
|
-
for (const measure of insightMeasures(insight)) {
|
|
204
|
-
const def = measure.measure.definition;
|
|
205
|
-
if (!isMeasureDefinition(def)) {
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
const parameterRefs = measureParameters[objRefToString(def.measureDefinition.item)] ?? [];
|
|
209
|
-
for (const parameterRef of parameterRefs) {
|
|
210
|
-
referencedRefs.add(objRefToString(parameterRef));
|
|
211
|
-
}
|
|
212
|
-
}
|
|
208
|
+
const referencedRefs = new Set(collectReferencedParameterRefs(insight, measureParameters).map(objRefToString));
|
|
213
209
|
const entries = context.tab.parameters?.parameters ?? parametersInitialState.parameters;
|
|
214
210
|
const result = [];
|
|
215
211
|
const seen = new Set();
|
|
@@ -234,121 +230,43 @@ export const selectEffectiveParameterValuesForWidget = createMemoizedSelector((r
|
|
|
234
230
|
}
|
|
235
231
|
return result.length === 0 ? EMPTY_PARAMETER_VALUES : result;
|
|
236
232
|
}));
|
|
237
|
-
const selectParameterExecutionContextByWidgetRef = createMemoizedSelector((ref) => createSelector(selectTabs, (tabs) => findParameterExecutionContext(tabs, ref)));
|
|
238
|
-
function findParameterExecutionContext(tabs, ref) {
|
|
239
|
-
if (!ref || !tabs) {
|
|
240
|
-
return undefined;
|
|
241
|
-
}
|
|
242
|
-
for (const tab of tabs) {
|
|
243
|
-
const layout = tab.layout?.layout;
|
|
244
|
-
if (!layout) {
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
const widget = findInsightWidgetInLayout(layout, ref);
|
|
248
|
-
if (widget) {
|
|
249
|
-
return { tab, widget };
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
function findInsightWidgetInLayout(layout, ref) {
|
|
255
|
-
for (const section of layout.sections) {
|
|
256
|
-
for (const item of section.items) {
|
|
257
|
-
const widget = item.widget;
|
|
258
|
-
if (!widget) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (isInsightWidget(widget) && areObjRefsEqual(widget.ref, ref)) {
|
|
262
|
-
return widget;
|
|
263
|
-
}
|
|
264
|
-
if (isDashboardLayout(widget)) {
|
|
265
|
-
const nestedWidget = findInsightWidgetInLayout(widget, ref);
|
|
266
|
-
if (nestedWidget) {
|
|
267
|
-
return nestedWidget;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (isVisualizationSwitcherWidget(widget)) {
|
|
271
|
-
const visualization = widget.visualizations.find((visualization) => areObjRefsEqual(visualization.ref, ref));
|
|
272
|
-
if (visualization) {
|
|
273
|
-
return visualization;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
function buildPersistedByTabAndRef(persistedTabs, rootPersistedParameters) {
|
|
281
|
-
const result = new Map();
|
|
282
|
-
for (const tab of persistedTabs) {
|
|
283
|
-
const sourceParameters = pickTabParametersSource(tab, persistedTabs, rootPersistedParameters) ?? EMPTY_PARAMETERS;
|
|
284
|
-
result.set(tab.localIdentifier, new Map(sourceParameters.map((parameter) => [objRefToString(parameter.ref), parameter])));
|
|
285
|
-
}
|
|
286
|
-
return result;
|
|
287
|
-
}
|
|
288
|
-
function smartPersistResolvedEntry(entry, workspaceParameter) {
|
|
289
|
-
const workspaceDefault = isNumberParameterDefinition(workspaceParameter.definition)
|
|
290
|
-
? workspaceParameter.definition.defaultValue
|
|
291
|
-
: undefined;
|
|
292
|
-
const result = {
|
|
293
|
-
ref: entry.parameter.ref,
|
|
294
|
-
parameterType: entry.parameter.parameterType,
|
|
295
|
-
mode: entry.parameter.mode,
|
|
296
|
-
...labelOverride(entry, workspaceParameter),
|
|
297
|
-
};
|
|
298
|
-
if (entry.runtimeOverride === undefined || entry.runtimeOverride === workspaceDefault) {
|
|
299
|
-
return result;
|
|
300
|
-
}
|
|
301
|
-
return { ...result, value: entry.runtimeOverride };
|
|
302
|
-
}
|
|
303
|
-
function labelOverride(entry, workspaceParameter) {
|
|
304
|
-
if (entry.parameter.label && entry.parameter.label !== workspaceParameter.title) {
|
|
305
|
-
return { label: entry.parameter.label };
|
|
306
|
-
}
|
|
307
|
-
return {};
|
|
308
|
-
}
|
|
309
233
|
/**
|
|
310
|
-
* Returns
|
|
311
|
-
*
|
|
234
|
+
* Returns the per-tab parameter overrides to send on dashboard tabular export, keyed by
|
|
235
|
+
* tab `localIdentifier`. The shape matches the backend's `dashboardTabsParametersOverrides`
|
|
236
|
+
* field directly.
|
|
312
237
|
*
|
|
313
|
-
* @
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (isInEditMode) {
|
|
322
|
-
if (dashboardOverride === undefined || dashboardOverride === workspaceDefault) {
|
|
323
|
-
return undefined;
|
|
324
|
-
}
|
|
325
|
-
return workspaceDefault;
|
|
326
|
-
}
|
|
327
|
-
return dashboardOverride ?? workspaceDefault;
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Only `mode: "active"` entries with a defined `runtimeOverride` are considered resettable;
|
|
331
|
-
* HIDDEN and READONLY entries are skipped, and entries with `runtimeOverride === undefined`
|
|
332
|
-
* (chip hidden, execution falls back to `insight.parameters`) are preserved as-is — symmetric
|
|
333
|
-
* with per-chip behavior in `DashboardParameterFilter`.
|
|
238
|
+
* @remarks
|
|
239
|
+
* Scope rules:
|
|
240
|
+
* - When `widgetIds` is empty/undefined (whole-dashboard export), every tab in scope contributes
|
|
241
|
+
* parameter entries with a `runtimeOverride`.
|
|
242
|
+
* - When `widgetIds` is non-empty, each widget's owning tab contributes only entries with a
|
|
243
|
+
* `runtimeOverride` whose ref is referenced by the widget's insight metrics via the dashboard-wide
|
|
244
|
+
* metric → parameter dependency map. Multi-widget across distinct owning tabs yields one
|
|
245
|
+
* map entry per owning tab; entries for the same tab are unioned and deduplicated by ref.
|
|
334
246
|
*
|
|
335
|
-
*
|
|
247
|
+
* Returns `{}` (signalling "omit the field on the wire") when:
|
|
248
|
+
* - `enableParameters` is off,
|
|
249
|
+
* - the workspace catalog parameters are not loaded,
|
|
250
|
+
* - (widget scope only) the `measureParameters` dependency map is not loaded,
|
|
251
|
+
* - or no in-scope parameter has a `runtimeOverride`.
|
|
252
|
+
*
|
|
253
|
+
* @alpha
|
|
336
254
|
*/
|
|
337
|
-
export
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
if (entry.runtimeOverride === undefined) {
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
const workspaceParameter = workspaceByRef.get(objRefToString(entry.parameter.ref));
|
|
348
|
-
const resetValue = computeParameterResetValue(entry, workspaceParameter, isInEditMode);
|
|
349
|
-
if (resetValue !== undefined && resetValue !== entry.runtimeOverride) {
|
|
350
|
-
result.push({ ref: entry.parameter.ref, value: resetValue });
|
|
351
|
-
}
|
|
255
|
+
export const selectExportEffectiveParameters = (widgetIds) => widgetIds?.length
|
|
256
|
+
? selectExportEffectiveParametersForWidgets(widgetIds)
|
|
257
|
+
: selectExportEffectiveParametersDashboardScope;
|
|
258
|
+
const selectExportEffectiveParametersDashboardScope = createSelector(selectTabs, selectEnableParameters, selectCatalogParameters, selectCatalogParametersIsLoaded, (tabs, isEnabled, catalogParameters, isCatalogLoaded) => {
|
|
259
|
+
if (!isEnabled || !isCatalogLoaded || !tabs?.length) {
|
|
260
|
+
return EMPTY_EXPORT_PARAMETERS;
|
|
352
261
|
}
|
|
353
|
-
|
|
354
|
-
}
|
|
262
|
+
const workspaceByRef = new Map(catalogParameters.map((parameter) => [objRefToString(parameter.ref), parameter]));
|
|
263
|
+
return collectExportOverrides(tabs.map((tab) => ({ tab })), workspaceByRef);
|
|
264
|
+
});
|
|
265
|
+
const selectExportEffectiveParametersForWidgets = createMemoizedSelector((widgetIds) => createSelector(selectAllTabsInsightWidgetContexts, selectInsightsMap, selectEnableParameters, selectCatalogParameters, selectCatalogParametersIsLoaded, selectCatalogMeasureParameters, selectCatalogMeasureParametersStatus, (widgetContexts, insights, isEnabled, catalogParameters, isCatalogLoaded, measureParameters, measureParametersStatus) => {
|
|
266
|
+
if (!isEnabled || !isCatalogLoaded || measureParametersStatus !== "loaded") {
|
|
267
|
+
return EMPTY_EXPORT_PARAMETERS;
|
|
268
|
+
}
|
|
269
|
+
const workspaceByRef = new Map(catalogParameters.map((parameter) => [objRefToString(parameter.ref), parameter]));
|
|
270
|
+
const tabRefSelections = buildWidgetScopeTabRefSelections(widgetContexts, widgetIds, insights, measureParameters);
|
|
271
|
+
return collectExportOverrides(tabRefSelections, workspaceByRef);
|
|
272
|
+
}));
|
|
@@ -98,6 +98,7 @@ import { IDashboardMeasureValueFilter } from '@gooddata/sdk-model';
|
|
|
98
98
|
import { IDashboardMeasureValueFilterConfig } from '@gooddata/sdk-model';
|
|
99
99
|
import { IDashboardObjectIdentity } from '@gooddata/sdk-model';
|
|
100
100
|
import { IDashboardParameter } from '@gooddata/sdk-model';
|
|
101
|
+
import { IDashboardParameterValueOverride } from '@gooddata/sdk-backend-spi';
|
|
101
102
|
import { IDashboardPermissions } from '@gooddata/sdk-model';
|
|
102
103
|
import { IDashboardReferences } from '@gooddata/sdk-backend-spi';
|
|
103
104
|
import { IDashboardWidget } from '@gooddata/sdk-model';
|
|
@@ -24093,6 +24094,30 @@ export declare const selectExecutionResultByRef: (ref: ObjRef | undefined) => Da
|
|
|
24093
24094
|
*/
|
|
24094
24095
|
export declare const selectExecutionTimestamp: DashboardSelector<string | undefined>;
|
|
24095
24096
|
|
|
24097
|
+
/**
|
|
24098
|
+
* Returns the per-tab parameter overrides to send on dashboard tabular export, keyed by
|
|
24099
|
+
* tab `localIdentifier`. The shape matches the backend's `dashboardTabsParametersOverrides`
|
|
24100
|
+
* field directly.
|
|
24101
|
+
*
|
|
24102
|
+
* @remarks
|
|
24103
|
+
* Scope rules:
|
|
24104
|
+
* - When `widgetIds` is empty/undefined (whole-dashboard export), every tab in scope contributes
|
|
24105
|
+
* parameter entries with a `runtimeOverride`.
|
|
24106
|
+
* - When `widgetIds` is non-empty, each widget's owning tab contributes only entries with a
|
|
24107
|
+
* `runtimeOverride` whose ref is referenced by the widget's insight metrics via the dashboard-wide
|
|
24108
|
+
* metric → parameter dependency map. Multi-widget across distinct owning tabs yields one
|
|
24109
|
+
* map entry per owning tab; entries for the same tab are unioned and deduplicated by ref.
|
|
24110
|
+
*
|
|
24111
|
+
* Returns `{}` (signalling "omit the field on the wire") when:
|
|
24112
|
+
* - `enableParameters` is off,
|
|
24113
|
+
* - the workspace catalog parameters are not loaded,
|
|
24114
|
+
* - (widget scope only) the `measureParameters` dependency map is not loaded,
|
|
24115
|
+
* - or no in-scope parameter has a `runtimeOverride`.
|
|
24116
|
+
*
|
|
24117
|
+
* @alpha
|
|
24118
|
+
*/
|
|
24119
|
+
export declare const selectExportEffectiveParameters: (widgetIds: string[] | undefined) => DashboardSelector<Record<string, IDashboardParameterValueOverride[]>>;
|
|
24120
|
+
|
|
24096
24121
|
/**
|
|
24097
24122
|
* Returns custom export metadata.
|
|
24098
24123
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-dashboard",
|
|
3
|
-
"version": "11.40.0-alpha.
|
|
3
|
+
"version": "11.40.0-alpha.1",
|
|
4
4
|
"description": "GoodData SDK - Dashboard Component",
|
|
5
5
|
"license": "LicenseRef-LICENSE",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -60,19 +60,19 @@
|
|
|
60
60
|
"ts-invariant": "0.10.3",
|
|
61
61
|
"tslib": "2.8.1",
|
|
62
62
|
"uuid": "11.1.0",
|
|
63
|
-
"@gooddata/sdk-backend-base": "11.40.0-alpha.
|
|
64
|
-
"@gooddata/sdk-backend-spi": "11.40.0-alpha.
|
|
65
|
-
"@gooddata/sdk-
|
|
66
|
-
"@gooddata/sdk-
|
|
67
|
-
"@gooddata/sdk-ui-charts": "11.40.0-alpha.
|
|
68
|
-
"@gooddata/sdk-ui-ext": "11.40.0-alpha.
|
|
69
|
-
"@gooddata/sdk-ui-
|
|
70
|
-
"@gooddata/sdk-ui-
|
|
71
|
-
"@gooddata/sdk-ui-
|
|
72
|
-
"@gooddata/sdk-ui-
|
|
73
|
-
"@gooddata/sdk-ui-
|
|
74
|
-
"@gooddata/util": "11.40.0-alpha.
|
|
75
|
-
"@gooddata/sdk-ui-
|
|
63
|
+
"@gooddata/sdk-backend-base": "11.40.0-alpha.1",
|
|
64
|
+
"@gooddata/sdk-backend-spi": "11.40.0-alpha.1",
|
|
65
|
+
"@gooddata/sdk-model": "11.40.0-alpha.1",
|
|
66
|
+
"@gooddata/sdk-ui": "11.40.0-alpha.1",
|
|
67
|
+
"@gooddata/sdk-ui-charts": "11.40.0-alpha.1",
|
|
68
|
+
"@gooddata/sdk-ui-ext": "11.40.0-alpha.1",
|
|
69
|
+
"@gooddata/sdk-ui-filters": "11.40.0-alpha.1",
|
|
70
|
+
"@gooddata/sdk-ui-geo": "11.40.0-alpha.1",
|
|
71
|
+
"@gooddata/sdk-ui-pivot": "11.40.0-alpha.1",
|
|
72
|
+
"@gooddata/sdk-ui-vis-commons": "11.40.0-alpha.1",
|
|
73
|
+
"@gooddata/sdk-ui-kit": "11.40.0-alpha.1",
|
|
74
|
+
"@gooddata/util": "11.40.0-alpha.1",
|
|
75
|
+
"@gooddata/sdk-ui-theme-provider": "11.40.0-alpha.1"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -118,12 +118,12 @@
|
|
|
118
118
|
"typescript": "5.9.3",
|
|
119
119
|
"vitest": "4.1.0",
|
|
120
120
|
"vitest-dom": "0.1.1",
|
|
121
|
-
"@gooddata/
|
|
122
|
-
"@gooddata/
|
|
123
|
-
"@gooddata/
|
|
124
|
-
"@gooddata/reference-workspace": "11.40.0-alpha.
|
|
125
|
-
"@gooddata/
|
|
126
|
-
"@gooddata/
|
|
121
|
+
"@gooddata/eslint-config": "11.40.0-alpha.1",
|
|
122
|
+
"@gooddata/i18n-toolkit": "11.40.0-alpha.1",
|
|
123
|
+
"@gooddata/oxlint-config": "11.40.0-alpha.1",
|
|
124
|
+
"@gooddata/reference-workspace": "11.40.0-alpha.1",
|
|
125
|
+
"@gooddata/sdk-backend-mockingbird": "11.40.0-alpha.1",
|
|
126
|
+
"@gooddata/stylelint-config": "11.40.0-alpha.1"
|
|
127
127
|
},
|
|
128
128
|
"peerDependencies": {
|
|
129
129
|
"react": "^18.0.0 || ^19.0.0",
|