@gooddata/sdk-ui-dashboard 11.35.0-alpha.5 → 11.35.0-alpha.7
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/NOTICE +8 -8
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/_staging/dashboard/dashboardFilterContext.js +18 -1
- package/esm/_staging/sharedHooks/useFiltersNamings.d.ts +1 -1
- package/esm/_staging/sharedHooks/useFiltersNamings.js +32 -5
- package/esm/index.d.ts +4 -5
- package/esm/index.js +2 -3
- package/esm/kdaDialog/dialog/hooks/useChangeAnalysis.js +21 -5
- package/esm/kdaDialog/internalTypes.d.ts +6 -1
- package/esm/kdaDialog/providers/Kda.js +4 -1
- package/esm/kdaDialog/providers/KdaState.js +1 -0
- package/esm/kdaDialog/types.d.ts +13 -2
- package/esm/model/commandHandlers/dashboard/common/parameterHydration.d.ts +11 -2
- package/esm/model/commandHandlers/dashboard/common/parameterHydration.js +21 -0
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.js +44 -79
- package/esm/model/commandHandlers/dashboard/saveAsDashboardHandler.js +10 -5
- package/esm/model/commandHandlers/dashboard/saveDashboardHandler.js +11 -7
- package/esm/model/commandHandlers/drill/keyDriverAnalysisHandler.js +8 -3
- package/esm/model/store/_infra/generators.d.ts +1 -0
- package/esm/model/store/_infra/generators.js +4 -1
- package/esm/model/store/dashboardStore.d.ts +0 -2
- package/esm/model/store/dashboardStore.js +0 -2
- package/esm/model/store/filtering/dashboardFilterSelectors.d.ts +1 -1
- package/esm/model/store/filtering/dashboardFilterSelectors.js +8 -5
- package/esm/model/store/meta/metaSelectors.js +4 -3
- package/esm/model/store/tabs/filterContext/filterContextReducers.js +2 -2
- package/esm/model/store/tabs/index.d.ts +12 -0
- package/esm/model/store/tabs/index.js +2 -0
- package/esm/model/store/{parameters → tabs/parameters}/parametersReducers.d.ts +3 -7
- package/esm/model/store/tabs/parameters/parametersReducers.js +46 -0
- package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +72 -0
- package/esm/model/store/tabs/parameters/parametersSelectors.js +228 -0
- package/esm/model/store/{parameters → tabs/parameters}/parametersState.d.ts +11 -1
- package/esm/model/store/tabs/parameters/parametersState.js +20 -0
- package/esm/model/store/tabs/tabsState.d.ts +2 -0
- package/esm/model/store/types.d.ts +0 -6
- package/esm/model/utils/widgetFilters.d.ts +1 -1
- package/esm/model/utils/widgetFilters.js +1 -1
- package/esm/presentation/automationFilters/components/AutomationFiltersSelect.js +8 -2
- package/esm/presentation/automationFilters/components/AutomationMeasureValueFilter.d.ts +10 -0
- package/esm/presentation/automationFilters/components/AutomationMeasureValueFilter.js +66 -0
- package/esm/presentation/automationFilters/components/AutomationMeasureValueFilterContext.d.ts +25 -0
- package/esm/presentation/automationFilters/components/AutomationMeasureValueFilterContext.js +28 -0
- package/esm/presentation/automationFilters/useAutomationFilters.d.ts +4 -1
- package/esm/presentation/automationFilters/useAutomationFilters.js +35 -4
- package/esm/presentation/automationFilters/utils.d.ts +2 -1
- package/esm/presentation/automationFilters/utils.js +34 -5
- package/esm/presentation/dragAndDrop/draggableParameterFilter/DefaultParameterDraggingComponent.js +1 -1
- package/esm/presentation/dragAndDrop/useFilterDeleteDrop.js +2 -2
- package/esm/presentation/filterBar/filterBar/DefaultFilterBar.js +1 -1
- package/esm/presentation/filterBar/measureValueFilter/DefaultDashboardMeasureValueFilter.js +8 -42
- package/esm/presentation/filterBar/measureValueFilter/useDashboardMeasureValueFilterData.d.ts +86 -0
- package/esm/presentation/filterBar/measureValueFilter/useDashboardMeasureValueFilterData.js +105 -0
- package/esm/presentation/filterBar/parameterFilter/DashboardParameterFilter.js +3 -3
- package/esm/presentation/filterBar/parameterFilter/DashboardParameterPicker.js +3 -3
- package/esm/presentation/widget/insight/ViewModeDashboardInsight/Insight/DashboardInsight.js +1 -1
- package/esm/sdk-ui-dashboard.d.ts +44 -32
- package/package.json +21 -21
- package/esm/model/store/parameters/index.d.ts +0 -12
- package/esm/model/store/parameters/index.js +0 -14
- package/esm/model/store/parameters/parametersReducers.js +0 -36
- package/esm/model/store/parameters/parametersSelectors.d.ts +0 -67
- package/esm/model/store/parameters/parametersSelectors.js +0 -122
- package/esm/model/store/parameters/parametersState.js +0 -4
|
@@ -14,7 +14,6 @@ import { selectCrossFilteringFiltersLocalIdentifiers } from "../../store/drill/d
|
|
|
14
14
|
import { listedDashboardsActions } from "../../store/listedDashboards/index.js";
|
|
15
15
|
import { metaActions } from "../../store/meta/index.js";
|
|
16
16
|
import { selectDashboardDescriptor, selectPersistedDashboard, selectPersistedDashboardFilterContextAsFilterContextDefinition, } from "../../store/meta/metaSelectors.js";
|
|
17
|
-
import { selectSmartPersistedDashboardParameters } from "../../store/parameters/parametersSelectors.js";
|
|
18
17
|
import { selectIsInViewMode } from "../../store/renderMode/renderModeSelectors.js";
|
|
19
18
|
import { savingActions } from "../../store/saving/index.js";
|
|
20
19
|
import { selectAttributeFilterConfigsOverrides } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
@@ -24,6 +23,7 @@ import { selectFilterContextAttributeFilters, selectFilterContextDefinition, } f
|
|
|
24
23
|
import { tabsActions } from "../../store/tabs/index.js";
|
|
25
24
|
import { filterOutCustomWidgets, selectBasicLayout } from "../../store/tabs/layout/layoutSelectors.js";
|
|
26
25
|
import { selectMeasureValueFilterConfigsOverrides } from "../../store/tabs/measureValueFilterConfigs/measureValueFilterConfigsSelectors.js";
|
|
26
|
+
import { selectSmartPersistedTabsParameters } from "../../store/tabs/parameters/parametersSelectors.js";
|
|
27
27
|
import { selectTabs } from "../../store/tabs/tabsSelectors.js";
|
|
28
28
|
import { selectCurrentUser } from "../../store/user/userSelectors.js";
|
|
29
29
|
import { changeRenderModeHandler } from "../renderMode/changeRenderModeHandler.js";
|
|
@@ -39,7 +39,7 @@ function createDashboard(ctx, saveAsCtx) {
|
|
|
39
39
|
* @param tabs - Array of TabState objects from the dashboard state
|
|
40
40
|
* @returns Array of IDashboardTab objects ready for saving to backend
|
|
41
41
|
*/
|
|
42
|
-
function processExistingTabsForSaveAs(tabs, useOriginalFilterContext) {
|
|
42
|
+
function processExistingTabsForSaveAs(tabs, parametersByTab, useOriginalFilterContext) {
|
|
43
43
|
return tabs.map((tab) => {
|
|
44
44
|
const dateFilterConfig = tab.dateFilterConfig?.dateFilterConfig;
|
|
45
45
|
const dateFilterConfigs = tab.dateFilterConfigs?.dateFilterConfigs;
|
|
@@ -61,6 +61,7 @@ function processExistingTabsForSaveAs(tabs, useOriginalFilterContext) {
|
|
|
61
61
|
: tab.filterContext.filterContextDefinition),
|
|
62
62
|
}
|
|
63
63
|
: undefined;
|
|
64
|
+
const tabParameters = parametersByTab[tab.localIdentifier] ?? [];
|
|
64
65
|
const result = {
|
|
65
66
|
// explicitly type the result to avoid type errors caused by spread operators
|
|
66
67
|
localIdentifier: tab.localIdentifier,
|
|
@@ -72,6 +73,9 @@ function processExistingTabsForSaveAs(tabs, useOriginalFilterContext) {
|
|
|
72
73
|
...dateFilterConfigsProp,
|
|
73
74
|
...attributeFilterConfigsProp,
|
|
74
75
|
...measureValueFilterConfigsProp,
|
|
76
|
+
// Always persist `parameters` (incl. `[]`) so V1 root fallback never re-hydrates stale
|
|
77
|
+
// root parameters when every tab has been emptied.
|
|
78
|
+
parameters: tabParameters,
|
|
75
79
|
};
|
|
76
80
|
return result;
|
|
77
81
|
});
|
|
@@ -108,11 +112,13 @@ function* createDashboardSaveAsContext(cmd) {
|
|
|
108
112
|
const dateFilterConfigs = yield select(selectDateFilterConfigsOverrides);
|
|
109
113
|
const measureValueFilterConfigs = yield select(selectMeasureValueFilterConfigsOverrides);
|
|
110
114
|
const tabs = yield select(selectTabs);
|
|
111
|
-
const
|
|
115
|
+
const parametersByTab = yield select(selectSmartPersistedTabsParameters);
|
|
112
116
|
const capabilities = yield select(selectBackendCapabilities);
|
|
113
117
|
const { isUnderStrictControl: _unusedProp, ...dashboardDescriptorRest } = dashboardDescriptor;
|
|
114
118
|
// Process tabs if tabs exist
|
|
115
|
-
const processedTabs = tabs && tabs.length > 0
|
|
119
|
+
const processedTabs = tabs && tabs.length > 0
|
|
120
|
+
? processExistingTabsForSaveAs(tabs, parametersByTab, useOriginalFilterContext)
|
|
121
|
+
: undefined;
|
|
116
122
|
const dashboardFromState = {
|
|
117
123
|
type: "IDashboard",
|
|
118
124
|
...dashboardDescriptorRest,
|
|
@@ -125,7 +131,6 @@ function* createDashboardSaveAsContext(cmd) {
|
|
|
125
131
|
...(dateFilterConfigs?.length ? { dateFilterConfigs } : {}),
|
|
126
132
|
...(measureValueFilterConfigs?.length ? { measureValueFilterConfigs } : {}),
|
|
127
133
|
...(processedTabs ? { tabs: processedTabs } : {}),
|
|
128
|
-
...(parameters.length ? { parameters } : {}),
|
|
129
134
|
};
|
|
130
135
|
const pluginsProp = persistedDashboard?.plugins ? { plugins: persistedDashboard.plugins } : {};
|
|
131
136
|
const shareProp = capabilities.supportsAccessControl
|
|
@@ -18,7 +18,6 @@ import { selectLocale } from "../../store/config/configSelectors.js";
|
|
|
18
18
|
import { listedDashboardsActions } from "../../store/listedDashboards/index.js";
|
|
19
19
|
import { metaActions } from "../../store/meta/index.js";
|
|
20
20
|
import { selectDashboardDescriptor, selectPersistedDashboard } from "../../store/meta/metaSelectors.js";
|
|
21
|
-
import { selectSmartPersistedDashboardParameters } from "../../store/parameters/parametersSelectors.js";
|
|
22
21
|
import { selectIsInViewMode } from "../../store/renderMode/renderModeSelectors.js";
|
|
23
22
|
import { savingActions } from "../../store/saving/index.js";
|
|
24
23
|
import { selectAttributeFilterConfigsOverrides } from "../../store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
@@ -28,6 +27,7 @@ import { selectFilterContextDefinition, selectFilterContextIdentity, } from "../
|
|
|
28
27
|
import { tabsActions } from "../../store/tabs/index.js";
|
|
29
28
|
import { filterOutCustomWidgets, selectBasicLayout } from "../../store/tabs/layout/layoutSelectors.js";
|
|
30
29
|
import { selectMeasureValueFilterConfigsOverrides } from "../../store/tabs/measureValueFilterConfigs/measureValueFilterConfigsSelectors.js";
|
|
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";
|
|
33
33
|
import { changeRenderModeHandler } from "../renderMode/changeRenderModeHandler.js";
|
|
@@ -66,9 +66,10 @@ export function getDashboardWithSharing(dashboard, sharingSupported = true, isNe
|
|
|
66
66
|
* Converts TabState[] from the dashboard state into IDashboardTab[] for saving.
|
|
67
67
|
*
|
|
68
68
|
* @param tabs - Array of TabState objects from the dashboard state
|
|
69
|
+
* @param parametersByTab - Smart-persisted parameters keyed by tab localIdentifier
|
|
69
70
|
* @returns Array of IDashboardTab objects ready for saving to backend
|
|
70
71
|
*/
|
|
71
|
-
function processExistingTabs(tabs) {
|
|
72
|
+
function processExistingTabs(tabs, parametersByTab) {
|
|
72
73
|
return tabs.map((tab) => {
|
|
73
74
|
const dateFilterConfig = tab.dateFilterConfig?.dateFilterConfig;
|
|
74
75
|
const dateFilterConfigProp = dateFilterConfig ? { dateFilterConfig } : {};
|
|
@@ -93,6 +94,7 @@ function processExistingTabs(tabs) {
|
|
|
93
94
|
...tab.filterContext.filterContextDefinition,
|
|
94
95
|
}
|
|
95
96
|
: undefined;
|
|
97
|
+
const tabParameters = parametersByTab[tab.localIdentifier] ?? [];
|
|
96
98
|
const result = {
|
|
97
99
|
// explicitly type the result to avoid type errors caused by spread operators
|
|
98
100
|
localIdentifier: tab.localIdentifier,
|
|
@@ -105,6 +107,9 @@ function processExistingTabs(tabs) {
|
|
|
105
107
|
...attributeFilterConfigsProp,
|
|
106
108
|
...measureValueFilterConfigsProp,
|
|
107
109
|
...filterGroupsConfigProp,
|
|
110
|
+
// Always persist `parameters` (incl. `[]`) so V1 root fallback never re-hydrates stale
|
|
111
|
+
// root parameters when every tab has been emptied.
|
|
112
|
+
parameters: tabParameters,
|
|
108
113
|
};
|
|
109
114
|
return result;
|
|
110
115
|
});
|
|
@@ -131,14 +136,14 @@ function createDefaultTab(layout, rootFilterContext, dateFilterConfig, attribute
|
|
|
131
136
|
},
|
|
132
137
|
];
|
|
133
138
|
}
|
|
134
|
-
function resolveProcessedTabs(tabs, rootFilterContext, layout, dateFilterConfig, attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs) {
|
|
139
|
+
function resolveProcessedTabs(tabs, rootFilterContext, layout, dateFilterConfig, attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs, parametersByTab) {
|
|
135
140
|
// If no tabs exist, create a default tab with root-level properties
|
|
136
141
|
const shouldCreateDefaultTab = !tabs || tabs.length === 0;
|
|
137
142
|
if (shouldCreateDefaultTab && rootFilterContext) {
|
|
138
143
|
return createDefaultTab(layout, rootFilterContext, dateFilterConfig, attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs);
|
|
139
144
|
}
|
|
140
145
|
if (tabs) {
|
|
141
|
-
return processExistingTabs(tabs);
|
|
146
|
+
return processExistingTabs(tabs, parametersByTab);
|
|
142
147
|
}
|
|
143
148
|
return undefined;
|
|
144
149
|
}
|
|
@@ -159,7 +164,7 @@ function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
|
159
164
|
const dateFilterConfigs = yield select(selectDateFilterConfigsOverrides);
|
|
160
165
|
const measureValueFilterConfigs = yield select(selectMeasureValueFilterConfigsOverrides);
|
|
161
166
|
const tabs = yield select(selectTabs);
|
|
162
|
-
const
|
|
167
|
+
const parametersByTab = yield select(selectSmartPersistedTabsParameters);
|
|
163
168
|
const capabilities = yield select(selectBackendCapabilities);
|
|
164
169
|
/*
|
|
165
170
|
* When updating an existing dashboard, the services expect that the dashboard definition to use for
|
|
@@ -179,7 +184,7 @@ function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
|
179
184
|
...filterContextDefinition,
|
|
180
185
|
}
|
|
181
186
|
: undefined;
|
|
182
|
-
const processedTabs = resolveProcessedTabs(tabs, rootFilterContext, layout, dateFilterConfig, attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs);
|
|
187
|
+
const processedTabs = resolveProcessedTabs(tabs, rootFilterContext, layout, dateFilterConfig, attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs, parametersByTab);
|
|
183
188
|
const locale = yield select(selectLocale);
|
|
184
189
|
const translations = yield call(resolveMessages, locale);
|
|
185
190
|
const title = (cmd.payload.title ?? dashboardDescriptor.title) || translations[messages.untitled.id];
|
|
@@ -198,7 +203,6 @@ function* createDashboardSaveContext(cmd, isNewDashboard) {
|
|
|
198
203
|
dateFilterConfig,
|
|
199
204
|
...buildOptionalFilterConfigsProps(attributeFilterConfigs, dateFilterConfigs, measureValueFilterConfigs),
|
|
200
205
|
...(processedTabs ? { tabs: processedTabs } : {}),
|
|
201
|
-
...(parameters.length > 0 ? { parameters } : {}),
|
|
202
206
|
...pluginsProp,
|
|
203
207
|
};
|
|
204
208
|
const dashboardToSave = {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
2
|
import { put, select } from "redux-saga/effects";
|
|
3
|
-
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, isAttributeDescriptor, isMeasureDescriptor, } from "@gooddata/sdk-model";
|
|
3
|
+
import { areObjRefsEqual, dashboardAttributeFilterItemDisplayForm, isAttributeDescriptor, isDashboardMeasureValueFilter, isMeasureDescriptor, } from "@gooddata/sdk-model";
|
|
4
4
|
import { keyDriverAnalysisRequested, keyDriverAnalysisResolved, } from "../../events/drill.js";
|
|
5
5
|
import { invalidArgumentsProvided } from "../../events/general.js";
|
|
6
6
|
import { generateFilterLocalIdentifier } from "../../store/_infra/generators.js";
|
|
7
7
|
import { selectCatalogDateAttributes } from "../../store/catalog/catalogSelectors.js";
|
|
8
8
|
import { selectWidgetByRef } from "../../store/tabs/layout/layoutSelectors.js";
|
|
9
|
-
import {
|
|
9
|
+
import { getAttributeFilters, removeIgnoredWidgetFilters } from "../../utils/widgetFilters.js";
|
|
10
10
|
import { convertIntersectionToAttributeFilters } from "./common/intersectionUtils.js";
|
|
11
11
|
export function* keyDriverAnalysisHandler(ctx, cmd) {
|
|
12
12
|
const { drillDefinition, drillEvent, keyDriveItem, filters: availableFilters } = cmd.payload;
|
|
@@ -52,7 +52,11 @@ export function* keyDriverAnalysisHandler(ctx, cmd) {
|
|
|
52
52
|
return dashboardFilter;
|
|
53
53
|
});
|
|
54
54
|
const widget = yield select(selectWidgetByRef(drillEvent.widgetRef));
|
|
55
|
-
const
|
|
55
|
+
const widgetFilters = removeIgnoredWidgetFilters(availableFilters, widget);
|
|
56
|
+
// KDA defines its own date range — drop date filters. Keep attribute and measure value
|
|
57
|
+
// filters so the change analysis respects the rest of the dashboard filter context.
|
|
58
|
+
const attributeFilters = getAttributeFilters(widgetFilters);
|
|
59
|
+
const measureValueFilters = widgetFilters.filter(isDashboardMeasureValueFilter);
|
|
56
60
|
const filters = mergeFilters(intersectionFilters, attributeFilters);
|
|
57
61
|
return keyDriverAnalysisResolved(ctx, cmd.payload.drillDefinition, cmd.payload.drillEvent, {
|
|
58
62
|
metric: {
|
|
@@ -65,6 +69,7 @@ export function* keyDriverAnalysisHandler(ctx, cmd) {
|
|
|
65
69
|
},
|
|
66
70
|
metrics,
|
|
67
71
|
filters,
|
|
72
|
+
measureValueFilters,
|
|
68
73
|
dateAttribute: dateAttribute.attribute.ref,
|
|
69
74
|
range: [
|
|
70
75
|
{
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
// (C) 2024 GoodData Corporation
|
|
1
|
+
// (C) 2024-2026 GoodData Corporation
|
|
2
2
|
import { objRefToString } from "@gooddata/sdk-model";
|
|
3
3
|
export const generateFilterLocalIdentifier = (ref, index) => {
|
|
4
4
|
return `${objRefToString(ref)}_${index}_attributeFilter`;
|
|
5
5
|
};
|
|
6
|
+
export const generateMeasureValueFilterLocalIdentifier = (ref, index) => {
|
|
7
|
+
return `${objRefToString(ref)}_${index}_measureValueFilter`;
|
|
8
|
+
};
|
|
@@ -127,7 +127,6 @@ export declare function createDashboardRootReducer({ queryCache }?: {
|
|
|
127
127
|
dashboardPermissions: import("./dashboardPermissions/dashboardPermissionsState.js").DashboardPermissionsState;
|
|
128
128
|
showWidgetAsTable: import("./showWidgetAsTable/showWidgetAsTableState.js").IShowWidgetAsTableState;
|
|
129
129
|
notificationChannels: import("./notificationChannels/notificationChannelsState.js").INotificationChannelsState;
|
|
130
|
-
parameters: import("./parameters/parametersState.js").IParametersState;
|
|
131
130
|
automations: import("./automations/automationsState.js").IAutomationsState;
|
|
132
131
|
users: import("./users/usersState.js").IUsersState;
|
|
133
132
|
filterViews: import("./filterViews/filterViewsState.js").IFilterViewsState;
|
|
@@ -157,7 +156,6 @@ export declare function createDashboardRootReducer({ queryCache }?: {
|
|
|
157
156
|
dashboardPermissions: import("./dashboardPermissions/dashboardPermissionsState.js").DashboardPermissionsState | undefined;
|
|
158
157
|
showWidgetAsTable: import("./showWidgetAsTable/showWidgetAsTableState.js").IShowWidgetAsTableState | undefined;
|
|
159
158
|
notificationChannels: import("./notificationChannels/notificationChannelsState.js").INotificationChannelsState | undefined;
|
|
160
|
-
parameters: import("./parameters/parametersState.js").IParametersState | undefined;
|
|
161
159
|
automations: import("./automations/automationsState.js").IAutomationsState | undefined;
|
|
162
160
|
users: import("./users/usersState.js").IUsersState | undefined;
|
|
163
161
|
filterViews: import("./filterViews/filterViewsState.js").IFilterViewsState | undefined;
|
|
@@ -31,7 +31,6 @@ import { listedDashboardsSliceReducer } from "./listedDashboards/index.js";
|
|
|
31
31
|
import { loadingSliceReducer } from "./loading/index.js";
|
|
32
32
|
import { metaSliceReducer } from "./meta/index.js";
|
|
33
33
|
import { notificationChannelsSliceReducer } from "./notificationChannels/index.js";
|
|
34
|
-
import { parametersSliceReducer } from "./parameters/index.js";
|
|
35
34
|
import { permissionsSliceReducer } from "./permissions/index.js";
|
|
36
35
|
import { renderModeSliceReducer } from "./renderMode/index.js";
|
|
37
36
|
import { savingSliceReducer } from "./saving/index.js";
|
|
@@ -162,7 +161,6 @@ export function createDashboardRootReducer({ queryCache = (state = {}) => state,
|
|
|
162
161
|
dashboardPermissions: dashboardPermissionsSliceReducer,
|
|
163
162
|
showWidgetAsTable: showWidgetAsTableSliceReducer,
|
|
164
163
|
notificationChannels: notificationChannelsSliceReducer,
|
|
165
|
-
parameters: parametersSliceReducer,
|
|
166
164
|
automations: automationsSliceReducer,
|
|
167
165
|
users: usersSliceReducer,
|
|
168
166
|
filterViews: filterViewsSliceReducer,
|
|
@@ -24,7 +24,7 @@ export declare const selectAutomationDefaultSelectedFilters: DashboardSelector<F
|
|
|
24
24
|
* @alpha
|
|
25
25
|
*/
|
|
26
26
|
export declare const selectAutomationCommonDateFilterId: DashboardSelector<string | undefined>;
|
|
27
|
-
export declare function
|
|
27
|
+
export declare function removeEmptyDashboardFilters(filters?: FilterContextItem[]): FilterContextItem[];
|
|
28
28
|
export declare const isFilterContextItemHidden: (filter: FilterContextItem, filterConfigurations: {
|
|
29
29
|
commonDateFilterConfig?: IDashboardDateFilterConfig | undefined;
|
|
30
30
|
dateFilterWithDimensionConfigs: IDashboardDateFilterConfigItem[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// (C) 2024-2026 GoodData Corporation
|
|
2
2
|
import { createSelector } from "@reduxjs/toolkit";
|
|
3
3
|
import { generateDateFilterLocalIdentifier } from "@gooddata/sdk-backend-base";
|
|
4
|
-
import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, isAllValuesDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
4
|
+
import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, isAllDashboardMeasureValueFilter, isAllValuesDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMeasureValueFilter, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
5
5
|
import { selectCrossFilteringItems, selectCrossFilteringItemsByTab } from "../drill/drillSelectors.js";
|
|
6
6
|
import { selectAttributeFilterConfigsOverrides, selectAttributeFilterConfigsOverridesByTab, } from "../tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
7
7
|
import { selectDateFilterConfigOverrides, selectDateFilterConfigOverridesByTab, } from "../tabs/dateFilterConfig/dateFilterConfigSelectors.js";
|
|
@@ -55,7 +55,7 @@ export const selectAutomationAvailableDashboardFilters = createSelector(selectDa
|
|
|
55
55
|
* @alpha
|
|
56
56
|
*/
|
|
57
57
|
export const selectAutomationDefaultSelectedFilters = createSelector(selectAutomationAvailableDashboardFilters, (availableDashboardFilters) => {
|
|
58
|
-
return
|
|
58
|
+
return removeEmptyDashboardFilters(availableDashboardFilters);
|
|
59
59
|
});
|
|
60
60
|
/**
|
|
61
61
|
* @alpha
|
|
@@ -79,11 +79,14 @@ const removeCrossFilteringFilters = (filters, crossFilteringItems) => {
|
|
|
79
79
|
return true;
|
|
80
80
|
});
|
|
81
81
|
};
|
|
82
|
-
export function
|
|
82
|
+
export function removeEmptyDashboardFilters(filters = []) {
|
|
83
83
|
return filters.filter((filter) => {
|
|
84
84
|
if (isDashboardAttributeFilterItem(filter)) {
|
|
85
85
|
return !isAllValuesDashboardAttributeFilter(filter);
|
|
86
86
|
}
|
|
87
|
+
if (isDashboardMeasureValueFilter(filter)) {
|
|
88
|
+
return !isAllDashboardMeasureValueFilter(filter);
|
|
89
|
+
}
|
|
87
90
|
return true;
|
|
88
91
|
});
|
|
89
92
|
}
|
|
@@ -166,8 +169,8 @@ export const selectAutomationFiltersByTab = createSelector(selectTabs, selectFil
|
|
|
166
169
|
const lockedFilters = filtersWithCommonDate.filter((filter) => isFilterContextItemLocked(filter, filterConfigurations));
|
|
167
170
|
// Get hidden filters
|
|
168
171
|
const hiddenFilters = filtersWithCommonDate.filter((filter) => isFilterContextItemHidden(filter, filterConfigurations));
|
|
169
|
-
// Get default selected filters (
|
|
170
|
-
const defaultSelectedFilters =
|
|
172
|
+
// Get default selected filters (noop "All" filters removed — attribute and MVF)
|
|
173
|
+
const defaultSelectedFilters = removeEmptyDashboardFilters(availableFilters);
|
|
171
174
|
return {
|
|
172
175
|
tabId,
|
|
173
176
|
tabTitle,
|
|
@@ -4,13 +4,13 @@ import { isEqual } from "lodash-es";
|
|
|
4
4
|
import { invariant } from "ts-invariant";
|
|
5
5
|
import { isDashboardLayoutEmpty } from "@gooddata/sdk-backend-spi";
|
|
6
6
|
import { idRef, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDashboardDateFilterWithDimension, isDashboardMeasureValueFilter, isDashboardTextAttributeFilter, isTempFilterContext, uriRef, } from "@gooddata/sdk-model";
|
|
7
|
-
import { selectIsParametersChanged, selectSmartPersistedDashboardParameters, } from "../parameters/parametersSelectors.js";
|
|
8
7
|
import { selectAttributeFilterConfigsOverridesByTab } from "../tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
|
|
9
8
|
import { selectDateFilterConfigOverrides, selectDateFilterConfigOverridesByTab, } from "../tabs/dateFilterConfig/dateFilterConfigSelectors.js";
|
|
10
9
|
import { selectDateFilterConfigsOverridesByTab } from "../tabs/dateFilterConfigs/dateFilterConfigsSelectors.js";
|
|
11
10
|
import { selectFilterContextDefinition, selectFilterContextDefinitionsByTab, selectFilterContextIdentity, } from "../tabs/filterContext/filterContextSelectors.js";
|
|
12
11
|
import { filterOutCustomWidgets, selectBasicLayout, selectBasicLayoutByTab, } from "../tabs/layout/layoutSelectors.js";
|
|
13
12
|
import { selectMeasureValueFilterConfigsOverridesByTab } from "../tabs/measureValueFilterConfigs/measureValueFilterConfigsSelectors.js";
|
|
13
|
+
import { selectIsParametersChanged, selectSmartPersistedTabsParameters, } from "../tabs/parameters/parametersSelectors.js";
|
|
14
14
|
import { selectActiveTabLocalIdentifier, selectTabs } from "../tabs/tabsSelectors.js";
|
|
15
15
|
import { DEFAULT_TAB_ID } from "../tabs/tabsState.js";
|
|
16
16
|
const selectSelf = createSelector((state) => state, (state) => state.meta);
|
|
@@ -71,6 +71,7 @@ export const selectPersistedDashboardTabs = createSelector(selectSelf, (state) =
|
|
|
71
71
|
dateFilterConfig: persistedDashboard.dateFilterConfig,
|
|
72
72
|
dateFilterConfigs: persistedDashboard.dateFilterConfigs,
|
|
73
73
|
layout: persistedDashboard.layout,
|
|
74
|
+
parameters: persistedDashboard.parameters,
|
|
74
75
|
},
|
|
75
76
|
];
|
|
76
77
|
});
|
|
@@ -731,7 +732,7 @@ export const selectIsDashboardDirty = createSelector(selectIsNewDashboard, selec
|
|
|
731
732
|
/**
|
|
732
733
|
* @internal
|
|
733
734
|
*/
|
|
734
|
-
export const selectDashboardWorkingDefinition = createSelector(selectPersistedDashboard, selectDashboardDescriptor, selectFilterContextDefinition, selectFilterContextIdentity, selectBasicLayout, selectDateFilterConfigOverrides, selectTabs,
|
|
735
|
+
export const selectDashboardWorkingDefinition = createSelector(selectPersistedDashboard, selectDashboardDescriptor, selectFilterContextDefinition, selectFilterContextIdentity, selectBasicLayout, selectDateFilterConfigOverrides, selectTabs, selectSmartPersistedTabsParameters, (persistedDashboard, dashboardDescriptor, filterContextDefinition, filterContextIdentity, layout, dateFilterConfig, tabs, parametersByTab) => {
|
|
735
736
|
const dashboardIdentity = {
|
|
736
737
|
ref: persistedDashboard?.ref,
|
|
737
738
|
uri: persistedDashboard?.uri,
|
|
@@ -748,7 +749,6 @@ export const selectDashboardWorkingDefinition = createSelector(selectPersistedDa
|
|
|
748
749
|
},
|
|
749
750
|
layout,
|
|
750
751
|
dateFilterConfig,
|
|
751
|
-
...(parameters.length > 0 ? { parameters } : {}),
|
|
752
752
|
...(tabs
|
|
753
753
|
? {
|
|
754
754
|
tabs: tabs.map((tab) => ({
|
|
@@ -768,6 +768,7 @@ export const selectDashboardWorkingDefinition = createSelector(selectPersistedDa
|
|
|
768
768
|
attributeFilterConfigs: tab.attributeFilterConfigs?.attributeFilterConfigs,
|
|
769
769
|
measureValueFilterConfigs: tab.measureValueFilterConfigs?.measureValueFilterConfigs,
|
|
770
770
|
filterGroupsConfig: tab.filterGroupsConfig,
|
|
771
|
+
parameters: parametersByTab[tab.localIdentifier] ?? [],
|
|
771
772
|
})),
|
|
772
773
|
}
|
|
773
774
|
: {}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
2
|
import { invariant } from "ts-invariant";
|
|
3
3
|
import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemFilterElementsBy, dashboardAttributeFilterItemFilterElementsByDate, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemValidateElementsBy, dashboardFilterLocalIdentifier, isAttributeElementsByRef, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
4
|
-
import { generateFilterLocalIdentifier } from "../../_infra/generators.js";
|
|
4
|
+
import { generateFilterLocalIdentifier, generateMeasureValueFilterLocalIdentifier, } from "../../_infra/generators.js";
|
|
5
5
|
import { getActiveTab, getTabOrActive } from "../tabsState.js";
|
|
6
6
|
import { filterContextInitialState } from "./filterContextState.js";
|
|
7
7
|
import { applyFilterContext, initializeFilterContext } from "./filterContextUtils.js";
|
|
@@ -674,7 +674,7 @@ const addMeasureValueFilter = (state, action) => {
|
|
|
674
674
|
const filter = {
|
|
675
675
|
dashboardMeasureValueFilter: {
|
|
676
676
|
measure,
|
|
677
|
-
localIdentifier: localIdentifier ??
|
|
677
|
+
localIdentifier: localIdentifier ?? generateMeasureValueFilterLocalIdentifier(measure, Math.max(0, index)),
|
|
678
678
|
title,
|
|
679
679
|
},
|
|
680
680
|
};
|
|
@@ -561,4 +561,16 @@ export declare const tabsActions: import("@reduxjs/toolkit").CaseReducerActions<
|
|
|
561
561
|
payload: string;
|
|
562
562
|
type: string;
|
|
563
563
|
}) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
|
|
564
|
+
readonly addParameter: (state: import("immer").WritableDraft<import("./tabsState.js").ITabsState>, action: {
|
|
565
|
+
payload: import("./parameters/parametersReducers.js").IAddParameterPayload;
|
|
566
|
+
type: string;
|
|
567
|
+
}) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
|
|
568
|
+
readonly setParameterRuntimeValue: (state: import("immer").WritableDraft<import("./tabsState.js").ITabsState>, action: {
|
|
569
|
+
payload: import("./parameters/parametersReducers.js").ISetParameterRuntimeValuePayload;
|
|
570
|
+
type: string;
|
|
571
|
+
}) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
|
|
572
|
+
readonly removeParameter: (state: import("immer").WritableDraft<import("./tabsState.js").ITabsState>, action: {
|
|
573
|
+
payload: import("./parameters/parametersReducers.js").IRemoveParameterPayload;
|
|
574
|
+
type: string;
|
|
575
|
+
}) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
|
|
564
576
|
}, "tabs">;
|
|
@@ -6,6 +6,7 @@ import { dateFilterConfigsReducers } from "./dateFilterConfigs/dateFilterConfigs
|
|
|
6
6
|
import { filterContextReducers } from "./filterContext/filterContextReducers.js";
|
|
7
7
|
import { layoutReducers } from "./layout/layoutReducers.js";
|
|
8
8
|
import { measureValueFilterConfigsReducers } from "./measureValueFilterConfigs/measureValueFilterConfigsReducers.js";
|
|
9
|
+
import { parametersReducers } from "./parameters/parametersReducers.js";
|
|
9
10
|
import { tabsReducers } from "./tabsReducers.js";
|
|
10
11
|
import { tabsInitialState } from "./tabsState.js";
|
|
11
12
|
const allReducers = {
|
|
@@ -16,6 +17,7 @@ const allReducers = {
|
|
|
16
17
|
...measureValueFilterConfigsReducers,
|
|
17
18
|
...filterContextReducers,
|
|
18
19
|
...layoutReducers,
|
|
20
|
+
...parametersReducers,
|
|
19
21
|
};
|
|
20
22
|
const tabsSlice = createSlice({
|
|
21
23
|
name: "tabs",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type Action, type CaseReducer } from "@reduxjs/toolkit";
|
|
2
2
|
import { type IDashboardParameter, type ObjRef } from "@gooddata/sdk-model";
|
|
3
|
-
import { type
|
|
4
|
-
type ParametersReducer<A extends Action> = CaseReducer<
|
|
3
|
+
import { type ITabsState } from "../tabsState.js";
|
|
4
|
+
type ParametersReducer<A extends Action> = CaseReducer<ITabsState, A>;
|
|
5
5
|
/**
|
|
6
|
-
* Add a parameter to the
|
|
6
|
+
* Add a parameter to the active tab. Initial `runtimeOverride` is `parameter.value`
|
|
7
7
|
* (when pinned) otherwise the workspace default supplied by the caller.
|
|
8
8
|
*
|
|
9
9
|
* @alpha
|
|
@@ -38,9 +38,5 @@ export declare const parametersReducers: {
|
|
|
38
38
|
payload: IRemoveParameterPayload;
|
|
39
39
|
type: string;
|
|
40
40
|
}>;
|
|
41
|
-
setParameterEntries: ParametersReducer<{
|
|
42
|
-
payload: IDashboardParameterEntry[];
|
|
43
|
-
type: string;
|
|
44
|
-
}>;
|
|
45
41
|
};
|
|
46
42
|
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { areObjRefsEqual } from "@gooddata/sdk-model";
|
|
3
|
+
import { getActiveTab } from "../tabsState.js";
|
|
4
|
+
import { parametersInitialState } from "./parametersState.js";
|
|
5
|
+
const addParameter = (state, action) => {
|
|
6
|
+
const activeTab = getActiveTab(state);
|
|
7
|
+
if (!activeTab) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const { parameter, workspaceDefault } = action.payload;
|
|
11
|
+
const tabParameters = activeTab.parameters ?? parametersInitialState;
|
|
12
|
+
if (tabParameters.parameters.some((entry) => areObjRefsEqual(entry.parameter.ref, parameter.ref))) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
activeTab.parameters = {
|
|
16
|
+
parameters: [
|
|
17
|
+
...tabParameters.parameters,
|
|
18
|
+
{ parameter, runtimeOverride: parameter.value ?? workspaceDefault },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const setParameterRuntimeValue = (state, action) => {
|
|
23
|
+
const activeTab = getActiveTab(state);
|
|
24
|
+
if (!activeTab?.parameters) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { ref, value } = action.payload;
|
|
28
|
+
const entry = activeTab.parameters.parameters.find((item) => areObjRefsEqual(item.parameter.ref, ref));
|
|
29
|
+
if (entry) {
|
|
30
|
+
entry.runtimeOverride = value;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const removeParameter = (state, action) => {
|
|
34
|
+
const activeTab = getActiveTab(state);
|
|
35
|
+
if (!activeTab?.parameters) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
activeTab.parameters = {
|
|
39
|
+
parameters: activeTab.parameters.parameters.filter((entry) => !areObjRefsEqual(entry.parameter.ref, action.payload.ref)),
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export const parametersReducers = {
|
|
43
|
+
addParameter,
|
|
44
|
+
setParameterRuntimeValue,
|
|
45
|
+
removeParameter,
|
|
46
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type IDashboardParameter, type IInsightParameterValue, type ObjRef } from "@gooddata/sdk-model";
|
|
2
|
+
import { type DashboardSelector } from "../../types.js";
|
|
3
|
+
import { type IDashboardParameterEntry } from "./parametersState.js";
|
|
4
|
+
/**
|
|
5
|
+
* Returns the persisted-shape parameter entries currently held by the active tab.
|
|
6
|
+
*
|
|
7
|
+
* @alpha
|
|
8
|
+
*/
|
|
9
|
+
export declare const selectDashboardParameters: DashboardSelector<IDashboardParameter[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns currently active parameter references on the active tab.
|
|
12
|
+
*
|
|
13
|
+
* @alpha
|
|
14
|
+
*/
|
|
15
|
+
export declare const selectActiveParameterRefKeys: DashboardSelector<ReadonlySet<string>>;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the full per-parameter entries (persisted shape + ephemeral `runtimeOverride`) for the
|
|
18
|
+
* active tab.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export declare const selectDashboardParameterEntries: DashboardSelector<IDashboardParameterEntry[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns a selector that yields the entry held by the active tab for a given parameter ref,
|
|
25
|
+
* or `undefined` if no such entry exists.
|
|
26
|
+
*
|
|
27
|
+
* @alpha
|
|
28
|
+
*/
|
|
29
|
+
export declare const selectDashboardParameterEntryByRef: (ref: ObjRef) => DashboardSelector<IDashboardParameterEntry | undefined>;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a selector that yields the current `runtimeOverride` for a given parameter ref on the
|
|
32
|
+
* active tab, or `undefined` if the active tab does not hold an entry for that ref.
|
|
33
|
+
*
|
|
34
|
+
* @alpha
|
|
35
|
+
*/
|
|
36
|
+
export declare const selectParameterRuntimeOverrideByRef: (ref: ObjRef) => DashboardSelector<number | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Computes the dashboard parameters keyed by tab `localIdentifier` in the shape that would be
|
|
39
|
+
* persisted on save right now.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Smart persistence applies independently per tab: `value` is dropped when equal to the
|
|
43
|
+
* workspace default, `label` is dropped when equal to the parameter title, all per tab.
|
|
44
|
+
* Non-resolved entries (catalog not loaded, gated off, ref missing) are emitted verbatim from
|
|
45
|
+
* the previously persisted entry on the same tab when available. V1 fallback applies when no
|
|
46
|
+
* tab in the persisted dashboard carries `parameters` — the persisted root array is used as the
|
|
47
|
+
* persistence source for every tab.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export declare const selectSmartPersistedTabsParameters: DashboardSelector<Record<string, IDashboardParameter[]>>;
|
|
52
|
+
/**
|
|
53
|
+
* Returns true if the dashboard parameters that would be persisted differ from the persisted
|
|
54
|
+
* version on any tab.
|
|
55
|
+
*
|
|
56
|
+
* @alpha
|
|
57
|
+
*/
|
|
58
|
+
export declare const selectIsParametersChanged: DashboardSelector<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Returns the parameter values to inject into the widget's `IExecutionConfig.parameterValues`.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* The widget's owning tab is resolved from layout, then the result is the intersection of that
|
|
64
|
+
* tab's parameter entries and the parameters referenced by the widget's insight (per
|
|
65
|
+
* `insightParameters`). Dashboard parameters not referenced by the widget's insight are excluded
|
|
66
|
+
* so that adding/removing unrelated parameters does not invalidate the widget's `defFingerprint`.
|
|
67
|
+
* Returns an empty array when `enableParameters` is off so persisted parameter values cannot
|
|
68
|
+
* silently affect execution while the UI is hidden.
|
|
69
|
+
*
|
|
70
|
+
* @alpha
|
|
71
|
+
*/
|
|
72
|
+
export declare const selectEffectiveParameterValuesForWidget: (ref: ObjRef | undefined) => DashboardSelector<IInsightParameterValue[]>;
|