@gooddata/sdk-ui-dashboard 11.36.0-alpha.1 → 11.36.0-alpha.2
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/_staging/drills/drillingUtils.d.ts +9 -0
- package/esm/_staging/drills/drillingUtils.js +19 -1
- package/esm/index.d.ts +2 -2
- package/esm/index.js +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.js +9 -2
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/loadMeasureParameterDependencies.d.ts +12 -0
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/loadMeasureParameterDependencies.js +98 -0
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.js +1 -0
- package/esm/model/commandHandlers/drill/common/mergeFilters.d.ts +3 -3
- package/esm/model/commandHandlers/drill/common/mergeFilters.js +26 -1
- package/esm/model/commandHandlers/drill/common/sourceDrillFilters.d.ts +2 -1
- package/esm/model/commandHandlers/drill/common/sourceDrillFilters.js +25 -5
- package/esm/model/commandHandlers/drill/drillToDashboardHandler.js +17 -16
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +7 -4
- package/esm/model/commandHandlers/filterContext/measureValueFilter/removeMeasureValueFilterHandler.js +7 -0
- package/esm/model/react/useWidgetFilters.js +4 -1
- package/esm/model/store/catalog/catalogReducers.d.ts +5 -1
- package/esm/model/store/catalog/catalogReducers.js +4 -0
- package/esm/model/store/catalog/catalogSelectors.d.ts +15 -2
- package/esm/model/store/catalog/catalogSelectors.js +13 -0
- package/esm/model/store/catalog/catalogState.d.ts +20 -1
- package/esm/model/store/catalog/catalogState.js +1 -0
- package/esm/model/store/catalog/index.d.ts +1 -0
- package/esm/model/store/tabs/index.d.ts +6 -0
- package/esm/model/store/tabs/layout/layoutReducers.d.ts +4 -0
- package/esm/model/store/tabs/layout/layoutReducers.js +25 -0
- package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +19 -6
- package/esm/model/store/tabs/parameters/parametersSelectors.js +69 -16
- package/esm/presentation/filterBar/parameterFilter/DashboardParameterFilter.js +3 -2
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +8 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.js +8 -0
- package/esm/presentation/widget/common/configuration/FilterConfiguration.js +4 -2
- package/esm/presentation/widget/common/configuration/useMeasureValueFilterCompatibility.d.ts +6 -2
- package/esm/presentation/widget/common/configuration/useMeasureValueFilterCompatibility.js +95 -22
- package/esm/presentation/widget/insight/ViewModeDashboardInsight/InsightDrillDialog/useExcludedDrillDefinitionFilters.js +2 -2
- package/esm/presentation/widget/insight/configuration/DrillFilters/TargetDashboardFiltersContext.d.ts +2 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/TargetDashboardFiltersContext.js +3 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/drillFiltersConfigUtils.d.ts +4 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/drillFiltersConfigUtils.js +15 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/messages.d.ts +6 -0
- package/esm/presentation/widget/insight/configuration/DrillFilters/messages.js +6 -0
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapDashboardFilterToOption.d.ts +6 -2
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapDashboardFilterToOption.js +18 -3
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceInsightFilterToOption.d.ts +4 -2
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceInsightFilterToOption.js +10 -6
- package/esm/presentation/widget/insight/configuration/DrillFilters/types.d.ts +2 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/useDrillFiltersConfig.js +78 -6
- package/esm/presentation/widget/insight/configuration/DrillFilters/useFetchTargetDashboardFilters.d.ts +2 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/useFetchTargetDashboardFilters.js +4 -1
- package/esm/sdk-ui-dashboard.d.ts +53 -6
- 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.36.0-alpha.
|
|
3
|
+
export const LIB_VERSION = "11.36.0-alpha.2";
|
|
4
4
|
export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
|
|
5
5
|
export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
|
|
@@ -40,3 +40,12 @@ export declare function isMatchingSourceInsightFilter(filter: IFilter, sourceFil
|
|
|
40
40
|
export declare function isSourceInsightFilterObjRefEqual(left: SourceInsightFilterObjRef, right: SourceInsightFilterObjRef): boolean;
|
|
41
41
|
export declare function sourceInsightFilterObjRef(filter: IFilter): SourceInsightFilterObjRef | undefined;
|
|
42
42
|
export declare function sourceInsightFilterObjRefValue(sourceFilterObjRef: SourceInsightFilterObjRef): ObjRefInScope;
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a measure-scoped ref from an insight filter to the underlying catalog measure ref.
|
|
45
|
+
*
|
|
46
|
+
* Source insight MVFs usually target a local measure identifier, while dashboard MVFs target
|
|
47
|
+
* the metric object ref. Use this when comparing or merging source MVFs with dashboard MVFs.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveSourceMeasureRef(measureRef: ObjRefInScope | undefined, sourceInsightMeasures: IMeasure[]): ObjRef | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// (C) 2020-2026 GoodData Corporation
|
|
2
2
|
import { isEqual } from "lodash-es";
|
|
3
|
-
import { areObjRefsEqual, drillDownReferenceHierarchyRef, filterMeasureRef, filterObjRef, isAttributeFilter, isCrossFiltering, isDateFilter, isDrillFromAttribute, isDrillFromMeasure, isDrillToLegacyDashboard, isIdentifierRef, isKeyDriveAnalysis, isLocalIdRef, isMeasureDescriptor, isMeasureValueFilter, isRankingFilter, measureFilters, measureLocalId, } from "@gooddata/sdk-model";
|
|
3
|
+
import { areObjRefsEqual, drillDownReferenceHierarchyRef, filterMeasureRef, filterObjRef, isAttributeFilter, isCrossFiltering, isDateFilter, isDrillFromAttribute, isDrillFromMeasure, isDrillToLegacyDashboard, isIdentifierRef, isKeyDriveAnalysis, isLocalIdRef, isMeasureDescriptor, isMeasureValueFilter, isRankingFilter, measureFilters, measureItem, measureLocalId, } from "@gooddata/sdk-model";
|
|
4
4
|
import { getMappingHeaderLocalIdentifier, } from "@gooddata/sdk-ui";
|
|
5
5
|
export function getDrillsBySourceLocalIdentifiers(widgetDrillDefinition, drillSourceLocalIdentifiers) {
|
|
6
6
|
return widgetDrillDefinition.filter((d) => isDrillToLegacyDashboard(d) ||
|
|
@@ -185,3 +185,21 @@ export function sourceInsightFilterObjRefValue(sourceFilterObjRef) {
|
|
|
185
185
|
}
|
|
186
186
|
throw new Error("Invalid SourceInsightFilterObjRef");
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Resolves a measure-scoped ref from an insight filter to the underlying catalog measure ref.
|
|
190
|
+
*
|
|
191
|
+
* Source insight MVFs usually target a local measure identifier, while dashboard MVFs target
|
|
192
|
+
* the metric object ref. Use this when comparing or merging source MVFs with dashboard MVFs.
|
|
193
|
+
*
|
|
194
|
+
* @internal
|
|
195
|
+
*/
|
|
196
|
+
export function resolveSourceMeasureRef(measureRef, sourceInsightMeasures) {
|
|
197
|
+
if (!measureRef) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
if (!isLocalIdRef(measureRef)) {
|
|
201
|
+
return measureRef;
|
|
202
|
+
}
|
|
203
|
+
const sourceMeasure = sourceInsightMeasures.find((measure) => measureLocalId(measure) === measureRef.localIdentifier);
|
|
204
|
+
return sourceMeasure ? measureItem(sourceMeasure) : undefined;
|
|
205
|
+
}
|
package/esm/index.d.ts
CHANGED
|
@@ -138,8 +138,8 @@ export type { FilterContextState, WorkingDashboardAttributeFilter, WorkingDashbo
|
|
|
138
138
|
export { selectFilterGroupsConfig } from "./model/store/tabs/filterGroups/filterGroupsSelectors.js";
|
|
139
139
|
export { selectDateFilterConfigsOverrides, selectDateFilterConfigsOverridesByTab, selectDateFilterConfigsModeMap, selectEffectiveDateFiltersModeMap, selectDateFilterConfigsModeMapByTab, } from "./model/store/tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
|
|
140
140
|
export { selectInsights, selectInsightRefs, selectInsightsMap, selectInsightByRef, selectInsightByWidgetRef, selectRawExportOverridesForInsightByRef, } from "./model/store/insights/insightsSelectors.js";
|
|
141
|
-
export type { CatalogState, CatalogParametersStatus, ICatalogParametersState, } from "./model/store/catalog/catalogState.js";
|
|
142
|
-
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, } from "./model/store/catalog/catalogSelectors.js";
|
|
141
|
+
export type { CatalogState, CatalogParametersStatus, ICatalogParametersState, CatalogMeasureParametersStatus, ICatalogMeasureParametersState, } from "./model/store/catalog/catalogState.js";
|
|
142
|
+
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";
|
|
143
143
|
export { catalogActions } from "./model/store/catalog/index.js";
|
|
144
144
|
export type { SetCatalogMeasuresAndFactsPayload, SetCatalogItemsPayload, } from "./model/store/catalog/catalogReducers.js";
|
|
145
145
|
export type { IAddParameterPayload, IRemoveParameterPayload, ISetParameterRuntimeValuePayload, } from "./model/store/tabs/parameters/parametersReducers.js";
|
package/esm/index.js
CHANGED
|
@@ -103,7 +103,7 @@ export { DEFAULT_TAB_ID } from "./model/store/tabs/tabsState.js";
|
|
|
103
103
|
export { selectFilterGroupsConfig } from "./model/store/tabs/filterGroups/filterGroupsSelectors.js";
|
|
104
104
|
export { selectDateFilterConfigsOverrides, selectDateFilterConfigsOverridesByTab, selectDateFilterConfigsModeMap, selectEffectiveDateFiltersModeMap, selectDateFilterConfigsModeMapByTab, } from "./model/store/tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
|
|
105
105
|
export { selectInsights, selectInsightRefs, selectInsightsMap, selectInsightByRef, selectInsightByWidgetRef, selectRawExportOverridesForInsightByRef, } from "./model/store/insights/insightsSelectors.js";
|
|
106
|
-
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, } from "./model/store/catalog/catalogSelectors.js";
|
|
106
|
+
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";
|
|
107
107
|
export { catalogActions } from "./model/store/catalog/index.js";
|
|
108
108
|
export { selectDashboardParameterEntries, selectDashboardParameters, selectEffectiveParameterValuesForWidget, selectIsParametersChanged, selectParameterRuntimeOverrideByRef, } from "./model/store/tabs/parameters/parametersSelectors.js";
|
|
109
109
|
export { drillActions } from "./model/store/drill/index.js";
|
|
@@ -33,6 +33,7 @@ import { loadDashboardParameters } from "./loadDashboardParameters.js";
|
|
|
33
33
|
import { loadDashboardPermissions } from "./loadDashboardPermissions.js";
|
|
34
34
|
import { loadDateHierarchyTemplates } from "./loadDateHierarchyTemplates.js";
|
|
35
35
|
import { loadFilterViews } from "./loadFilterViews.js";
|
|
36
|
+
import { loadMeasureParameterDependencies } from "./loadMeasureParameterDependencies.js";
|
|
36
37
|
import { loadUser } from "./loadUser.js";
|
|
37
38
|
import { mergeDateFilterConfigWithOverrides } from "./mergeDateFilterConfigs.js";
|
|
38
39
|
import { preloadAttributeFiltersData as preloadAttributeFiltersDataFromBackend } from "./preloadAttributeFiltersData.js";
|
|
@@ -180,8 +181,11 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
180
181
|
// Note: activeTabLocalIdentifier is app state only, NOT persisted to/read from dashboard MD
|
|
181
182
|
const resolvedActiveTabId = resolveActiveTabId(dashboardWithFilterView.tabs, cmd.payload.initialTabId, undefined);
|
|
182
183
|
const dashboard = dashboardWithFilterView;
|
|
183
|
-
const { tabsAttributeFilterConfigs, tabsDateFilterConfig, tabsDateFilterConfigSource }
|
|
184
|
-
|
|
184
|
+
const [{ tabsAttributeFilterConfigs, tabsDateFilterConfig, tabsDateFilterConfigSource }, workspaceParameters, measureParameterDependencies,] = yield all([
|
|
185
|
+
call(getTabsFilterConfigs, dashboard, config, ctx, cmd),
|
|
186
|
+
call(loadWorkspaceParametersWithStatus, ctx, config.settings?.enableParameters ?? false),
|
|
187
|
+
call(loadMeasureParameterDependencies, ctx, insights, config.settings?.enableParameters ?? false),
|
|
188
|
+
]);
|
|
185
189
|
const workspaceParametersList = Array.isArray(workspaceParameters) ? workspaceParameters : [];
|
|
186
190
|
const initActions = yield call(actionsToInitializeExistingDashboard, ctx, dashboard, insights, config.settings, config.settings.enableImmediateAttributeFilterDisplayAsLabelMigration ?? false, undefined, undefined, tabsAttributeFilterConfigs, tabsDateFilterConfig, tabsDateFilterConfigSource, createDisplayFormMap([], []), cmd.payload.persistedDashboard, resolvedActiveTabId, workspaceParametersList);
|
|
187
191
|
const catalogPayload = {
|
|
@@ -195,6 +199,7 @@ function* loadExistingDashboard(ctx, cmd, dashboardRef) {
|
|
|
195
199
|
permissionsActions.setPermissions(permissions),
|
|
196
200
|
catalogActions.setCatalogItems(catalogPayload),
|
|
197
201
|
catalogActions.setCatalogParameters(makeCatalogParametersPayload(workspaceParameters)),
|
|
202
|
+
catalogActions.setCatalogMeasureParameters(measureParameterDependencies),
|
|
198
203
|
...initActions,
|
|
199
204
|
// NOTE: Tab configs (dateFilterConfig, dateFilterConfigs, attributeFilterConfigs, filterContext)
|
|
200
205
|
// are now initialized as part of the tabs state in initActions via setTabs action
|
|
@@ -229,6 +234,7 @@ function* initializeNewDashboard(ctx, cmd) {
|
|
|
229
234
|
const workspaceParameters = yield call(loadWorkspaceParametersWithStatus, ctx, config.settings?.enableParameters ?? false);
|
|
230
235
|
const workspaceParametersList = Array.isArray(workspaceParameters) ? workspaceParameters : [];
|
|
231
236
|
const { initActions, dashboard, insights } = yield call(actionsToInitializeNewDashboard, ctx, config.settings, config.dateFilterConfig, catalog ? createDisplayFormMapFromCatalog(catalog) : createDisplayFormMap([], []), cmd.payload.initialTabId, workspaceParametersList);
|
|
237
|
+
const measureParameterDependencies = yield call(loadMeasureParameterDependencies, ctx, insights, config.settings?.enableParameters ?? false);
|
|
232
238
|
const batch = batchActions([
|
|
233
239
|
backendCapabilitiesActions.setBackendCapabilities(backend.capabilities),
|
|
234
240
|
configActions.setConfig(config),
|
|
@@ -244,6 +250,7 @@ function* initializeNewDashboard(ctx, cmd) {
|
|
|
244
250
|
dateHierarchyTemplates: dateHierarchyTemplates,
|
|
245
251
|
}),
|
|
246
252
|
catalogActions.setCatalogParameters(makeCatalogParametersPayload(workspaceParameters)),
|
|
253
|
+
catalogActions.setCatalogMeasureParameters(measureParameterDependencies),
|
|
247
254
|
listedDashboardsActions.setListedDashboards(listedDashboards),
|
|
248
255
|
accessibleDashboardsActions.setAccessibleDashboards(listedDashboards),
|
|
249
256
|
executionResultsActions.clearAllExecutionResults(),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type IInsight } from "@gooddata/sdk-model";
|
|
2
|
+
import { type ICatalogMeasureParametersState } from "../../../store/catalog/catalogState.js";
|
|
3
|
+
import { type DashboardContext } from "../../../types/commonTypes.js";
|
|
4
|
+
/**
|
|
5
|
+
* Loads the dashboard-wide metric -> parameter dependency map from the workspace references service.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Walks the dependency graph in `direction: "down"`, then collects parameter nodes reachable from
|
|
9
|
+
* each root metric, including transitive metric -> metric -> parameter chains. Disabled parameters
|
|
10
|
+
* return `uninitialized`; backend errors return `failed`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadMeasureParameterDependencies(ctx: DashboardContext, insights: IInsight[], enableParameters: boolean): Promise<ICatalogMeasureParametersState>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { insightMeasures, isIdentifierRef, isMeasureDefinition, objRefToString, } from "@gooddata/sdk-model";
|
|
3
|
+
/**
|
|
4
|
+
* Loads the dashboard-wide metric -> parameter dependency map from the workspace references service.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Walks the dependency graph in `direction: "down"`, then collects parameter nodes reachable from
|
|
8
|
+
* each root metric, including transitive metric -> metric -> parameter chains. Disabled parameters
|
|
9
|
+
* return `uninitialized`; backend errors return `failed`.
|
|
10
|
+
*/
|
|
11
|
+
export async function loadMeasureParameterDependencies(ctx, insights, enableParameters) {
|
|
12
|
+
if (!enableParameters) {
|
|
13
|
+
return { status: "uninitialized", byMetric: {} };
|
|
14
|
+
}
|
|
15
|
+
const metricRefs = collectMetricRefs(insights);
|
|
16
|
+
if (metricRefs.length === 0) {
|
|
17
|
+
return { status: "loaded", byMetric: {} };
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const result = await ctx.backend
|
|
21
|
+
.workspace(ctx.workspace)
|
|
22
|
+
.references()
|
|
23
|
+
.getReferences(metricRefs, { direction: "down" });
|
|
24
|
+
const byMetric = buildReachableParameterMap(metricRefs, result.edges);
|
|
25
|
+
return { status: "loaded", byMetric };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { status: "failed", byMetric: {} };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function collectMetricRefs(insights) {
|
|
32
|
+
const seen = new Set();
|
|
33
|
+
const refs = [];
|
|
34
|
+
for (const insight of insights) {
|
|
35
|
+
for (const measure of insightMeasures(insight)) {
|
|
36
|
+
const def = measure.measure.definition;
|
|
37
|
+
if (!isMeasureDefinition(def)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const item = def.measureDefinition.item;
|
|
41
|
+
if (!isIdentifierRef(item)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const key = objRefToString(item);
|
|
45
|
+
if (seen.has(key)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
seen.add(key);
|
|
49
|
+
refs.push(item);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return refs;
|
|
53
|
+
}
|
|
54
|
+
function buildReachableParameterMap(roots, edges) {
|
|
55
|
+
const adjacency = buildAdjacency(edges);
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const root of roots) {
|
|
58
|
+
const reachableParameters = reachableParameterRefs(root, adjacency);
|
|
59
|
+
result[objRefToString(root)] = reachableParameters;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
function buildAdjacency(edges) {
|
|
64
|
+
const adjacency = new Map();
|
|
65
|
+
for (const edge of edges) {
|
|
66
|
+
const fromKey = objRefToString(edge.from);
|
|
67
|
+
const toKey = objRefToString(edge.to);
|
|
68
|
+
const next = adjacency.get(fromKey) ?? [];
|
|
69
|
+
next.push({ to: edge.to, key: toKey });
|
|
70
|
+
adjacency.set(fromKey, next);
|
|
71
|
+
}
|
|
72
|
+
return adjacency;
|
|
73
|
+
}
|
|
74
|
+
function reachableParameterRefs(start, adjacency) {
|
|
75
|
+
const parameters = [];
|
|
76
|
+
const parametersSeen = new Set();
|
|
77
|
+
const visited = new Set([objRefToString(start)]);
|
|
78
|
+
const queue = [start];
|
|
79
|
+
while (queue.length > 0) {
|
|
80
|
+
const node = queue.shift();
|
|
81
|
+
const neighbors = adjacency.get(objRefToString(node)) ?? [];
|
|
82
|
+
for (const neighbor of neighbors) {
|
|
83
|
+
if (visited.has(neighbor.key)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
visited.add(neighbor.key);
|
|
87
|
+
if (neighbor.to.type === "parameter") {
|
|
88
|
+
if (!parametersSeen.has(neighbor.key)) {
|
|
89
|
+
parametersSeen.add(neighbor.key);
|
|
90
|
+
parameters.push(neighbor.to);
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
queue.push(neighbor.to);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return parameters;
|
|
98
|
+
}
|
package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.js
CHANGED
|
@@ -135,6 +135,7 @@ export function* resolveDashboardConfig(ctx, cmd) {
|
|
|
135
135
|
maxZoomLevel: config.maxZoomLevel === undefined
|
|
136
136
|
? settings.settings?.maxZoomLevel
|
|
137
137
|
: config.maxZoomLevel,
|
|
138
|
+
isWhiteLabeled: config.isWhiteLabeled ?? settings.settings?.whiteLabeling?.enabled ?? false,
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
141
|
const CONFIG_DEFAULTS = {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type FilterContextItem, type IAttributeFilter, type IDateFilter } from "@gooddata/sdk-model";
|
|
2
|
-
type SourceFilter = IAttributeFilter | IDateFilter;
|
|
1
|
+
import { type FilterContextItem, type IAttributeFilter, type IDateFilter, type IMeasureValueFilter } from "@gooddata/sdk-model";
|
|
2
|
+
export type SourceFilter = IAttributeFilter | IDateFilter | IMeasureValueFilter;
|
|
3
3
|
/**
|
|
4
4
|
* Merges dashboard filters with source filters by intersecting attribute element values
|
|
5
5
|
* for filters that target the same attribute (matched by display form ref).
|
|
6
6
|
*
|
|
7
7
|
* - Matching attribute filters: element values are intersected (AND semantics).
|
|
8
|
+
* - Matching measure value filters: conditions get replaced.
|
|
8
9
|
* - Unmatched filters from either side: passed through as-is.
|
|
9
10
|
* - Date filters: passed through as-is (no intersection).
|
|
10
11
|
*/
|
|
11
12
|
export declare function mergeDashboardAndSourceFilters(dashboardFilters: FilterContextItem[], sourceFilters: SourceFilter[]): Array<FilterContextItem | SourceFilter>;
|
|
12
|
-
export {};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
|
-
import { areObjRefsEqual, filterAttributeElements, filterObjRef, isAttributeFilterWithSelection, isDashboardAttributeFilter, isNegativeAttributeFilter, } from "@gooddata/sdk-model";
|
|
2
|
+
import { areObjRefsEqual, filterAttributeElements, filterMeasureRef, filterObjRef, isAttributeFilterWithSelection, isDashboardAttributeFilter, isDashboardMeasureValueFilter, isMeasureValueFilter, isNegativeAttributeFilter, measureValueFilterConditions, } from "@gooddata/sdk-model";
|
|
3
3
|
/**
|
|
4
4
|
* Merges dashboard filters with source filters by intersecting attribute element values
|
|
5
5
|
* for filters that target the same attribute (matched by display form ref).
|
|
6
6
|
*
|
|
7
7
|
* - Matching attribute filters: element values are intersected (AND semantics).
|
|
8
|
+
* - Matching measure value filters: conditions get replaced.
|
|
8
9
|
* - Unmatched filters from either side: passed through as-is.
|
|
9
10
|
* - Date filters: passed through as-is (no intersection).
|
|
10
11
|
*/
|
|
@@ -14,6 +15,22 @@ export function mergeDashboardAndSourceFilters(dashboardFilters, sourceFilters)
|
|
|
14
15
|
}
|
|
15
16
|
const usedSourceIndices = new Set();
|
|
16
17
|
const mergedDashboardFilters = dashboardFilters.map((dashFilter) => {
|
|
18
|
+
if (isDashboardMeasureValueFilter(dashFilter)) {
|
|
19
|
+
const dashRef = dashFilter.dashboardMeasureValueFilter.measure;
|
|
20
|
+
const sourceIndex = sourceFilters.findIndex((sf, i) => {
|
|
21
|
+
if (usedSourceIndices.has(i) || !isMeasureValueFilter(sf)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const sourceRef = filterMeasureRef(sf);
|
|
25
|
+
return sourceRef ? areObjRefsEqual(dashRef, sourceRef) : false;
|
|
26
|
+
});
|
|
27
|
+
if (sourceIndex === -1) {
|
|
28
|
+
return dashFilter;
|
|
29
|
+
}
|
|
30
|
+
usedSourceIndices.add(sourceIndex);
|
|
31
|
+
const sourceFilter = sourceFilters[sourceIndex];
|
|
32
|
+
return intersectMeasureValueFilters(dashFilter, sourceFilter);
|
|
33
|
+
}
|
|
17
34
|
if (!isDashboardAttributeFilter(dashFilter)) {
|
|
18
35
|
return dashFilter;
|
|
19
36
|
}
|
|
@@ -35,6 +52,14 @@ export function mergeDashboardAndSourceFilters(dashboardFilters, sourceFilters)
|
|
|
35
52
|
const remainingSourceFilters = sourceFilters.filter((_, i) => !usedSourceIndices.has(i));
|
|
36
53
|
return [...mergedDashboardFilters, ...remainingSourceFilters];
|
|
37
54
|
}
|
|
55
|
+
function intersectMeasureValueFilters(dashFilter, sourceFilter) {
|
|
56
|
+
return {
|
|
57
|
+
dashboardMeasureValueFilter: {
|
|
58
|
+
...dashFilter.dashboardMeasureValueFilter,
|
|
59
|
+
conditions: measureValueFilterConditions(sourceFilter),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
38
63
|
function intersectAttributeFilters(dashFilter, sourceFilter) {
|
|
39
64
|
const dashElements = dashFilter.attributeFilter.attributeElements;
|
|
40
65
|
const dashNegative = dashFilter.attributeFilter.negativeSelection;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type IAttributeFilter, type IDateFilter, type IDrillToDashboard, type IDrillToInsight, type IFilter, type IInsight, type SourceInsightFilterObjRef } from "@gooddata/sdk-model";
|
|
2
|
+
import { type SourceFilter } from "./mergeFilters.js";
|
|
2
3
|
type IDrillDefinitionWithIncludedSourceFilters = Pick<IDrillToDashboard | IDrillToInsight, "origin" | "includedSourceInsightFiltersObjRefs" | "includedSourceMeasureFiltersObjRefs">;
|
|
3
4
|
export declare function getIncludedSourceInsightFilters(sourceInsight: IInsight | null, includedSourceInsightFiltersObjRefs: SourceInsightFilterObjRef[]): IFilter[];
|
|
4
5
|
export declare function getIncludedSourceMeasureFilters(sourceInsight: IInsight | null, drillDefinition: IDrillDefinitionWithIncludedSourceFilters): Array<IAttributeFilter | IDateFilter>;
|
|
5
|
-
export declare function getIncludedSourceFiltersForDashboard(sourceInsight: IInsight | null, drillDefinition: IDrillDefinitionWithIncludedSourceFilters): Array<
|
|
6
|
+
export declare function getIncludedSourceFiltersForDashboard(sourceInsight: IInsight | null, drillDefinition: IDrillDefinitionWithIncludedSourceFilters): Array<SourceFilter>;
|
|
6
7
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
|
-
import { insightFilters, insightMeasures, isAttributeFilter, isDateFilter, isMeasureValueFilter, isRankingFilter, } from "@gooddata/sdk-model";
|
|
3
|
-
import { getSourceMeasureFiltersForDrillDefinition, isMatchingSourceInsightFilter, } from "../../../../_staging/drills/drillingUtils.js";
|
|
2
|
+
import { filterMeasureRef, insightFilters, insightMeasures, isAttributeFilter, isDateFilter, isMeasureValueFilter, isRankingFilter, } from "@gooddata/sdk-model";
|
|
3
|
+
import { getSourceMeasureFiltersForDrillDefinition, isMatchingSourceInsightFilter, resolveSourceMeasureRef, } from "../../../../_staging/drills/drillingUtils.js";
|
|
4
4
|
export function getIncludedSourceInsightFilters(sourceInsight, includedSourceInsightFiltersObjRefs) {
|
|
5
5
|
const sourceFilters = sourceInsight
|
|
6
6
|
? insightFilters(sourceInsight).filter((filter) => isAttributeFilter(filter) ||
|
|
@@ -18,13 +18,33 @@ export function getIncludedSourceMeasureFilters(sourceInsight, drillDefinition)
|
|
|
18
18
|
return includedSourceFilters(sourceMeasureFilters, drillDefinition.includedSourceMeasureFiltersObjRefs ?? []);
|
|
19
19
|
}
|
|
20
20
|
export function getIncludedSourceFiltersForDashboard(sourceInsight, drillDefinition) {
|
|
21
|
+
const sourceInsightMeasures = sourceInsight ? insightMeasures(sourceInsight) : [];
|
|
21
22
|
const sourceMeasureFilters = getIncludedSourceMeasureFilters(sourceInsight, drillDefinition);
|
|
22
|
-
const sourceInsightFilters = getIncludedSourceInsightFilters(sourceInsight, drillDefinition.includedSourceInsightFiltersObjRefs ?? [])
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const sourceInsightFilters = getIncludedSourceInsightFilters(sourceInsight, drillDefinition.includedSourceInsightFiltersObjRefs ?? [])
|
|
24
|
+
.filter((filter) => {
|
|
25
|
+
return isAttributeFilter(filter) || isDateFilter(filter) || isMeasureValueFilter(filter);
|
|
26
|
+
})
|
|
27
|
+
.flatMap((filter) => normalizeSourceFilterForDashboard(filter, sourceInsightMeasures));
|
|
25
28
|
// Keep the same precedence as drill-to-insight: measure filters win over source insight filters.
|
|
26
29
|
return [...sourceMeasureFilters, ...sourceInsightFilters];
|
|
27
30
|
}
|
|
31
|
+
function normalizeSourceFilterForDashboard(filter, sourceInsightMeasures) {
|
|
32
|
+
if (!isMeasureValueFilter(filter)) {
|
|
33
|
+
return [filter];
|
|
34
|
+
}
|
|
35
|
+
const measureRef = resolveSourceMeasureRef(filterMeasureRef(filter), sourceInsightMeasures);
|
|
36
|
+
if (!measureRef) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
measureValueFilter: {
|
|
42
|
+
...filter.measureValueFilter,
|
|
43
|
+
measure: measureRef,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}
|
|
28
48
|
function includedSourceFilters(sourceFilters, includedFilterObjRefs) {
|
|
29
49
|
return sourceFilters.filter((sourceFilter) => {
|
|
30
50
|
return includedFilterObjRefs.some((includedFilterObjRef) => isMatchingSourceInsightFilter(sourceFilter, includedFilterObjRef));
|
|
@@ -3,7 +3,7 @@ import { compact, isEmpty, isEqual } from "lodash-es";
|
|
|
3
3
|
import { call, put, select } from "redux-saga/effects";
|
|
4
4
|
import { invariant } from "ts-invariant";
|
|
5
5
|
import { areObjRefsEqual, dashboardFilterLocalIdentifier, insightMeasures, isDashboardAttributeFilter, isDashboardMeasureValueFilter, isDateFilter, isSimpleMeasure, measureFilters, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
6
|
-
import { dashboardAttributeFilterItemToAttributeFilter, dashboardDateFilterToDateFilterByWidget, } from "../../../converters/filterConverters.js";
|
|
6
|
+
import { dashboardAttributeFilterItemToAttributeFilter, dashboardDateFilterToDateFilterByWidget, dashboardMeasureValueFilterToMeasureValueFilter, } from "../../../converters/filterConverters.js";
|
|
7
7
|
import { switchDashboardTab } from "../../commands/tabs.js";
|
|
8
8
|
import { drillToDashboardRequested, drillToDashboardResolved, } from "../../events/drill.js";
|
|
9
9
|
import { generateFilterLocalIdentifier } from "../../store/_infra/generators.js";
|
|
@@ -12,7 +12,7 @@ import { selectCatalogDateAttributes } from "../../store/catalog/catalogSelector
|
|
|
12
12
|
import { selectEnableMultipleDateFilters } from "../../store/config/configSelectors.js";
|
|
13
13
|
import { selectInsightByRef } from "../../store/insights/insightsSelectors.js";
|
|
14
14
|
import { selectAttributeFilterConfigsDisplayAsLabelMap, selectAttributeFilterConfigsOverrides, } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
15
|
-
import { selectFilterContextAttributeFilterItems, selectFilterContextDateFilter, selectFilterContextDraggableFilterItems, } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
15
|
+
import { selectFilterContextAttributeFilterItems, selectFilterContextDateFilter, selectFilterContextDraggableFilterItems, selectFilterContextMeasureValueFilters, } from "../../store/tabs/filterContext/filterContextSelectors.js";
|
|
16
16
|
import { selectAnalyticalWidgetByRef } from "../../store/tabs/layout/layoutSelectors.js";
|
|
17
17
|
import { convertIntersectionToAttributeFilters, removeIgnoredValuesFromDrillIntersection, } from "./common/intersectionUtils.js";
|
|
18
18
|
import { mergeDashboardAndSourceFilters } from "./common/mergeFilters.js";
|
|
@@ -36,16 +36,17 @@ export function* drillToDashboardHandler(ctx, cmd) {
|
|
|
36
36
|
const supportsMultipleDateFilters = yield select(selectSupportsMultipleDateFilters);
|
|
37
37
|
const enableMultipleDateFilters = yield select(selectEnableMultipleDateFilters);
|
|
38
38
|
const includeOtherDateFilters = supportsMultipleDateFilters && enableMultipleDateFilters;
|
|
39
|
-
const
|
|
39
|
+
const allDraggableFilters = yield select(selectAllDraggableFilters);
|
|
40
40
|
const allAttributeFilters = yield select(selectAllAttributeFilters);
|
|
41
|
+
const allMeasureValueFilters = yield select(selectAllMeasureValueFilters);
|
|
42
|
+
const allNonDateFilters = [...allAttributeFilters, ...allMeasureValueFilters];
|
|
43
|
+
const dashboardFiltersToResolve = includeOtherDateFilters ? allDraggableFilters : allNonDateFilters;
|
|
41
44
|
const widgetAwareFilters = isDrillingToSelf
|
|
42
45
|
? []
|
|
43
|
-
: yield call(getWidgetAwareDashboardFilters, ctx, widget,
|
|
46
|
+
: yield call(getWidgetAwareDashboardFilters, ctx, widget, dashboardFiltersToResolve);
|
|
44
47
|
const candidateDashboardFilters = isDrillingToSelf
|
|
45
48
|
? // if drilling to self, just take all filters
|
|
46
|
-
|
|
47
|
-
? allOtherFilters
|
|
48
|
-
: allAttributeFilters
|
|
49
|
+
dashboardFiltersToResolve
|
|
49
50
|
: // if drilling to other, resolve widget filter ignores
|
|
50
51
|
widgetAwareFilters;
|
|
51
52
|
const dashboardFilters = removeIgnoredDashboardFilters(candidateDashboardFilters, ignoredDashboardFilterIds);
|
|
@@ -122,13 +123,19 @@ function selectDrillingDateFilter(state) {
|
|
|
122
123
|
function selectAllAttributeFilters(state) {
|
|
123
124
|
return selectFilterContextAttributeFilterItems(state);
|
|
124
125
|
}
|
|
125
|
-
function
|
|
126
|
+
function selectAllMeasureValueFilters(state) {
|
|
127
|
+
return selectFilterContextMeasureValueFilters(state);
|
|
128
|
+
}
|
|
129
|
+
function selectAllDraggableFilters(state) {
|
|
126
130
|
return selectFilterContextDraggableFilterItems(state);
|
|
127
131
|
}
|
|
128
132
|
function convertFilterItemsToFilters(filter, widget) {
|
|
129
133
|
if ("dateFilter" in filter) {
|
|
130
134
|
return dashboardDateFilterToDateFilterByWidget(filter, widget);
|
|
131
135
|
}
|
|
136
|
+
if (isDashboardMeasureValueFilter(filter)) {
|
|
137
|
+
return dashboardMeasureValueFilterToMeasureValueFilter(filter);
|
|
138
|
+
}
|
|
132
139
|
return dashboardAttributeFilterItemToAttributeFilter(filter);
|
|
133
140
|
}
|
|
134
141
|
function removeIgnoredDashboardFilters(filters, ignoredDashboardFilterIds) {
|
|
@@ -140,15 +147,9 @@ function removeIgnoredDashboardFilters(filters, ignoredDashboardFilterIds) {
|
|
|
140
147
|
return !localIdentifier || !ignoredDashboardFilterIds.has(localIdentifier);
|
|
141
148
|
});
|
|
142
149
|
}
|
|
143
|
-
function* getWidgetAwareDashboardFilters(ctx, widget,
|
|
144
|
-
const filtersIncludingDateFilters = yield select(selectFilterContextDraggableFilterItems);
|
|
145
|
-
const attributeFilters = yield select(selectFilterContextAttributeFilterItems);
|
|
146
|
-
const filterContextItems = includeOtherDateFilters ? filtersIncludingDateFilters : attributeFilters;
|
|
150
|
+
function* getWidgetAwareDashboardFilters(ctx, widget, filterContextItems) {
|
|
147
151
|
const attributeFilterConfigs = yield select(selectAttributeFilterConfigsOverrides);
|
|
148
|
-
|
|
149
|
-
// TODO INE: will be solved in https://gooddata.atlassian.net/browse/CQ-2285
|
|
150
|
-
const drillTransferableItems = filterContextItems.filter((item) => !isDashboardMeasureValueFilter(item));
|
|
151
|
-
const filtersPairs = drillTransferableItems.map((filter) => ({
|
|
152
|
+
const filtersPairs = filterContextItems.map((filter) => ({
|
|
152
153
|
filter: convertFilterItemsToFilters(filter, widget),
|
|
153
154
|
originalFilter: filter,
|
|
154
155
|
}));
|
|
@@ -3,7 +3,7 @@ import { compact, partition, uniqBy } from "lodash-es";
|
|
|
3
3
|
import { batchActions } from "redux-batched-actions";
|
|
4
4
|
import { all, call, put, select } from "redux-saga/effects";
|
|
5
5
|
import { NotSupported } from "@gooddata/sdk-backend-spi";
|
|
6
|
-
import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemFilterElementsBy, dashboardAttributeFilterItemFilterElementsByDate, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemValidateElementsBy, dashboardMeasureValueFilterLocalIdentifier, getAttributeElementsItems, isAllTimeDashboardDateFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMeasureValueFilter, isDashboardTextAttributeFilter, isSingleSelectionFilter, isUriRef, objRefToString, serializeObjRef, updateAttributeElementsItems, } from "@gooddata/sdk-model";
|
|
6
|
+
import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemFilterElementsBy, dashboardAttributeFilterItemFilterElementsByDate, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemValidateElementsBy, dashboardFilterObjRef, dashboardMeasureValueFilterLocalIdentifier, getAttributeElementsItems, isAllTimeDashboardDateFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMeasureValueFilter, isDashboardTextAttributeFilter, isSingleSelectionFilter, isUriRef, objRefToString, serializeObjRef, updateAttributeElementsItems, } from "@gooddata/sdk-model";
|
|
7
7
|
import { dashboardFilterToFilterContextItem } from "../../../_staging/dashboard/dashboardFilterContext.js";
|
|
8
8
|
import { invalidArgumentsProvided } from "../../events/general.js";
|
|
9
9
|
import { dispatchDashboardEvent } from "../../store/_infra/eventDispatcher.js";
|
|
@@ -455,13 +455,16 @@ function* getMeasureValueFiltersUpdateActions(measureValueFilters, resetOthers)
|
|
|
455
455
|
const currentMeasureValueFilters = yield select(selectFilterContextMeasureValueFilters);
|
|
456
456
|
const currentByLocalId = new Map(currentMeasureValueFilters.map((f) => [dashboardMeasureValueFilterLocalIdentifier(f), f]));
|
|
457
457
|
for (const incoming of measureValueFilters) {
|
|
458
|
-
const
|
|
458
|
+
const incomingLocalIdentifier = dashboardMeasureValueFilterLocalIdentifier(incoming);
|
|
459
|
+
const currentFilter = currentByLocalId.get(incomingLocalIdentifier) ??
|
|
460
|
+
currentMeasureValueFilters.find((current) => areObjRefsEqual(dashboardFilterObjRef(current), dashboardFilterObjRef(incoming)));
|
|
459
461
|
// Only update filters that already exist on the dashboard. Adding/removing MVFs is an
|
|
460
462
|
// edit-mode operation and is not part of changeFilterContextSelection in view mode.
|
|
461
|
-
if (!
|
|
462
|
-
console.warn(`changeFilterContextSelection: ignoring measure value filter with unknown localIdentifier "${
|
|
463
|
+
if (!currentFilter) {
|
|
464
|
+
console.warn(`changeFilterContextSelection: ignoring measure value filter with unknown localIdentifier "${incomingLocalIdentifier}". The dashboard does not currently have a measure value filter with this id or metric; adding new MVFs is not supported in view mode.`);
|
|
463
465
|
continue;
|
|
464
466
|
}
|
|
467
|
+
const localIdentifier = dashboardMeasureValueFilterLocalIdentifier(currentFilter);
|
|
465
468
|
handledLocalIds.add(localIdentifier);
|
|
466
469
|
updateActions.push(tabsActions.changeMeasureValueFilterCondition({
|
|
467
470
|
localIdentifier,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { call, put, select } from "redux-saga/effects";
|
|
3
|
+
import { dashboardFilterObjRef } from "@gooddata/sdk-model";
|
|
3
4
|
import { measureValueFilterRemoved } from "../../../events/filters.js";
|
|
4
5
|
import { invalidArgumentsProvided } from "../../../events/general.js";
|
|
5
6
|
import { dispatchDashboardEvent } from "../../../store/_infra/eventDispatcher.js";
|
|
@@ -12,8 +13,14 @@ export function* removeMeasureValueFilterHandler(ctx, cmd) {
|
|
|
12
13
|
if (!affectedFilter) {
|
|
13
14
|
throw invalidArgumentsProvided(ctx, cmd, `Filter with localIdentifier ${localIdentifier} not found.`);
|
|
14
15
|
}
|
|
16
|
+
const removedMeasureRef = dashboardFilterObjRef(affectedFilter);
|
|
15
17
|
yield put(tabsActions.removeMeasureValueFilter({ localIdentifier }));
|
|
16
18
|
yield put(tabsActions.removeMeasureValueFilterConfig(localIdentifier));
|
|
19
|
+
if (removedMeasureRef) {
|
|
20
|
+
yield put(tabsActions.removeIgnoredMeasureValueFilter({
|
|
21
|
+
measureRefs: [removedMeasureRef],
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
17
24
|
yield dispatchDashboardEvent(measureValueFilterRemoved(ctx, affectedFilter, cmd.correlationId));
|
|
18
25
|
yield call(dispatchFilterContextChanged, ctx, cmd);
|
|
19
26
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import stringify from "json-stable-stringify";
|
|
4
4
|
import { isEqual } from "lodash-es";
|
|
5
|
-
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemLocalIdentifier, filterObjRef, isAllTimeDashboardDateFilter, isAllValuesDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardDateFilter, isDashboardDateFilterWithDimension, } from "@gooddata/sdk-model";
|
|
5
|
+
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemLocalIdentifier, filterObjRef, isAllTimeDashboardDateFilter, isAllValuesDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMeasureValueFilter, } from "@gooddata/sdk-model";
|
|
6
6
|
import { usePrevious } from "@gooddata/sdk-ui";
|
|
7
7
|
import { safeSerializeObjRef } from "../../_staging/metadata/safeSerializeObjRef.js";
|
|
8
8
|
import { queryWidgetFilters } from "../queries/widgets.js";
|
|
@@ -168,6 +168,9 @@ function useNonIgnoredFilters(widget) {
|
|
|
168
168
|
else if (isDashboardDateFilterWithDimension(filter)) {
|
|
169
169
|
return nonIgnoredFilterState.nonIgnoredFilterRefs.some((validRef) => areObjRefsEqual(validRef, filter.dateFilter.dataSet));
|
|
170
170
|
}
|
|
171
|
+
else if (isDashboardMeasureValueFilter(filter)) {
|
|
172
|
+
return nonIgnoredFilterState.nonIgnoredFilterRefs.some((validRef) => areObjRefsEqual(validRef, filter.dashboardMeasureValueFilter.measure));
|
|
173
|
+
}
|
|
171
174
|
else {
|
|
172
175
|
// Common date filter: include only when widget does not ignore date filters
|
|
173
176
|
return !widgetIgnoresDateFilter;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Action, type CaseReducer } from "@reduxjs/toolkit";
|
|
2
2
|
import { type ICatalogAttribute, type ICatalogAttributeHierarchy, type ICatalogDateDataset, type ICatalogFact, type ICatalogMeasure, type IDateHierarchyTemplate } from "@gooddata/sdk-model";
|
|
3
|
-
import { type CatalogState, type ICatalogParametersState } from "./catalogState.js";
|
|
3
|
+
import { type CatalogState, type ICatalogMeasureParametersState, type ICatalogParametersState } from "./catalogState.js";
|
|
4
4
|
type CatalogReducer<A extends Action> = CaseReducer<CatalogState, A>;
|
|
5
5
|
/**
|
|
6
6
|
* @public
|
|
@@ -45,5 +45,9 @@ export declare const catalogReducers: {
|
|
|
45
45
|
payload: ICatalogParametersState;
|
|
46
46
|
type: string;
|
|
47
47
|
}>;
|
|
48
|
+
setCatalogMeasureParameters: CatalogReducer<{
|
|
49
|
+
payload: ICatalogMeasureParametersState;
|
|
50
|
+
type: string;
|
|
51
|
+
}>;
|
|
48
52
|
};
|
|
49
53
|
export {};
|
|
@@ -32,6 +32,9 @@ const deleteAttributeHierarchy = (state, action) => {
|
|
|
32
32
|
const setCatalogParameters = (state, action) => {
|
|
33
33
|
state.parameters = action.payload;
|
|
34
34
|
};
|
|
35
|
+
const setCatalogMeasureParameters = (state, action) => {
|
|
36
|
+
state.measureParameters = action.payload;
|
|
37
|
+
};
|
|
35
38
|
export const catalogReducers = {
|
|
36
39
|
setCatalogItems,
|
|
37
40
|
setCatalogMeasuresAndFacts,
|
|
@@ -39,4 +42,5 @@ export const catalogReducers = {
|
|
|
39
42
|
updateAttributeHierarchy,
|
|
40
43
|
deleteAttributeHierarchy,
|
|
41
44
|
setCatalogParameters,
|
|
45
|
+
setCatalogMeasureParameters,
|
|
42
46
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type IAttributeDisplayFormMetadataObject, type ICatalogAttribute, type ICatalogAttributeHierarchy, type ICatalogDateAttribute, type ICatalogDateAttributeHierarchy, type ICatalogDateDataset, type ICatalogFact, type ICatalogMeasure, type IDateHierarchyTemplate, type IParameterMetadataObject, type ObjRef } from "@gooddata/sdk-model";
|
|
1
|
+
import { type IAttributeDisplayFormMetadataObject, type ICatalogAttribute, type ICatalogAttributeHierarchy, type ICatalogDateAttribute, type ICatalogDateAttributeHierarchy, type ICatalogDateDataset, type ICatalogFact, type ICatalogMeasure, type IDateHierarchyTemplate, type IParameterMetadataObject, type IdentifierRef, type ObjRef } from "@gooddata/sdk-model";
|
|
2
2
|
import { type CatalogDateAttributeWithDataset } from "../../../_staging/catalog/dateAttributeWithDatasetMap.js";
|
|
3
3
|
import { type ObjRefMap } from "../../../_staging/metadata/objRefMap.js";
|
|
4
4
|
import { type DashboardSelector } from "../types.js";
|
|
5
|
-
import { type CatalogParametersStatus } from "./catalogState.js";
|
|
5
|
+
import { type CatalogMeasureParametersStatus, type CatalogParametersStatus } from "./catalogState.js";
|
|
6
6
|
/**
|
|
7
7
|
* @public
|
|
8
8
|
*/
|
|
@@ -32,6 +32,19 @@ export declare const selectCatalogParametersStatus: DashboardSelector<CatalogPar
|
|
|
32
32
|
* @alpha
|
|
33
33
|
*/
|
|
34
34
|
export declare const selectCatalogParametersIsLoaded: DashboardSelector<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Returns the dashboard-wide map from metric ref string to the parameter refs the metric depends on.
|
|
37
|
+
* The map is populated during dashboard initialization from the workspace references service.
|
|
38
|
+
*
|
|
39
|
+
* @alpha
|
|
40
|
+
*/
|
|
41
|
+
export declare const selectCatalogMeasureParameters: DashboardSelector<Record<string, IdentifierRef[]>>;
|
|
42
|
+
/**
|
|
43
|
+
* Returns the load status of the dashboard-wide metric → parameter dependency map.
|
|
44
|
+
*
|
|
45
|
+
* @alpha
|
|
46
|
+
*/
|
|
47
|
+
export declare const selectCatalogMeasureParametersStatus: DashboardSelector<CatalogMeasureParametersStatus>;
|
|
35
48
|
/**
|
|
36
49
|
* @alpha
|
|
37
50
|
*/
|
|
@@ -39,6 +39,19 @@ export const selectCatalogParametersStatus = createSelector(selectSelf, (state)
|
|
|
39
39
|
* @alpha
|
|
40
40
|
*/
|
|
41
41
|
export const selectCatalogParametersIsLoaded = createSelector(selectCatalogParametersStatus, (status) => status === "loaded");
|
|
42
|
+
/**
|
|
43
|
+
* Returns the dashboard-wide map from metric ref string to the parameter refs the metric depends on.
|
|
44
|
+
* The map is populated during dashboard initialization from the workspace references service.
|
|
45
|
+
*
|
|
46
|
+
* @alpha
|
|
47
|
+
*/
|
|
48
|
+
export const selectCatalogMeasureParameters = createSelector(selectSelf, (state) => state.measureParameters.byMetric);
|
|
49
|
+
/**
|
|
50
|
+
* Returns the load status of the dashboard-wide metric → parameter dependency map.
|
|
51
|
+
*
|
|
52
|
+
* @alpha
|
|
53
|
+
*/
|
|
54
|
+
export const selectCatalogMeasureParametersStatus = createSelector(selectSelf, (state) => state.measureParameters.status);
|
|
42
55
|
/**
|
|
43
56
|
* @alpha
|
|
44
57
|
*/
|