@gooddata/sdk-ui-dashboard 11.35.0-alpha.6 → 11.35.0
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/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 +1 -1
- package/esm/index.js +1 -1
- 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/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/filtering/dashboardFilterSelectors.d.ts +1 -1
- package/esm/model/store/filtering/dashboardFilterSelectors.js +8 -5
- package/esm/model/store/tabs/filterContext/filterContextReducers.js +2 -2
- 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/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/sdk-ui-dashboard.d.ts +17 -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.35.0
|
|
3
|
+
export const LIB_VERSION = "11.35.0";
|
|
4
4
|
export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
|
|
5
5
|
export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
3
|
import { NotSupported } from "@gooddata/sdk-backend-spi";
|
|
3
|
-
import { dashboardAttributeFilterItemFilterElementsBy, dashboardFilterLocalIdentifier, filterAttributeElements, filterLocalIdentifier, filterObjRef, isAbsoluteDateFilter, isAllTimeDateFilter, isArbitraryAttributeFilter, isAttributeFilterWithSelection, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isMatchAttributeFilter, isNegativeAttributeFilter, isRelativeBoundedDateFilter, isRelativeDateFilter, isTempFilterContext, newAbsoluteDashboardDateFilter, newAllTimeDashboardDateFilter, newRelativeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
4
|
+
import { dashboardAttributeFilterItemFilterElementsBy, dashboardFilterLocalIdentifier, filterAttributeElements, filterLocalIdentifier, filterObjRef, isAbsoluteDateFilter, isAllTimeDateFilter, isArbitraryAttributeFilter, isAttributeFilterWithSelection, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isMatchAttributeFilter, isMeasureValueFilter, isNegativeAttributeFilter, isObjRef, isRelativeBoundedDateFilter, isRelativeDateFilter, isTempFilterContext, measureValueFilterConditions, measureValueFilterMeasure, newAbsoluteDashboardDateFilter, newAllTimeDashboardDateFilter, newRelativeDashboardDateFilter, } from "@gooddata/sdk-model";
|
|
4
5
|
import { createDefaultFilterContext } from "./defaultFilterContext.js";
|
|
5
6
|
/**
|
|
6
7
|
* Given a dashboard, this function will inspect its filter context and always return a valid instance of IFilterContextDefinition to use.
|
|
@@ -154,5 +155,21 @@ export function dashboardFilterToFilterContextItem(filter, keepDatasets) {
|
|
|
154
155
|
else if (isRelativeDateFilter(filter)) {
|
|
155
156
|
return newRelativeDashboardDateFilter(filter.relativeDateFilter.granularity, filter.relativeDateFilter.from, filter.relativeDateFilter.to, keepDatasets ? filter.relativeDateFilter.dataSet : undefined, filter.relativeDateFilter.localIdentifier, isRelativeBoundedDateFilter(filter) ? filter.relativeDateFilter.boundedFilter : undefined, filter.relativeDateFilter.emptyValueHandling);
|
|
156
157
|
}
|
|
158
|
+
else if (isMeasureValueFilter(filter)) {
|
|
159
|
+
const measure = measureValueFilterMeasure(filter);
|
|
160
|
+
// Dashboard MVF requires an ObjRef (catalog metric reference); LocalIdRef cannot
|
|
161
|
+
// survive the round-trip from execution to filter context.
|
|
162
|
+
if (!isObjRef(measure)) {
|
|
163
|
+
throw new NotSupported(`Unsupported filter type! Please provide valid dashboard filter. Filter: ${JSON.stringify(filter)}`);
|
|
164
|
+
}
|
|
165
|
+
const conditions = measureValueFilterConditions(filter);
|
|
166
|
+
return {
|
|
167
|
+
dashboardMeasureValueFilter: {
|
|
168
|
+
measure,
|
|
169
|
+
localIdentifier: filter.measureValueFilter.localIdentifier ?? uuidv4(),
|
|
170
|
+
...(conditions ? { conditions } : {}),
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
157
174
|
throw new NotSupported(`Unsupported filter type! Please provide valid dashboard filter. Filter: ${JSON.stringify(filter)}`);
|
|
158
175
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// (C) 2024-2026 GoodData Corporation
|
|
2
2
|
import { useIntl } from "react-intl";
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
|
-
import { dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemTitle, getAttributeElementsItems, isAllTimeDashboardDateFilter, isAllValuesDashboardAttributeFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMatchAttributeFilter, serializeObjRef, } from "@gooddata/sdk-model";
|
|
5
|
-
import { DateFilterHelpers, getAttributeFilterSubtitle, getLocalizedIcuDateFormatPattern, getTextFilterStateText, } from "@gooddata/sdk-ui-filters";
|
|
4
|
+
import { areObjRefsEqual, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemTitle, getAttributeElementsItems, isAllDashboardMeasureValueFilter, isAllTimeDashboardDateFilter, isAllValuesDashboardAttributeFilter, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, objRefToString, serializeObjRef, } from "@gooddata/sdk-model";
|
|
5
|
+
import { DateFilterHelpers, getAttributeFilterSubtitle, getLocalizedIcuDateFormatPattern, getMeasureValueFilterConditionLabel, getTextFilterStateText, } from "@gooddata/sdk-ui-filters";
|
|
6
6
|
import { useDashboardSelector } from "../../model/react/DashboardStoreProvider.js";
|
|
7
|
-
import { selectAllCatalogAttributesMap } from "../../model/store/catalog/catalogSelectors.js";
|
|
8
|
-
import { selectLocale, selectSettings } from "../../model/store/config/configSelectors.js";
|
|
7
|
+
import { selectAllCatalogAttributesMap, selectCatalogMeasures, } from "../../model/store/catalog/catalogSelectors.js";
|
|
8
|
+
import { selectLocale, selectSeparators, selectSettings } from "../../model/store/config/configSelectors.js";
|
|
9
9
|
import { convertDateFilterConfigToDateFilterOptions } from "../dateFilterConfig/dateFilterConfigConverters.js";
|
|
10
10
|
import { matchDateFilterToDateFilterOptionWithPreference } from "../dateFilterConfig/dateFilterOptionMapping.js";
|
|
11
11
|
import { defaultDateFilterConfig } from "../dateFilterConfig/defaultConfig.js";
|
|
@@ -13,6 +13,10 @@ import { ensureAllTimeFilterForExport } from "../exportUtils/filterUtils.js";
|
|
|
13
13
|
import { useAttributeFilterDisplayFormFromMap } from "./useAttributeFilterDisplayFormFromMap.js";
|
|
14
14
|
import { useCommonDateFilterTitle } from "./useCommonDateFilterTitle.js";
|
|
15
15
|
import { useDateFiltersTitles } from "./useDateFiltersTitles.js";
|
|
16
|
+
const PERCENT_FORMAT_REGEX = /%/;
|
|
17
|
+
function isPercentageFormat(format) {
|
|
18
|
+
return !!format && PERCENT_FORMAT_REGEX.test(format);
|
|
19
|
+
}
|
|
16
20
|
/**
|
|
17
21
|
* Hook that gathers all dependencies needed for filter naming transformations.
|
|
18
22
|
* Reusable across different hooks that need to transform filters to namings.
|
|
@@ -29,6 +33,8 @@ function useFilterNamingDependencies(filtersForTitles) {
|
|
|
29
33
|
: settings.responsiveUiDateFormat;
|
|
30
34
|
const getAttributeFilterDisplayFormFromMap = useAttributeFilterDisplayFormFromMap();
|
|
31
35
|
const attrMap = useDashboardSelector(selectAllCatalogAttributesMap);
|
|
36
|
+
const measures = useDashboardSelector(selectCatalogMeasures);
|
|
37
|
+
const separators = useDashboardSelector(selectSeparators);
|
|
32
38
|
const dateFiltersForTitles = filtersForTitles.filter(isDashboardDateFilterWithDimension);
|
|
33
39
|
const commonDateFilterTitle = useCommonDateFilterTitle(intl);
|
|
34
40
|
const allDateFiltersTitlesObj = useDateFiltersTitles(dateFiltersForTitles, intl);
|
|
@@ -38,6 +44,8 @@ function useFilterNamingDependencies(filtersForTitles) {
|
|
|
38
44
|
dateFormat,
|
|
39
45
|
getAttributeFilterDisplayFormFromMap,
|
|
40
46
|
attrMap,
|
|
47
|
+
measures,
|
|
48
|
+
separators,
|
|
41
49
|
commonDateFilterTitle,
|
|
42
50
|
allDateFiltersTitlesObj,
|
|
43
51
|
};
|
|
@@ -47,7 +55,7 @@ function useFilterNamingDependencies(filtersForTitles) {
|
|
|
47
55
|
* Extracted to avoid code duplication between useFiltersNamings and useFiltersByTabNamings.
|
|
48
56
|
*/
|
|
49
57
|
function transformFiltersToNamings(filtersToDisplay, deps) {
|
|
50
|
-
const { intl, dateFormat, getAttributeFilterDisplayFormFromMap, attrMap, commonDateFilterTitle, allDateFiltersTitlesObj, } = deps;
|
|
58
|
+
const { intl, dateFormat, getAttributeFilterDisplayFormFromMap, attrMap, measures, separators, commonDateFilterTitle, allDateFiltersTitlesObj, } = deps;
|
|
51
59
|
// we want to show all time filter in the list of filters even if it is not stored
|
|
52
60
|
const extendedFiltersToDisplay = ensureAllTimeFilterForExport(filtersToDisplay);
|
|
53
61
|
return extendedFiltersToDisplay.map((filter) => {
|
|
@@ -127,6 +135,25 @@ function transformFiltersToNamings(filtersToDisplay, deps) {
|
|
|
127
135
|
subtitle,
|
|
128
136
|
};
|
|
129
137
|
}
|
|
138
|
+
else if (isDashboardMeasureValueFilter(filter)) {
|
|
139
|
+
const { measure, localIdentifier, title: customTitle, conditions, } = filter.dashboardMeasureValueFilter;
|
|
140
|
+
const catalogMetric = measures.find((m) => areObjRefsEqual(m.measure.ref, measure));
|
|
141
|
+
const defaultTitle = catalogMetric?.measure.title ?? objRefToString(measure);
|
|
142
|
+
const title = customTitle ?? defaultTitle;
|
|
143
|
+
const format = catalogMetric?.measure.format;
|
|
144
|
+
const usePercentage = isPercentageFormat(format);
|
|
145
|
+
const subtitle = getMeasureValueFilterConditionLabel(intl, conditions, {
|
|
146
|
+
usePercentage,
|
|
147
|
+
separators,
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
type: "measureValueFilter",
|
|
151
|
+
all: isAllDashboardMeasureValueFilter(filter),
|
|
152
|
+
id: localIdentifier,
|
|
153
|
+
title,
|
|
154
|
+
subtitle,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
130
157
|
else if (isDashboardMatchAttributeFilter(filter)) {
|
|
131
158
|
const { operator: matchOperator, literal, negativeSelection, displayForm, } = filter.matchAttributeFilter;
|
|
132
159
|
const filterDisplayForm = getAttributeFilterDisplayFormFromMap(displayForm);
|
package/esm/index.d.ts
CHANGED
|
@@ -197,7 +197,7 @@ export { newDrillToSameDashboardHandler } from "./model/eventHandlers/drillToSam
|
|
|
197
197
|
export { type IHeadlessDashboardConfig, type IMonitoredAction, HeadlessDashboard, } from "./model/headlessDashboard/HeadlessDashboard.js";
|
|
198
198
|
export { isTemporaryIdentity, getWidgetTitle } from "./model/utils/dashboardItemUtils.js";
|
|
199
199
|
export { existBlacklistHierarchyPredicate } from "./model/utils/attributeHierarchyUtils.js";
|
|
200
|
-
export {
|
|
200
|
+
export { getAttributeFilters, removeIgnoredWidgetFilters } from "./model/utils/widgetFilters.js";
|
|
201
201
|
export { getAuthor } from "./model/utils/author.js";
|
|
202
202
|
export type { ICustomComponentBase, IDraggingComponentProps, IDropTargetComponentProps, IAttributeFilterDraggingComponentProps, IDateFilterDraggingComponentProps, IInsightDraggingComponentProps, IKpiDraggingComponentProps, IRichTextDraggingComponentProps, IVisualizationSwitcherDraggingComponentProps, IDashboardLayoutDraggingComponentProps, ICustomDraggingComponentProps, AttributeFilterDraggingComponent, DateFilterDraggingComponent, InsightDraggingComponent, KpiDraggingComponent, RichTextDraggingComponent, VisualizationSwitcherDraggingComponent, DashboardLayoutDraggingComponent, CustomDraggingComponent, AttributeFilterDraggableComponent, DateFilterDraggableComponent, InsightDraggableComponent, KpiDraggableComponent, RichTextDraggableComponent, VisualizationSwitcherDraggableComponent, DashboardLayoutDraggableComponent, CustomDraggableComponent, DraggableComponent, DropTarget, ICreatePanelItemComponentProps, CustomCreatePanelItemComponent, CreatableByDragComponent, CreatablePlaceholderComponent, CustomWidgetConfigPanelComponent, IWidgetConfigPanelProps, ConfigurableWidget, AttributeFilterComponentSet, DateFilterComponentSet, InsightWidgetComponentSet, RichTextWidgetComponentSet, VisualizationSwitcherWidgetComponentSet, DashboardLayoutWidgetComponentSet, CustomWidgetComponentSet, InsightComponentSetProvider, } from "./presentation/componentDefinition/types.js";
|
|
203
203
|
export { renderModeAware } from "./presentation/componentDefinition/renderModeAware.js";
|
package/esm/index.js
CHANGED
|
@@ -140,7 +140,7 @@ export { newDrillToSameDashboardHandler } from "./model/eventHandlers/drillToSam
|
|
|
140
140
|
export { HeadlessDashboard, } from "./model/headlessDashboard/HeadlessDashboard.js";
|
|
141
141
|
export { isTemporaryIdentity, getWidgetTitle } from "./model/utils/dashboardItemUtils.js";
|
|
142
142
|
export { existBlacklistHierarchyPredicate } from "./model/utils/attributeHierarchyUtils.js";
|
|
143
|
-
export {
|
|
143
|
+
export { getAttributeFilters, removeIgnoredWidgetFilters } from "./model/utils/widgetFilters.js";
|
|
144
144
|
export { getAuthor } from "./model/utils/author.js";
|
|
145
145
|
export { renderModeAware } from "./presentation/componentDefinition/renderModeAware.js";
|
|
146
146
|
export { Dashboard } from "./presentation/dashboard/Dashboard.js";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { useEffect, useMemo } from "react";
|
|
3
3
|
import stringify from "json-stable-stringify";
|
|
4
4
|
import { ClientFormatterFacade } from "@gooddata/number-formatter";
|
|
5
|
-
import { isAllValuesDashboardAttributeFilter, newAttribute, } from "@gooddata/sdk-model";
|
|
5
|
+
import { isAllDashboardMeasureValueFilter, isAllValuesDashboardAttributeFilter, newAttribute, } from "@gooddata/sdk-model";
|
|
6
6
|
import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
7
|
-
import { dashboardAttributeFilterItemToAttributeFilter } from "../../../converters/filterConverters.js";
|
|
7
|
+
import { dashboardAttributeFilterItemToAttributeFilter, dashboardMeasureValueFilterToMeasureValueFilter, } from "../../../converters/filterConverters.js";
|
|
8
8
|
import { useAttribute } from "../../hooks/useAttribute.js";
|
|
9
9
|
import { useDateAttribute } from "../../hooks/useDateAttribute.js";
|
|
10
10
|
import { useRelevantFilters } from "../../hooks/useRelevantFilters.js";
|
|
@@ -18,14 +18,15 @@ export function useChangeAnalysis() {
|
|
|
18
18
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
19
19
|
}, [state.selectedUpdated]);
|
|
20
20
|
const filters = useRelevantFilters();
|
|
21
|
+
const measureValueFilters = state.measureValueFilters;
|
|
21
22
|
const loading = state.relevantStatus === "loading" || state.relevantStatus === "pending";
|
|
22
|
-
const results = useChangeAnalysisResults(definition, attributes, filters, loading);
|
|
23
|
+
const results = useChangeAnalysisResults(definition, attributes, filters, measureValueFilters, loading);
|
|
23
24
|
const list = useKdaStateWithList(results, definition);
|
|
24
25
|
useEffect(() => {
|
|
25
26
|
setState(list);
|
|
26
27
|
}, [list, setState]);
|
|
27
28
|
}
|
|
28
|
-
function useChangeAnalysisResults(definition, attrs, attrFilters, loading) {
|
|
29
|
+
function useChangeAnalysisResults(definition, attrs, attrFilters, measureValueFilters, loading) {
|
|
29
30
|
const backend = useBackendStrict();
|
|
30
31
|
const workspace = useWorkspaceStrict();
|
|
31
32
|
const from = definition?.range[0].date;
|
|
@@ -38,6 +39,13 @@ function useChangeAnalysisResults(definition, attrs, attrFilters, loading) {
|
|
|
38
39
|
.map((f) => stringify(f))
|
|
39
40
|
.join();
|
|
40
41
|
}, [attrFilters]);
|
|
42
|
+
// Use the same fingerprint approach for MVF so cancelable-promise recomputes only when conditions actually change.
|
|
43
|
+
const measureValueFiltersFingerprint = useMemo(() => {
|
|
44
|
+
return measureValueFilters
|
|
45
|
+
.filter((f) => !isAllDashboardMeasureValueFilter(f))
|
|
46
|
+
.map((f) => stringify(f))
|
|
47
|
+
.join();
|
|
48
|
+
}, [measureValueFilters]);
|
|
41
49
|
const dateAttribute = dateAttributeFinder(definition?.dateAttribute);
|
|
42
50
|
const shouldComputeChangeAnalysis = !!definition && !!dateAttribute && !loading;
|
|
43
51
|
const { includeTags, excludeTags } = useTags();
|
|
@@ -51,9 +59,16 @@ function useChangeAnalysisResults(definition, attrs, attrFilters, loading) {
|
|
|
51
59
|
return attr ? newAttribute(ref) : null;
|
|
52
60
|
})
|
|
53
61
|
.filter(Boolean);
|
|
54
|
-
const
|
|
62
|
+
const attributeExecutionFilters = attrFilters
|
|
55
63
|
.filter((f) => !isAllValuesDashboardAttributeFilter(f))
|
|
56
64
|
.map(dashboardAttributeFilterItemToAttributeFilter);
|
|
65
|
+
const measureValueExecutionFilters = measureValueFilters
|
|
66
|
+
.filter((f) => !isAllDashboardMeasureValueFilter(f))
|
|
67
|
+
.map(dashboardMeasureValueFilterToMeasureValueFilter);
|
|
68
|
+
const filters = [
|
|
69
|
+
...attributeExecutionFilters,
|
|
70
|
+
...measureValueExecutionFilters,
|
|
71
|
+
];
|
|
57
72
|
return backend
|
|
58
73
|
.workspace(workspace)
|
|
59
74
|
.keyDriverAnalysis()
|
|
@@ -83,6 +98,7 @@ function useChangeAnalysisResults(definition, attrs, attrFilters, loading) {
|
|
|
83
98
|
loading,
|
|
84
99
|
dateAttribute,
|
|
85
100
|
attributeFiltersFingerprint,
|
|
101
|
+
measureValueFiltersFingerprint,
|
|
86
102
|
includeTags,
|
|
87
103
|
excludeTags,
|
|
88
104
|
]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DashboardAttributeFilterItem, type ICatalogDateAttribute, type ISeparators, type ObjRef } from "@gooddata/sdk-model";
|
|
1
|
+
import { type DashboardAttributeFilterItem, type ICatalogDateAttribute, type IDashboardMeasureValueFilter, type ISeparators, type ObjRef } from "@gooddata/sdk-model";
|
|
2
2
|
import { type IUiListboxInteractiveItem } from "@gooddata/sdk-ui-kit";
|
|
3
3
|
import { type DeepReadonly, type IKdaDataPoint, type IKdaDefinition, type KdaPeriodType } from "./types.js";
|
|
4
4
|
export interface IKdaDateOptions {
|
|
@@ -48,6 +48,11 @@ export interface IKdaState {
|
|
|
48
48
|
selectedStatus: KdaAsyncStatus;
|
|
49
49
|
selectedError: Error | undefined;
|
|
50
50
|
attributeFilters: DashboardAttributeFilterItem[];
|
|
51
|
+
/**
|
|
52
|
+
* Dashboard measure value filters propagated to the change analysis computation.
|
|
53
|
+
* Not editable in the dialog UI — these flow through from the dashboard filter context.
|
|
54
|
+
*/
|
|
55
|
+
measureValueFilters: IDashboardMeasureValueFilter[];
|
|
51
56
|
items: IUiListboxInteractiveItem<IKdaItem>[];
|
|
52
57
|
itemsStatus: KdaAsyncStatus;
|
|
53
58
|
itemsError: Error | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2025-2026 GoodData Corporation
|
|
3
3
|
import { useMemo } from "react";
|
|
4
|
-
import { isAllValuesDashboardAttributeFilter } from "@gooddata/sdk-model";
|
|
4
|
+
import { isAllDashboardMeasureValueFilter, isAllValuesDashboardAttributeFilter, } from "@gooddata/sdk-model";
|
|
5
5
|
import { KdaStateProvider } from "./KdaState.js";
|
|
6
6
|
export function KdaProvider({ children, definition, separators, includeTags, excludeTags }) {
|
|
7
7
|
const state = useMemo(() => {
|
|
@@ -15,6 +15,9 @@ export function KdaProvider({ children, definition, separators, includeTags, exc
|
|
|
15
15
|
definitionStatus: "success",
|
|
16
16
|
isMinimized: true,
|
|
17
17
|
attributeFilters: (definition.filters?.slice() ?? []).filter((f) => !isAllValuesDashboardAttributeFilter(f)),
|
|
18
|
+
// MVF flows through from the dashboard filter context; noop "All" MVFs are dropped
|
|
19
|
+
// so the backend only receives meaningful conditions.
|
|
20
|
+
measureValueFilters: (definition.measureValueFilters?.slice() ?? []).filter((f) => !isAllDashboardMeasureValueFilter(f)),
|
|
18
21
|
};
|
|
19
22
|
}, [definition, separators, includeTags, excludeTags]);
|
|
20
23
|
return _jsx(KdaStateProvider, { value: state, children: children });
|
package/esm/kdaDialog/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DashboardAttributeFilterItem, type IAttributeDescriptorBody, type IMeasure, type ObjRef } from "@gooddata/sdk-model";
|
|
1
|
+
import { type DashboardAttributeFilterItem, type IAttributeDescriptorBody, type IDashboardMeasureValueFilter, type IMeasure, type ObjRef } from "@gooddata/sdk-model";
|
|
2
2
|
import { type OverlayController } from "@gooddata/sdk-ui-kit";
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
@@ -60,9 +60,20 @@ export interface IKdaDefinition {
|
|
|
60
60
|
*/
|
|
61
61
|
metrics?: IMeasure[];
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Attribute filters to apply.
|
|
64
|
+
*
|
|
65
|
+
* Attribute filters drive both the KDA "segment-by attribute" UI in the dialog and the
|
|
66
|
+
* scope of the change analysis computation.
|
|
64
67
|
*/
|
|
65
68
|
filters?: DashboardAttributeFilterItem[];
|
|
69
|
+
/**
|
|
70
|
+
* Measure value filters to apply to the change analysis computation.
|
|
71
|
+
*
|
|
72
|
+
* They are not editable in the dialog UI (KDA segments by attribute, not by metric value),
|
|
73
|
+
* but the dashboard MVFs are propagated to the backend so the computation respects the
|
|
74
|
+
* same metric-driven scope as the rest of the dashboard.
|
|
75
|
+
*/
|
|
76
|
+
measureValueFilters?: IDashboardMeasureValueFilter[];
|
|
66
77
|
/**
|
|
67
78
|
* Date attribute
|
|
68
79
|
*/
|
|
@@ -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
|
+
};
|
|
@@ -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,
|
|
@@ -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
|
};
|
|
@@ -7,4 +7,4 @@ export declare function removeIgnoredWidgetFilters(filters: FilterContextItem[],
|
|
|
7
7
|
/**
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function getAttributeFilters(filters: FilterContextItem[]): DashboardAttributeFilterItem[];
|
|
@@ -9,6 +9,7 @@ import { AttributesDropdown } from "../../filterBar/attributeFilter/addAttribute
|
|
|
9
9
|
import { useAutomationFilters, useAutomationFiltersByTab } from "../useAutomationFilters.js";
|
|
10
10
|
import { AutomationAttributeFilter } from "./AutomationAttributeFilter.js";
|
|
11
11
|
import { AutomationDateFilter } from "./AutomationDateFilter.js";
|
|
12
|
+
import { AutomationMeasureValueFilter } from "./AutomationMeasureValueFilter.js";
|
|
12
13
|
const COLLAPSED_FILTERS_COUNT = 2;
|
|
13
14
|
function AutomationCheckboxOrNote({ isDashboardAutomation, storeFilters, handleStoreFiltersChange, handleKeyDown, automationFilterSelectTooltipId, }) {
|
|
14
15
|
const intl = useIntl();
|
|
@@ -51,6 +52,7 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
51
52
|
const filters = shouldRenderByTab ? [] : flatFiltersData.visibleFilters;
|
|
52
53
|
const attributes = shouldRenderByTab ? [] : flatFiltersData.attributes;
|
|
53
54
|
const dateDatasets = shouldRenderByTab ? [] : flatFiltersData.dateDatasets;
|
|
55
|
+
const measures = shouldRenderByTab ? [] : flatFiltersData.measures;
|
|
54
56
|
const handleChangeFilter = shouldRenderByTab
|
|
55
57
|
? () => { } // Not used in tab mode
|
|
56
58
|
: flatFiltersData.handleChangeFilter;
|
|
@@ -109,7 +111,7 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
109
111
|
// Add button for each tab section
|
|
110
112
|
addFilterButton: _jsx(AttributesDropdown, { id: `${AUTOMATION_FILTERS_DIALOG_ID}-${tab.tabId}`, onClose: () => { }, onSelect: (value) => {
|
|
111
113
|
tabFiltersData.handleTabFilterAdd(tab.tabId, value, tab.attributes, tab.dateDatasets);
|
|
112
|
-
}, attributes: tab.attributes, dateDatasets: tab.dateDatasets, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, tab.dateConfigs), accessibilityConfig: {
|
|
114
|
+
}, attributes: tab.attributes, dateDatasets: tab.dateDatasets, measures: tab.measures, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, tab.dateConfigs), accessibilityConfig: {
|
|
113
115
|
ariaLabelledBy: AUTOMATION_FILTERS_DIALOG_TITLE_ID,
|
|
114
116
|
searchAriaLabel: searchAriaLabel,
|
|
115
117
|
}, DropdownButtonComponent: ({ buttonRef, isOpen, onClick }) => (_jsx(UiTooltip, { arrowPlacement: "left", triggerBy: ["hover", "focus"], content: tabTooltipText, anchor: _jsx(ButtonDisabledFocusableWrapper, { isDisabled: isTabAddButtonDisabled, ariaLabel: tabTooltipText, onRefSet: (element) => setAddFilterButtonRefs(element, buttonRef), children: _jsx(UiIconButton, { icon: "plus", label: tabTooltipText, onClick: onClick, variant: "tertiary", isDisabled: isTabAddButtonDisabled, ref: (element) => {
|
|
@@ -136,7 +138,7 @@ export function AutomationFiltersSelect({ availableFilters = [], selectedFilters
|
|
|
136
138
|
}), isExpanded || !isExpandable ? (_jsx(AttributesDropdown, { id: AUTOMATION_FILTERS_DIALOG_ID, onClose: () => { }, onSelect: (value) => {
|
|
137
139
|
handleAddFilter(value, attributes, dateDatasets);
|
|
138
140
|
setIsExpanded(true);
|
|
139
|
-
}, attributes: attributes, dateDatasets: dateDatasets, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, dateConfigs), accessibilityConfig: {
|
|
141
|
+
}, attributes: attributes, dateDatasets: dateDatasets, measures: measures, openOnInit: false, overlayPositionType: overlayPositionType, className: "gd-automation-filters__dropdown s-automation-filters-add-filter-dropdown", getCustomItemTitle: (item) => getCatalogItemCustomTitle(item, availableFilters, dateConfigs), accessibilityConfig: {
|
|
140
142
|
ariaLabelledBy: AUTOMATION_FILTERS_DIALOG_TITLE_ID,
|
|
141
143
|
searchAriaLabel: searchAriaLabel,
|
|
142
144
|
}, DropdownButtonComponent: ({ buttonRef, isOpen, onClick }) => (_jsx(UiTooltip, { arrowPlacement: "left", triggerBy: ["hover", "focus"], content: tooltipText, anchor: _jsx(ButtonDisabledFocusableWrapper, { isDisabled: isAddButtonDisabled, ariaLabel: tooltipText, onRefSet: (element) => setAddFilterButtonRefs(element, buttonRef), children: _jsx(UiIconButton, { icon: "plus", label: tooltipText, onClick: onClick, variant: "tertiary", isDisabled: isAddButtonDisabled, ref: (element) => {
|
|
@@ -170,6 +172,10 @@ function AutomationFilter({ filter, attributeConfigs, onChange, onDelete, isComm
|
|
|
170
172
|
const isLocked = lockedFilters.some((f) => dashboardFilterLocalIdentifier(f) === dashboardFilterLocalIdentifier(filter));
|
|
171
173
|
return (_jsx(AutomationDateFilter, { filter: filter, onChange: onChange, onDelete: onDelete, isLocked: isLocked, isCommonDateFilter: isCommonDateFilter, overlayPositionType: overlayPositionType, readonly: isReadOnly, tabId: tabId }, filter.dateFilter.localIdentifier));
|
|
172
174
|
}
|
|
175
|
+
else if (isDashboardMeasureValueFilter(filter)) {
|
|
176
|
+
const isLocked = lockedFilters.some((f) => dashboardFilterLocalIdentifier(f) === dashboardFilterLocalIdentifier(filter));
|
|
177
|
+
return (_jsx(AutomationMeasureValueFilter, { filter: filter, onChange: onChange, onDelete: onDelete, isLocked: isLocked, overlayPositionType: overlayPositionType, readonly: isReadOnly }, filter.dashboardMeasureValueFilter.localIdentifier));
|
|
178
|
+
}
|
|
173
179
|
return null;
|
|
174
180
|
}
|
|
175
181
|
/**
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type FilterContextItem, type IDashboardMeasureValueFilter } from "@gooddata/sdk-model";
|
|
2
|
+
import { type OverlayPositionType } from "@gooddata/sdk-ui-kit";
|
|
3
|
+
export declare function AutomationMeasureValueFilter({ filter, onChange, onDelete, isLocked, overlayPositionType, readonly }: {
|
|
4
|
+
filter: IDashboardMeasureValueFilter;
|
|
5
|
+
onChange: (filter: FilterContextItem) => void;
|
|
6
|
+
onDelete: (filter: FilterContextItem) => void;
|
|
7
|
+
isLocked?: boolean;
|
|
8
|
+
overlayPositionType?: OverlayPositionType;
|
|
9
|
+
readonly?: boolean;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { MeasureValueFilter } from "@gooddata/sdk-ui-filters";
|
|
6
|
+
import { UiChip, UiTooltip, isActionKey, useIdPrefixed, } from "@gooddata/sdk-ui-kit";
|
|
7
|
+
import { getSharedDashboardMvfProps, normalizeMeasureValueFilterConditions, useDashboardMeasureValueFilterData, } from "../../filterBar/measureValueFilter/useDashboardMeasureValueFilterData.js";
|
|
8
|
+
import { AutomationMeasureValueFilterProvider, useAutomationMeasureValueFilterContext, } from "./AutomationMeasureValueFilterContext.js";
|
|
9
|
+
export function AutomationMeasureValueFilter({ filter, onChange, onDelete, isLocked, overlayPositionType, readonly, }) {
|
|
10
|
+
const intl = useIntl();
|
|
11
|
+
const deleteAriaLabel = intl.formatMessage({ id: "dialogs.automation.filters.deleteAriaLabel" });
|
|
12
|
+
const deleteTooltipContent = intl.formatMessage({ id: "dialogs.automation.filters.deleteTooltip" });
|
|
13
|
+
const lockedTooltipContent = intl.formatMessage({ id: "dialogs.automation.filters.lockedTooltip" });
|
|
14
|
+
// Automation/scheduling has no "Apply together" working filter — just reflect the persisted filter.
|
|
15
|
+
const mvfData = useDashboardMeasureValueFilterData(filter);
|
|
16
|
+
const { conditionLabel } = mvfData;
|
|
17
|
+
const handleApply = useCallback((updated) => {
|
|
18
|
+
const newConditions = normalizeMeasureValueFilterConditions(updated);
|
|
19
|
+
const next = {
|
|
20
|
+
dashboardMeasureValueFilter: {
|
|
21
|
+
...filter.dashboardMeasureValueFilter,
|
|
22
|
+
...(newConditions && newConditions.length > 0
|
|
23
|
+
? { conditions: newConditions }
|
|
24
|
+
: { conditions: undefined }),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
onChange(next);
|
|
28
|
+
}, [filter, onChange]);
|
|
29
|
+
return (_jsx(AutomationMeasureValueFilterProvider, { filter: filter, onChange: onChange, onDelete: onDelete, isLocked: isLocked, deleteAriaLabel: deleteAriaLabel, deleteTooltipContent: deleteTooltipContent, lockedTooltipContent: lockedTooltipContent, children: _jsx(MeasureValueFilter, { ...getSharedDashboardMvfProps(mvfData), onApply: handleApply, DropdownButtonComponent: (props) => (_jsx(AutomationMeasureValueFilterButton, { ...props, conditionLabel: conditionLabel, overlayPositionType: overlayPositionType, readonly: readonly })) }) }));
|
|
30
|
+
}
|
|
31
|
+
function AutomationMeasureValueFilterButton({ isActive, buttonTitle, onClick, conditionLabel, readonly, }) {
|
|
32
|
+
const { isLocked, onDelete, filter, deleteAriaLabel, deleteTooltipContent, lockedTooltipContent } = useAutomationMeasureValueFilterContext();
|
|
33
|
+
const label = `${buttonTitle}: ${conditionLabel}`;
|
|
34
|
+
const mvfTooltipId = useIdPrefixed("mvf-filter-tooltip");
|
|
35
|
+
const mvfDeleteTooltipId = useIdPrefixed("mvf-filter-delete-tooltip");
|
|
36
|
+
const tooltipContent = (_jsxs(_Fragment, { children: [label, isLocked ? _jsx("div", { children: lockedTooltipContent }) : null] }));
|
|
37
|
+
// The whole-panel disabled state is handled by a visual overlay in
|
|
38
|
+
// AutomationFiltersSelect (`gd-automation-filters__overlay`), so the chip should not
|
|
39
|
+
// claim a locked state from it. The lock icon only reflects the filter's own config.
|
|
40
|
+
const isDeletable = !isLocked && !readonly;
|
|
41
|
+
return (_jsx(UiChip, { label: label, iconBefore: "metric", isActive: isActive, isLocked: isLocked, isDeletable: isDeletable, onClick: onClick, onDelete: () => onDelete?.(filter), onKeyDown: (event) => {
|
|
42
|
+
// In case the button is locked and not disabled we need to explicitly
|
|
43
|
+
// stop the event propagation to prevent dropdown from opening
|
|
44
|
+
if (isLocked && isActionKey(event)) {
|
|
45
|
+
event.stopPropagation();
|
|
46
|
+
}
|
|
47
|
+
}, onDeleteKeyDown: (event) => {
|
|
48
|
+
// Do not propagate event to parent as MVF dropdown would always open
|
|
49
|
+
if (isActionKey(event)) {
|
|
50
|
+
event.stopPropagation();
|
|
51
|
+
}
|
|
52
|
+
}, accessibilityConfig: {
|
|
53
|
+
isExpanded: isActive,
|
|
54
|
+
popupType: "dialog",
|
|
55
|
+
ariaDescribedBy: mvfTooltipId,
|
|
56
|
+
deleteAriaLabel: buttonTitle ? `${deleteAriaLabel} ${buttonTitle}` : deleteAriaLabel,
|
|
57
|
+
deleteAriaDescribedBy: mvfDeleteTooltipId,
|
|
58
|
+
}, renderChipContent: (content) => (_jsx(UiTooltip, { id: mvfTooltipId, arrowPlacement: "top-start", content: tooltipContent, triggerBy: ["hover", "focus"], anchor: content, anchorWrapperStyles: {
|
|
59
|
+
display: "flex",
|
|
60
|
+
width: "100%",
|
|
61
|
+
height: "100%",
|
|
62
|
+
minWidth: 0,
|
|
63
|
+
} })), renderDeleteButton: (button) => (_jsx(UiTooltip, { id: mvfDeleteTooltipId, arrowPlacement: "top-start", content: deleteTooltipContent, triggerBy: ["hover", "focus"], anchor: button, anchorWrapperStyles: {
|
|
64
|
+
height: "100%",
|
|
65
|
+
} })) }));
|
|
66
|
+
}
|
package/esm/presentation/automationFilters/components/AutomationMeasureValueFilterContext.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type FilterContextItem, type IDashboardMeasureValueFilter } from "@gooddata/sdk-model";
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export interface IAutomationMeasureValueFilterContext {
|
|
7
|
+
onChange: (filter: FilterContextItem) => void;
|
|
8
|
+
onDelete: (filter: FilterContextItem) => void;
|
|
9
|
+
filter: IDashboardMeasureValueFilter;
|
|
10
|
+
isLocked?: boolean;
|
|
11
|
+
deleteAriaLabel?: string;
|
|
12
|
+
deleteTooltipContent?: string;
|
|
13
|
+
lockedTooltipContent?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare const useAutomationMeasureValueFilterContext: () => IAutomationMeasureValueFilterContext;
|
|
19
|
+
export interface IAutomationMeasureValueFilterProviderProps extends IAutomationMeasureValueFilterContext {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export declare function AutomationMeasureValueFilterProvider({ children, onChange, onDelete, isLocked, filter, deleteAriaLabel, deleteTooltipContent, lockedTooltipContent }: IAutomationMeasureValueFilterProviderProps): import("react/jsx-runtime").JSX.Element;
|