@gooddata/sdk-ui-dashboard 11.39.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.
@@ -1,3 +1,3 @@
1
- export declare const LIB_VERSION = "11.39.0";
1
+ export declare const LIB_VERSION = "11.40.0-alpha.1";
2
2
  export declare const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
3
3
  export declare const LIB_NAME = "@gooddata/sdk-ui-dashboard";
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.39.0";
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
- function exportDashboardToTabular(ctx, dashboardRef, title, mergeHeaders, exportInfo, widgetIds, dashboardFiltersOverride, filtersByTab, format, pdfConfiguration, timeout) {
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 { selectAttributeFilterConfigsOverrides } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
24
- import { selectDateFilterConfigOverrides } from "../../store/tabs/dateFilterConfig/dateFilterConfigSelectors.js";
25
- import { selectDateFilterConfigsOverrides } from "../../store/tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
26
- import { selectFilterContextDefinition, selectFilterContextIdentity, } from "../../store/tabs/filterContext/filterContextSelectors.js";
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, selectBasicLayout } from "../../store/tabs/layout/layoutSelectors.js";
29
- import { selectMeasureValueFilterConfigsOverrides } from "../../store/tabs/measureValueFilterConfigs/measureValueFilterConfigsSelectors.js";
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
- ...filterContextIdentity,
200
- ...filterContextDefinition,
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 IDashboardParameter, type IInsightParameterValue, type IParameterMetadataObject, type ObjRef } from "@gooddata/sdk-model";
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 `undefined` when reset would be a no-op: missing/non-number workspace parameter, or
112
- * in edit mode when `parameter.value` is unset / already equals the workspace default.
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
- * @internal
115
- */
116
- export declare function computeParameterResetValue(entry: IDashboardParameterEntry, workspaceParameter: IParameterMetadataObject | undefined, isInEditMode: boolean): number | undefined;
117
- /**
118
- * Only `mode: "active"` entries with a defined `runtimeOverride` are considered resettable;
119
- * HIDDEN and READONLY entries are skipped, and entries with `runtimeOverride === undefined`
120
- * (chip hidden, execution falls back to `insight.parameters`) are preserved as-is — symmetric
121
- * with per-chip behavior in `DashboardParameterFilter`.
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
- * @internal
131
+ * @alpha
124
132
  */
125
- export declare function computeParameterResetTargets(entries: IDashboardParameterEntry[], workspaceParameters: IParameterMetadataObject[], isInEditMode: boolean): {
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 { DashboardParameterModeValues, areObjRefsEqual, insightMeasures, insightParameters, isDashboardLayout, isInsightWidget, isMeasureDefinition, isNumberParameterDefinition, isVisualizationSwitcherWidget, objRefToString, } from "@gooddata/sdk-model";
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(selectParameterExecutionContextByWidgetRef(ref), selectInsightsMap, selectEnableParameters, selectCatalogMeasureParameters, selectCatalogMeasureParametersStatus, (context, insights, isEnabled, measureParameters, measureParametersStatus) => {
195
- if (!isEnabled || !context || measureParametersStatus !== "loaded") {
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 `undefined` when reset would be a no-op: missing/non-number workspace parameter, or
311
- * in edit mode when `parameter.value` is unset / already equals the workspace default.
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
- * @internal
314
- */
315
- export function computeParameterResetValue(entry, workspaceParameter, isInEditMode) {
316
- if (!workspaceParameter || !isNumberParameterDefinition(workspaceParameter.definition)) {
317
- return undefined;
318
- }
319
- const workspaceDefault = workspaceParameter.definition.defaultValue;
320
- const dashboardOverride = entry.parameter.value;
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
- * @internal
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 function computeParameterResetTargets(entries, workspaceParameters, isInEditMode) {
338
- const workspaceByRef = new Map(workspaceParameters.map((wp) => [objRefToString(wp.ref), wp]));
339
- const result = [];
340
- for (const entry of entries) {
341
- if (entry.parameter.mode !== DashboardParameterModeValues.ACTIVE) {
342
- continue;
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
- return result;
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.39.0",
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.39.0",
64
- "@gooddata/sdk-backend-spi": "11.39.0",
65
- "@gooddata/sdk-ui": "11.39.0",
66
- "@gooddata/sdk-ui-charts": "11.39.0",
67
- "@gooddata/sdk-model": "11.39.0",
68
- "@gooddata/sdk-ui-ext": "11.39.0",
69
- "@gooddata/sdk-ui-filters": "11.39.0",
70
- "@gooddata/sdk-ui-geo": "11.39.0",
71
- "@gooddata/sdk-ui-kit": "11.39.0",
72
- "@gooddata/sdk-ui-pivot": "11.39.0",
73
- "@gooddata/sdk-ui-theme-provider": "11.39.0",
74
- "@gooddata/sdk-ui-vis-commons": "11.39.0",
75
- "@gooddata/util": "11.39.0"
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/eslint-config": "11.39.0",
122
- "@gooddata/i18n-toolkit": "11.39.0",
123
- "@gooddata/oxlint-config": "11.39.0",
124
- "@gooddata/stylelint-config": "11.39.0",
125
- "@gooddata/reference-workspace": "11.39.0",
126
- "@gooddata/sdk-backend-mockingbird": "11.39.0"
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",