@gooddata/sdk-ui-dashboard 11.65.0-alpha.0 → 11.65.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/esm/__version.d.ts +1 -1
  2. package/esm/__version.js +1 -1
  3. package/esm/model/queryServices/queryAttributeElements.js +1 -0
  4. package/esm/model/types/attributeFilterTypes.d.ts +5 -0
  5. package/esm/model/types/attributeFilterTypes.d.ts.map +1 -1
  6. package/esm/model/types/attributeFilterTypes.js +1 -1
  7. package/esm/presentation/automations/alerting/hooks/useAttributeValuesFromExecResults.d.ts +33 -2
  8. package/esm/presentation/automations/alerting/hooks/useAttributeValuesFromExecResults.d.ts.map +1 -1
  9. package/esm/presentation/automations/alerting/hooks/useAttributeValuesFromExecResults.js +29 -13
  10. package/esm/presentation/automations/alerting/state/useAlertSupportedMetrics.d.ts.map +1 -1
  11. package/esm/presentation/automations/alerting/state/useAlertSupportedMetrics.js +3 -2
  12. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/DashboardAttachments.d.ts.map +1 -1
  13. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/DashboardAttachments.js +3 -3
  14. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/WidgetAttachments.d.ts.map +1 -1
  15. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/WidgetAttachments.js +5 -4
  16. package/esm/presentation/dashboard/components/DashboardScreenSizeContext.d.ts.map +1 -1
  17. package/esm/presentation/dashboard/components/DashboardScreenSizeContext.js +10 -7
  18. package/esm/presentation/dashboard/components/ToastMessages.d.ts.map +1 -1
  19. package/esm/presentation/dashboard/components/ToastMessages.js +6 -3
  20. package/esm/presentation/dashboard/components/useEditModeLayoutNoteMessages.d.ts +6 -0
  21. package/esm/presentation/dashboard/components/useEditModeLayoutNoteMessages.d.ts.map +1 -0
  22. package/esm/presentation/dashboard/components/useEditModeLayoutNoteMessages.js +48 -0
  23. package/esm/presentation/drill/utils/drillDownUtils.d.ts.map +1 -1
  24. package/esm/presentation/drill/utils/drillDownUtils.js +3 -2
  25. package/esm/presentation/export/hooks/useDashboardRelatedFilters.d.ts +8 -1
  26. package/esm/presentation/export/hooks/useDashboardRelatedFilters.d.ts.map +1 -1
  27. package/esm/presentation/export/hooks/useDashboardRelatedFilters.js +21 -5
  28. package/esm/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.d.ts.map +1 -1
  29. package/esm/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.js +3 -2
  30. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltip.d.ts.map +1 -1
  31. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltip.js +4 -1
  32. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltipContent.d.ts +6 -2
  33. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltipContent.d.ts.map +1 -1
  34. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltipContent.js +8 -4
  35. package/esm/presentation/filterBar/filterBar/filterViews/FilterViewDeleteConfirm.d.ts +2 -1
  36. package/esm/presentation/filterBar/filterBar/filterViews/FilterViewDeleteConfirm.d.ts.map +1 -1
  37. package/esm/presentation/filterBar/filterBar/filterViews/FilterViewDeleteConfirm.js +2 -2
  38. package/esm/presentation/filterBar/filterBar/filterViews/FilterViewsList.d.ts.map +1 -1
  39. package/esm/presentation/filterBar/filterBar/filterViews/FilterViewsList.js +11 -5
  40. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +8 -0
  41. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  42. package/esm/presentation/localization/bundles/en-US.localization-bundle.js +8 -0
  43. package/esm/presentation/topBar/buttonBar/button/editButton/DefaultEditButton.d.ts.map +1 -1
  44. package/esm/presentation/topBar/buttonBar/button/editButton/DefaultEditButton.js +2 -4
  45. package/esm/sdk-ui-dashboard.d.ts +5 -0
  46. package/package.json +21 -21
  47. package/styles/css/dashboard.css +9 -0
  48. package/styles/css/dashboard.css.map +1 -1
  49. package/styles/css/main.css +9 -0
  50. package/styles/css/main.css.map +1 -1
  51. package/styles/scss/dashboard.scss +11 -0
@@ -1,4 +1,4 @@
1
- export declare const LIB_VERSION = "11.65.0-alpha.0";
1
+ export declare const LIB_VERSION = "11.65.0-alpha.1";
2
2
  export declare const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
3
3
  export declare const LIB_NAME = "@gooddata/sdk-ui-dashboard";
4
4
  //# sourceMappingURL=__version.d.ts.map
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.65.0-alpha.0";
3
+ export const LIB_VERSION = "11.65.0-alpha.1";
4
4
  export const LIB_DESCRIPTION = "GoodData SDK - Dashboard Component";
5
5
  export const LIB_NAME = "@gooddata/sdk-ui-dashboard";
@@ -24,5 +24,6 @@ function* queryService(ctx, query) {
24
24
  return {
25
25
  elements: attributeElements.items,
26
26
  totalCount: attributeElements.totalCount,
27
+ numberFormat: attributeElements.numberFormat,
27
28
  };
28
29
  }
@@ -43,5 +43,10 @@ export interface IParentWithConnectingAttributes {
43
43
  export interface IUseAttributeElements {
44
44
  elements: IAttributeElement[];
45
45
  totalCount: number;
46
+ /**
47
+ * Number format of the element values, see
48
+ * {@link @gooddata/sdk-backend-spi#IElementsQueryResult.numberFormat}; the element titles stay raw.
49
+ */
50
+ numberFormat?: string;
46
51
  }
47
52
  //# sourceMappingURL=attributeFilterTypes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attributeFilterTypes.d.ts","sourceRoot":"","sources":["../../../src/model/types/attributeFilterTypes.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,mCAAmC,EACxC,KAAK,iBAAiB,EACtB,KAAK,MAAM,EACd,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2BAA2B,EAAE,MAAM,GAAG,SAAS,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,qCAAqC;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,mCAAmC,EAAE,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC5C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"attributeFilterTypes.d.ts","sourceRoot":"","sources":["../../../src/model/types/attributeFilterTypes.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,mCAAmC,EACxC,KAAK,iBAAiB,EACtB,KAAK,MAAM,EACd,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2BAA2B,EAAE,MAAM,GAAG,SAAS,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,qCAAqC;IAClD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,mCAAmC,EAAE,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC5C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB"}
@@ -1,2 +1,2 @@
1
- // (C) 2022-2023 GoodData Corporation
1
+ // (C) 2022-2026 GoodData Corporation
2
2
  export {};
@@ -1,5 +1,14 @@
1
1
  import type { IExecutionResult } from "@gooddata/sdk-backend-spi";
2
- import { type IAttribute, type IMeasure } from "@gooddata/sdk-model";
2
+ import { type IAttribute, type IMeasure, type ISeparators } from "@gooddata/sdk-model";
3
+ import { DataViewFacade, type IDataSeries } from "@gooddata/sdk-ui";
4
+ /**
5
+ * Formatting applied to attribute element values read from the execution result.
6
+ *
7
+ * @internal
8
+ */
9
+ export interface IAttributeValuesFormatting {
10
+ separators?: ISeparators;
11
+ }
3
12
  type IExecutionResultEnvelope = {
4
13
  isLoading?: boolean;
5
14
  executionResult?: IExecutionResult;
@@ -29,10 +38,32 @@ export type AttributeValue = {
29
38
  *
30
39
  * @internal
31
40
  */
32
- export declare function useAttributeValuesFromExecResults(execResult: IExecutionResultEnvelope | undefined): {
41
+ export declare function useAttributeValuesFromExecResults(execResult: IExecutionResultEnvelope | undefined, formatting?: IAttributeValuesFormatting): {
33
42
  isResultLoading: boolean;
34
43
  getAttributeValues: (attr: IAttribute) => AttributeValue[];
35
44
  getMetricValue: (measure?: IMeasure, attr?: IAttribute, value?: string | null) => number | undefined;
36
45
  };
46
+ /**
47
+ * Sums the measure values of the series scoped by the given raw attribute values.
48
+ *
49
+ * @remarks
50
+ * Matching works on raw element names: `scopeTitles()` return raw names, so the values passed in must be
51
+ * raw too, never the formatted titles shown in the UI.
52
+ *
53
+ * @internal exported for tests
54
+ */
55
+ export declare function calculatePredictedValue(data: IDataSeries[], values?: string[] | null): number;
56
+ /**
57
+ * Raw names of the attribute elements to predict the value for: the element the filter value names, or
58
+ * every element when there is none.
59
+ *
60
+ * @remarks
61
+ * The filter value is a raw element value, so it is matched against the raw name and uri, not against the
62
+ * formatted title (dates, numeric computed attributes) shown in the UI; the raw names are returned to match
63
+ * the raw scope titles in {@link calculatePredictedValue}.
64
+ *
65
+ * @internal exported for tests
66
+ */
67
+ export declare function calculateValues(dataView: DataViewFacade | null, indexes: [number, number], value?: string | null): string[];
37
68
  export {};
38
69
  //# sourceMappingURL=useAttributeValuesFromExecResults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAttributeValuesFromExecResults.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/alerting/hooks/useAttributeValuesFromExecResults.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACH,KAAK,UAAU,EAEf,KAAK,QAAQ,EAGhB,MAAM,qBAAqB,CAAC;AAG7B,KAAK,wBAAwB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE5F;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,UAAU,EAAE,wBAAwB,GAAG,SAAS;;+BAuCnF,UAAU,KAAG,cAAc,EAAE;+BASzB,QAAQ,SAAS,UAAU,UAAU,MAAM,GAAG,IAAI;EAyBpE"}
1
+ {"version":3,"file":"useAttributeValuesFromExecResults.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/alerting/hooks/useAttributeValuesFromExecResults.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EACH,KAAK,UAAU,EAEf,KAAK,QAAQ,EACb,KAAK,WAAW,EAGnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,cAAc,EACd,KAAK,WAAW,EAGnB,MAAM,kBAAkB,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACvC,UAAU,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,KAAK,wBAAwB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE5F;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC7C,UAAU,EAAE,wBAAwB,GAAG,SAAS,EAChD,UAAU,CAAC,EAAE,0BAA0B;;+BA6C5B,UAAU,KAAG,cAAc,EAAE;+BASzB,QAAQ,SAAS,UAAU,UAAU,MAAM,GAAG,IAAI;EAyBpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,UAkBpF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,EAAE,CAIV"}
@@ -1,13 +1,14 @@
1
1
  // (C) 2019-2026 GoodData Corporation
2
2
  import { useCallback, useEffect, useState } from "react";
3
3
  import { areObjRefsEqual, isResultAttributeHeader, } from "@gooddata/sdk-model";
4
- import { DataViewFacade } from "@gooddata/sdk-ui";
4
+ import { DataViewFacade, createNumberFormatter, formatAttributeHeaderValues, } from "@gooddata/sdk-ui";
5
5
  /**
6
6
  * Reads the widget's execution result and exposes the attribute values and measure values in it.
7
7
  *
8
8
  * @internal
9
9
  */
10
- export function useAttributeValuesFromExecResults(execResult) {
10
+ export function useAttributeValuesFromExecResults(execResult, formatting) {
11
+ const separators = formatting?.separators;
11
12
  // Derive loading from the envelope's own isLoading flag, not from the
12
13
  // presence of executionResult: an errored execution has no executionResult
13
14
  // but is no longer loading, and there is no readAll() promise to clear the
@@ -23,7 +24,7 @@ export function useAttributeValuesFromExecResults(execResult) {
23
24
  .readAll()
24
25
  .then((data) => {
25
26
  if (!cancelled) {
26
- setDataView(DataViewFacade.for(data));
27
+ setDataView(DataViewFacade.for(formatAttributeHeaderValues(data, createNumberFormatter(separators))));
27
28
  setIsResultLoading(false);
28
29
  }
29
30
  })
@@ -43,7 +44,7 @@ export function useAttributeValuesFromExecResults(execResult) {
43
44
  return () => {
44
45
  cancelled = true;
45
46
  };
46
- }, [execResult]);
47
+ }, [execResult, separators]);
47
48
  const getAttributeValues = useCallback((attr) => {
48
49
  const header = findAttributeHeader(dataView, attr);
49
50
  const indexes = findDimIndexes(dataView, header);
@@ -69,7 +70,16 @@ export function useAttributeValuesFromExecResults(execResult) {
69
70
  getMetricValue,
70
71
  };
71
72
  }
72
- function calculatePredictedValue(data, values) {
73
+ /**
74
+ * Sums the measure values of the series scoped by the given raw attribute values.
75
+ *
76
+ * @remarks
77
+ * Matching works on raw element names: `scopeTitles()` return raw names, so the values passed in must be
78
+ * raw too, never the formatted titles shown in the UI.
79
+ *
80
+ * @internal exported for tests
81
+ */
82
+ export function calculatePredictedValue(data, values) {
73
83
  return data.reduce((total, series) => {
74
84
  return (total +
75
85
  Array.from(series).reduce((acc, item) => {
@@ -84,15 +94,21 @@ function calculatePredictedValue(data, values) {
84
94
  }, 0));
85
95
  }, 0);
86
96
  }
87
- function calculateValues(dataView, indexes, value) {
97
+ /**
98
+ * Raw names of the attribute elements to predict the value for: the element the filter value names, or
99
+ * every element when there is none.
100
+ *
101
+ * @remarks
102
+ * The filter value is a raw element value, so it is matched against the raw name and uri, not against the
103
+ * formatted title (dates, numeric computed attributes) shown in the UI; the raw names are returned to match
104
+ * the raw scope titles in {@link calculatePredictedValue}.
105
+ *
106
+ * @internal exported for tests
107
+ */
108
+ export function calculateValues(dataView, indexes, value) {
88
109
  return findAttributeValues(dataView, indexes)
89
- .map((v) => v.title)
90
- .filter((v) => {
91
- if (value) {
92
- return v === value;
93
- }
94
- return true;
95
- });
110
+ .filter((v) => !value || v.name === value || v.value === value)
111
+ .map((v) => v.name);
96
112
  }
97
113
  // By display form, not catalog attribute: two display forms of one attribute are two descriptors.
98
114
  function findAttributeHeader(dataView, attr) {
@@ -1 +1 @@
1
- {"version":3,"file":"useAlertSupportedMetrics.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/alerting/state/useAlertSupportedMetrics.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,yBAAyB,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAYlG,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,8BAA8B;IAC3C,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,yBAAyB,CAAC;CAC3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,OAAO,EACP,MAAM,EACN,WAAW,GACd,EAAE,8BAA8B,GAAG,sBAAsB,CA8CzD"}
1
+ {"version":3,"file":"useAlertSupportedMetrics.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/automations/alerting/state/useAlertSupportedMetrics.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,yBAAyB,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAYlG,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,8BAA8B;IAC3C,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,yBAAyB,CAAC;CAC3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,OAAO,EACP,MAAM,EACN,WAAW,GACd,EAAE,8BAA8B,GAAG,sBAAsB,CAiDzD"}
@@ -17,7 +17,7 @@ import { getSupportedInsightAttributesByInsight, getSupportedInsightMeasuresByIn
17
17
  * @internal
18
18
  */
19
19
  export function useAlertSupportedMetrics({ insight, widget, alertToEdit, }) {
20
- const { locale, catalogDateDatasets, catalogAttributes } = useAutomationsContext();
20
+ const { locale, separators, catalogDateDatasets, catalogAttributes } = useAutomationsContext();
21
21
  const { executionResultByRef } = useAlertingDialogContext();
22
22
  const execResult = executionResultByRef(widget?.ref);
23
23
  const [effectiveInsight, setEffectiveInsight] = useState(insight);
@@ -31,7 +31,8 @@ export function useAlertSupportedMetrics({ insight, widget, alertToEdit, }) {
31
31
  }, [execResult?.executionResult]);
32
32
  const supportedMeasures = useMemo(() => getSupportedInsightMeasuresByInsight(effectiveInsight, catalogDateDatasets, alertToEdit), [effectiveInsight, catalogDateDatasets, alertToEdit]);
33
33
  const supportedAttributes = useMemo(() => getSupportedInsightAttributesByInsight(insight, catalogAttributes, catalogDateDatasets, alertToEdit), [insight, catalogDateDatasets, catalogAttributes, alertToEdit]);
34
- const { isResultLoading, getAttributeValues, getMetricValue } = useAttributeValuesFromExecResults(execResult);
34
+ // element values of numeric computed attributes are offered formatted, as the widget shows them
35
+ const { isResultLoading, getAttributeValues, getMetricValue } = useAttributeValuesFromExecResults(execResult, { separators });
35
36
  return {
36
37
  measureFormatMap,
37
38
  supportedMeasures,
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardAttachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/DashboardAttachments.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,8CAA8C,EAAE,MAAM,mBAAmB,CAAC;AAUxF,wBAAgB,oBAAoB,CAAC,EACjC,gBAAgB,EAChB,gBAAgB,EAChB,4BAA4B,EAC5B,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,GAC3B,EAAE,8CAA8C,2CA4HhD"}
1
+ {"version":3,"file":"DashboardAttachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/DashboardAttachments.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,8CAA8C,EAAE,MAAM,mBAAmB,CAAC;AAUxF,wBAAgB,oBAAoB,CAAC,EACjC,gBAAgB,EAChB,gBAAgB,EAChB,4BAA4B,EAC5B,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,iBAAiB,EACjB,wBAAwB,GAC3B,EAAE,8CAA8C,2CAqIhD"}
@@ -44,8 +44,8 @@ export function DashboardAttachments({ dashboardFilters, isCrossFiltering, onDas
44
44
  setTimeout(() => {
45
45
  setAnnouncement(intl.formatMessage({ id: "dialogs.schedule.management.attachments.removed" }, { format: attachment }));
46
46
  });
47
- // Focus management: if items remain, focus the group; otherwise focus add button
48
- if (newAttachments.length > 0) {
47
+ // Hidden formats are preserved, but only visible chips can receive focus.
48
+ if (visibleSelectedAttachments.some((att) => att !== attachment)) {
49
49
  focusAttachmentGroup();
50
50
  }
51
51
  else {
@@ -74,7 +74,7 @@ export function DashboardAttachments({ dashboardFilters, isCrossFiltering, onDas
74
74
  selected: selectedAttachments.includes(format),
75
75
  })), onChange: handleChange, mode: "dashboard", onAddButtonRef: (ref) => {
76
76
  addButtonRef.current = ref;
77
- } }), isCrossFiltering && selectedAttachments.length > 0 ? (_jsx(Message, { type: "progress", className: "gd-attachment-list-message", children: _jsx(FormattedMessage, { id: "dialogs.schedule.management.attachments.message", values: {
77
+ } }), visibleSelectedAttachments.length === 0 && selectedAttachments.length > 0 ? (_jsx(Message, { type: "progress", className: "gd-attachment-list-message", accessibilityConfig: { role: "status" }, children: _jsx(FormattedMessage, { id: "scheduledEmail.attachments.noVisibleAttachments" }) })) : null, isCrossFiltering && selectedAttachments.length > 0 ? (_jsx(Message, { type: "progress", className: "gd-attachment-list-message", children: _jsx(FormattedMessage, { id: "dialogs.schedule.management.attachments.message", values: {
78
78
  strong: (chunks) => _jsx("strong", { children: chunks }),
79
79
  } }) })) : null] }) }, selectedAttachments.join()), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: announcement })] }));
80
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetAttachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/WidgetAttachments.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,2CAA2C,EAAE,MAAM,mBAAmB,CAAC;AAmBrF,wBAAgB,iBAAiB,CAAC,EAC9B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,wBAAwB,GAC3B,EAAE,2CAA2C,2CA4H7C"}
1
+ {"version":3,"file":"WidgetAttachments.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/Attachments/WidgetAttachments.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,2CAA2C,EAAE,MAAM,mBAAmB,CAAC;AAmBrF,wBAAgB,iBAAiB,CAAC,EAC9B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,wBAAwB,GAC3B,EAAE,2CAA2C,2CAqI7C"}
@@ -1,7 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2019-2026 GoodData Corporation
3
3
  import { useCallback, useRef, useState } from "react";
4
- import { useIntl } from "react-intl";
4
+ import { FormattedMessage, useIntl } from "react-intl";
5
+ import { Message } from "@gooddata/sdk-ui-kit";
5
6
  import { AUTOMATION_ATTACHMENTS_GROUP_LABEL_ID } from "../../../../../constants/automations.js";
6
7
  import { partitionAttachments } from "./attachmentFormats.js";
7
8
  import { AttachmentsList } from "./AttachmentsList.js";
@@ -55,8 +56,8 @@ export function WidgetAttachments({ selectedAttachments, onWidgetAttachmentsChan
55
56
  setTimeout(() => {
56
57
  setAnnouncement(intl.formatMessage({ id: "dialogs.schedule.management.attachments.removed" }, { format: attachment }));
57
58
  });
58
- // Focus management: if items remain, focus the group; otherwise focus add button
59
- if (newAttachments.length > 0) {
59
+ // Hidden formats are preserved, but only visible chips can receive focus.
60
+ if (visibleSelectedAttachments.some((att) => att !== attachment)) {
60
61
  focusAttachmentGroup();
61
62
  }
62
63
  else {
@@ -86,5 +87,5 @@ export function WidgetAttachments({ selectedAttachments, onWidgetAttachmentsChan
86
87
  selected: selectedAttachments.includes(format),
87
88
  })), onChange: handleChange, mode: "widget", onAddButtonRef: (ref) => {
88
89
  addButtonRef.current = ref;
89
- } })] }) }, selectedAttachments.join()), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: announcement })] }));
90
+ } }), visibleSelectedAttachments.length === 0 && selectedAttachments.length > 0 ? (_jsx(Message, { type: "progress", className: "gd-attachment-list-message", accessibilityConfig: { role: "status" }, children: _jsx(FormattedMessage, { id: "scheduledEmail.attachments.noVisibleAttachments" }) })) : null] }) }, selectedAttachments.join()), _jsx("div", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", role: "status", children: announcement })] }));
90
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardScreenSizeContext.d.ts","sourceRoot":"","sources":["../../../../src/presentation/dashboard/components/DashboardScreenSizeContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEH,KAAK,SAAS,EAMjB,MAAM,OAAO,CAAC;AAIf,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA8CtD,MAAM,WAAW,0BAA0B;IACvC,QAAQ,EAAE,SAAS,CAAC;CACvB;AAKD,wBAAgB,2BAA2B,CAAC,EAAE,QAAQ,EAAE,EAAE,0BAA0B,2CA2BnF;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,QAAO,UAGhC,CAAC"}
1
+ {"version":3,"file":"DashboardScreenSizeContext.d.ts","sourceRoot":"","sources":["../../../../src/presentation/dashboard/components/DashboardScreenSizeContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEH,KAAK,SAAS,EAMjB,MAAM,OAAO,CAAC;AAIf,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAiDtD,MAAM,WAAW,0BAA0B;IACvC,QAAQ,EAAE,SAAS,CAAC;CACvB;AAKD,wBAAgB,2BAA2B,CAAC,EAAE,QAAQ,EAAE,EAAE,0BAA0B,2CA4BnF;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,QAAO,UAGhC,CAAC"}
@@ -3,9 +3,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext, useEffect, useState, } from "react";
4
4
  import { setScreenSize as setScreenSizeAction } from "../../../model/commands/layout.js";
5
5
  import { useDashboardDispatch, useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
6
+ import { selectIsInEditMode } from "../../../model/store/renderMode/renderModeSelectors.js";
6
7
  import { selectScreen } from "../../../model/store/tabs/layout/layoutSelectors.js";
7
8
  import { DASHBOARD_LAYOUT_BREAK_POINTS } from "../../constants/layout.js";
8
- const getCurrentScreenSize = () => {
9
+ const getCurrentScreenSize = (isEditMode) => {
9
10
  let viewportWidth = 0;
10
11
  if (window?.innerWidth) {
11
12
  viewportWidth = window.innerWidth;
@@ -16,7 +17,8 @@ const getCurrentScreenSize = () => {
16
17
  if (viewportWidth >= DASHBOARD_LAYOUT_BREAK_POINTS[3]) {
17
18
  return "xl";
18
19
  }
19
- if (viewportWidth >= DASHBOARD_LAYOUT_BREAK_POINTS[2]) {
20
+ // Only xl sizes are saved, so edit mode keeps the desktop grid instead of reflowing it.
21
+ if (isEditMode || viewportWidth >= DASHBOARD_LAYOUT_BREAK_POINTS[2]) {
20
22
  return "lg";
21
23
  }
22
24
  if (viewportWidth >= DASHBOARD_LAYOUT_BREAK_POINTS[1]) {
@@ -27,8 +29,8 @@ const getCurrentScreenSize = () => {
27
29
  }
28
30
  return "xs";
29
31
  };
30
- function setNewSize(prevSize, setScreenSize, dispatch) {
31
- const newSize = getCurrentScreenSize();
32
+ function setNewSize(prevSize, isEditMode, setScreenSize, dispatch) {
33
+ const newSize = getCurrentScreenSize(isEditMode);
32
34
  if (newSize && newSize !== prevSize) {
33
35
  // prevent unnecessary re-renders
34
36
  setScreenSize(newSize);
@@ -41,18 +43,19 @@ export function DashboardScreenSizeProvider({ children }) {
41
43
  const [screenSize, setScreenSize] = useState();
42
44
  const dispatch = useDashboardDispatch();
43
45
  const appStateScreenSize = useDashboardSelector(selectScreen);
46
+ const isEditMode = useDashboardSelector(selectIsInEditMode);
44
47
  useEffect(() => {
45
48
  // set initial screen size to app state for usage in sagas
46
- setNewSize(screenSize, setScreenSize, dispatch);
49
+ setNewSize(screenSize, isEditMode, setScreenSize, dispatch);
47
50
  // register event listener for window resize
48
51
  const handleWindowResized = () => {
49
- setNewSize(screenSize, setScreenSize, dispatch);
52
+ setNewSize(screenSize, isEditMode, setScreenSize, dispatch);
50
53
  };
51
54
  window.addEventListener("resize", handleWindowResized, false);
52
55
  return () => {
53
56
  window.removeEventListener("resize", handleWindowResized, false);
54
57
  };
55
- }, [screenSize, dispatch]);
58
+ }, [screenSize, isEditMode, dispatch]);
56
59
  return (_jsx("div", { className: "s-screen-size-container", children: _jsx(DashboardScreenSizeContext.Provider, { value: screenSize, children: appStateScreenSize ? children : null }) }));
57
60
  }
58
61
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ToastMessages.d.ts","sourceRoot":"","sources":["../../../../src/presentation/dashboard/components/ToastMessages.tsx"],"names":[],"mappings":"AAYA;;GAEG;AACH,wBAAgB,aAAa,4CAwB5B"}
1
+ {"version":3,"file":"ToastMessages.d.ts","sourceRoot":"","sources":["../../../../src/presentation/dashboard/components/ToastMessages.tsx"],"names":[],"mappings":"AAaA;;GAEG;AACH,wBAAgB,aAAa,4CA2B5B"}
@@ -5,6 +5,7 @@ import { NonContextToastsInterop } from "@gooddata/sdk-ui-kit";
5
5
  import { useParameterToastMessages } from "../../filterBar/parameterFilter/useParameterToastMessages.js";
6
6
  import { useCrossFilteringMessages } from "./useCrossFilteringMessages.js";
7
7
  import { useDrillValidationMessages } from "./useDrillValidationMessages.js";
8
+ import { useEditModeLayoutNoteMessages } from "./useEditModeLayoutNoteMessages.js";
8
9
  import { useFilterContextValidationMessages } from "./useFilterContextValidationMessages.js";
9
10
  /**
10
11
  * @internal
@@ -12,14 +13,16 @@ import { useFilterContextValidationMessages } from "./useFilterContextValidation
12
13
  export function ToastMessages() {
13
14
  const { messages: drillValidationMessages, removeMessage: removeDrillValidationMessage } = useDrillValidationMessages();
14
15
  const { messages: filterContextValidationMessages, removeMessage: removeFilterContextValidationMessage } = useFilterContextValidationMessages();
16
+ const { messages: editModeLayoutNoteMessages, removeMessage: removeEditModeLayoutNoteMessage } = useEditModeLayoutNoteMessages();
15
17
  useCrossFilteringMessages();
16
18
  useParameterToastMessages();
17
- const messages = useMemo(() => [...drillValidationMessages, ...filterContextValidationMessages], [drillValidationMessages, filterContextValidationMessages]);
19
+ const messages = useMemo(() => [...drillValidationMessages, ...filterContextValidationMessages, ...editModeLayoutNoteMessages], [drillValidationMessages, filterContextValidationMessages, editModeLayoutNoteMessages]);
18
20
  const removeMessage = useCallback(
19
- // try removing the id from both collections
21
+ // try removing the id from all collections
20
22
  (id) => {
21
23
  removeDrillValidationMessage(id);
22
24
  removeFilterContextValidationMessage(id);
23
- }, [removeDrillValidationMessage, removeFilterContextValidationMessage]);
25
+ removeEditModeLayoutNoteMessage(id);
26
+ }, [removeDrillValidationMessage, removeFilterContextValidationMessage, removeEditModeLayoutNoteMessage]);
24
27
  return _jsx(NonContextToastsInterop, { messages: messages, onDismissMessage: removeMessage });
25
28
  }
@@ -0,0 +1,6 @@
1
+ import { type IMessage } from "@gooddata/sdk-ui-kit";
2
+ export declare function useEditModeLayoutNoteMessages(): {
3
+ messages: IMessage[];
4
+ removeMessage: (id: string) => void;
5
+ };
6
+ //# sourceMappingURL=useEditModeLayoutNoteMessages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEditModeLayoutNoteMessages.d.ts","sourceRoot":"","sources":["../../../../src/presentation/dashboard/components/useEditModeLayoutNoteMessages.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,sBAAsB,CAAC;AAkBpE,wBAAgB,6BAA6B;;wBA0BhC,MAAM;EAalB"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { useCallback, useMemo, useState } from "react";
4
+ import { defineMessages, useIntl } from "react-intl";
5
+ import { useMediaQuery } from "@gooddata/sdk-ui-kit";
6
+ import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
7
+ import { selectIsInEditMode } from "../../../model/store/renderMode/renderModeSelectors.js";
8
+ const localizationMessages = defineMessages({
9
+ editModeLayoutNote: { id: "messages.dashboard.editModeDesktopLayout.note" },
10
+ });
11
+ const replacements = {
12
+ b: (chunks) => _jsx("b", { children: chunks }),
13
+ };
14
+ const EDIT_MODE_LAYOUT_NOTE_MESSAGE_ID = "edit_mode_desktop_layout_note";
15
+ // Dismissal lasts until the page reloads and is deliberately never persisted.
16
+ let isDismissedInSession = false;
17
+ export function useEditModeLayoutNoteMessages() {
18
+ const intl = useIntl();
19
+ const isEditMode = useDashboardSelector(selectIsInEditMode);
20
+ const isNarrowerThanEditModeMinWidth = useMediaQuery("<=lg");
21
+ const [isDismissed, setIsDismissed] = useState(isDismissedInSession);
22
+ const isVisible = isEditMode && isNarrowerThanEditModeMinWidth && !isDismissed;
23
+ const messages = useMemo(() => {
24
+ if (!isVisible) {
25
+ return [];
26
+ }
27
+ return [
28
+ {
29
+ id: EDIT_MODE_LAYOUT_NOTE_MESSAGE_ID,
30
+ type: "progress",
31
+ node: intl.formatMessage(localizationMessages.editModeLayoutNote, replacements),
32
+ createdAt: new Date().getTime(),
33
+ duration: Infinity,
34
+ },
35
+ ];
36
+ }, [isVisible, intl]);
37
+ // The toasts interop also reports removals caused by the note no longer applying; only a visible note can be dismissed.
38
+ const removeMessage = useCallback((id) => {
39
+ if (id === EDIT_MODE_LAYOUT_NOTE_MESSAGE_ID && isVisible) {
40
+ isDismissedInSession = true;
41
+ setIsDismissed(true);
42
+ }
43
+ }, [isVisible]);
44
+ return {
45
+ messages,
46
+ removeMessage,
47
+ };
48
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"drillDownUtils.d.ts","sourceRoot":"","sources":["../../../../src/presentation/drill/utils/drillDownUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,mCAAmC,EACxC,KAAK,uCAAuC,EAG/C,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,WAAW,EAAoC,MAAM,kBAAkB,CAAC;AAOtF,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAG7F;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,eAAe,EAAE,oBAAoB,EACrC,UAAU,EAAE,WAAW,EACvB,sCAAsC,CAAC,EAAE,uCAAuC,EAAE,EAClF,sBAAsB,CAAC,EAAE,mCAAmC,GAC7D,MAAM,GAAG,IAAI,CAoBf;AAED;;GAEG;AACH,wBAAgB,mCAAmC,CAC/C,kBAAkB,EAAE,MAAM,EAC1B,UAAU,EAAE,WAAW,GACxB,MAAM,GAAG,IAAI,CASf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAE3F;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAC9C,gBAAgB,EAAE,wBAAwB,EAAE,EAC5C,gBAAgB,GAAE,wBAAwB,EAAO,GAClD,wBAAwB,EAAE,CA0B5B"}
1
+ {"version":3,"file":"drillDownUtils.d.ts","sourceRoot":"","sources":["../../../../src/presentation/drill/utils/drillDownUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,mCAAmC,EACxC,KAAK,uCAAuC,EAG/C,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,WAAW,EAAoC,MAAM,kBAAkB,CAAC;AAOtF,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAG7F;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,eAAe,EAAE,oBAAoB,EACrC,UAAU,EAAE,WAAW,EACvB,sCAAsC,CAAC,EAAE,uCAAuC,EAAE,EAClF,sBAAsB,CAAC,EAAE,mCAAmC,GAC7D,MAAM,GAAG,IAAI,CAoBf;AAED;;GAEG;AACH,wBAAgB,mCAAmC,CAC/C,kBAAkB,EAAE,MAAM,EAC1B,UAAU,EAAE,WAAW,GACxB,MAAM,GAAG,IAAI,CAaf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAE3F;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAC9C,gBAAgB,EAAE,wBAAwB,EAAE,EAC5C,gBAAgB,GAAE,wBAAwB,EAAO,GAClD,wBAAwB,EAAE,CA0B5B"}
@@ -1,4 +1,4 @@
1
- // (C) 2020-2025 GoodData Corporation
1
+ // (C) 2020-2026 GoodData Corporation
2
2
  import { compact, isEqual, uniqWith } from "lodash-es";
3
3
  import { isCrossFiltering, isDrillToLegacyDashboard, } from "@gooddata/sdk-model";
4
4
  import { isDrillIntersectionAttributeItem } from "@gooddata/sdk-ui";
@@ -28,7 +28,8 @@ export function getDrillOriginAttributeElementTitle(drillOriginLocalId, drillEve
28
28
  .map((intersectionElement) => intersectionElement.header)
29
29
  .filter(isDrillIntersectionAttributeItem)
30
30
  .filter((intersectionAttributeItem) => intersectionAttributeItem.attributeHeader.localIdentifier === drillOriginLocalId)
31
- .map((intersectionAttributeItem) => intersectionAttributeItem.attributeHeaderItem.name)[0];
31
+ .map((intersectionAttributeItem) => intersectionAttributeItem.attributeHeaderItem.formattedName ??
32
+ intersectionAttributeItem.attributeHeaderItem.name)[0];
32
33
  }
33
34
  /**
34
35
  * Get total number of IDrillToUrl
@@ -1,4 +1,4 @@
1
- import { type DashboardAttributeFilterConfigMode, type DashboardDateFilterConfigMode } from "@gooddata/sdk-model";
1
+ import { type DashboardAttributeFilterConfigMode, type DashboardDateFilterConfigMode, type IAttributeElement, type ISeparators } from "@gooddata/sdk-model";
2
2
  /**
3
3
  * @alpha
4
4
  */
@@ -19,4 +19,11 @@ export declare function useDashboardRelatedFilters(run: boolean): {
19
19
  isError: boolean;
20
20
  isSuccess: boolean;
21
21
  };
22
+ /**
23
+ * Titles to display for the elements: the raw titles, with the values of a numeric computed attribute
24
+ * formatted by the number format of the elements query result.
25
+ *
26
+ * @internal exported for tests
27
+ */
28
+ export declare function elementsDisplayTitles(elements: IAttributeElement[], numberFormat: string | undefined, separators: ISeparators | undefined): Array<string | null>;
22
29
  //# sourceMappingURL=useDashboardRelatedFilters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDashboardRelatedFilters.d.ts","sourceRoot":"","sources":["../../../../src/presentation/export/hooks/useDashboardRelatedFilters.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,kCAAkC,EACvC,KAAK,6BAA6B,EAOrC,MAAM,qBAAqB,CAAC;AAW7B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,IAAI,EAAE,iBAAiB,GAAG,YAAY,GAAG,oBAAoB,CAAC;IAC9D,GAAG,EAAE,OAAO,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,6BAA6B,GAAG,kCAAkC,CAAC;CAC5E,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG;IACtD,WAAW,EAAE,sBAAsB,EAAE,CAAC;IACtC,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;IAC3C,mBAAmB,EAAE,sBAAsB,EAAE,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACtB,CA2DA"}
1
+ {"version":3,"file":"useDashboardRelatedFilters.d.ts","sourceRoot":"","sources":["../../../../src/presentation/export/hooks/useDashboardRelatedFilters.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,kCAAkC,EACvC,KAAK,6BAA6B,EAElC,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EAInB,MAAM,qBAAqB,CAAC;AAkB7B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACjC,IAAI,EAAE,iBAAiB,GAAG,YAAY,GAAG,oBAAoB,CAAC;IAC9D,GAAG,EAAE,OAAO,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,6BAA6B,GAAG,kCAAkC,CAAC;CAC5E,CAAC;AAEF,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG;IACtD,WAAW,EAAE,sBAAsB,EAAE,CAAC;IACtC,gBAAgB,EAAE,sBAAsB,EAAE,CAAC;IAC3C,mBAAmB,EAAE,sBAAsB,EAAE,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACtB,CA4DA;AA4CD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACjC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,UAAU,EAAE,WAAW,GAAG,SAAS,GACpC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAQtB"}
@@ -1,8 +1,9 @@
1
1
  // (C) 2022-2026 GoodData Corporation
2
2
  import { areObjRefsEqual, getAttributeElementsItems, isDashboardAttributeFilter, } from "@gooddata/sdk-model";
3
- import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
3
+ import { createNumberFormatter, formatAttributeValue, useBackendStrict, useCancelablePromise, useWorkspaceStrict, } from "@gooddata/sdk-ui";
4
4
  import { useFiltersNamings } from "../../../_staging/sharedHooks/useFiltersNamings.js";
5
5
  import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
6
+ import { selectSeparators } from "../../../model/store/config/configSelectors.js";
6
7
  import { selectExecutableDashboardFilters } from "../../../model/store/filtering/dashboardFilterSelectors.js";
7
8
  import { selectAttributeFilterConfigsOverrides } from "../../../model/store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
8
9
  import { selectDateFilterConfigOverrides } from "../../../model/store/tabs/dateFilterConfig/dateFilterConfigSelectors.js";
@@ -16,14 +17,15 @@ export function useDashboardRelatedFilters(run) {
16
17
  const dateFiltersConfig = useDashboardSelector(selectDateFilterConfigsOverrides);
17
18
  const attributeFiltersConfig = useDashboardSelector(selectAttributeFilterConfigsOverrides);
18
19
  const measureValueFiltersConfig = useDashboardSelector(selectMeasureValueFilterConfigsOverrides);
20
+ const separators = useDashboardSelector(selectSeparators);
19
21
  const { result, status } = useCancelablePromise({
20
22
  promise: async () => {
21
23
  if (!run) {
22
24
  return dashboardFilters;
23
25
  }
24
- return Promise.all(dashboardFilters.map((f) => updateLabelElements(backend, workspaceId, attributeFiltersConfig, f)));
26
+ return Promise.all(dashboardFilters.map((f) => updateLabelElements(backend, workspaceId, attributeFiltersConfig, separators, f)));
25
27
  },
26
- }, [run, dashboardFilters]);
28
+ }, [run, dashboardFilters, separators]);
27
29
  const filters = useFiltersNamings(result ?? []);
28
30
  const all = filters.filter((f) => f && !f.all).map((f) => {
29
31
  if (f?.common) {
@@ -58,7 +60,7 @@ export function useDashboardRelatedFilters(run) {
58
60
  measureValueFilters: all.filter((f) => f?.type === "measureValueFilter"),
59
61
  };
60
62
  }
61
- async function updateLabelElements(backend, workspaceId, attributeFiltersConfig, dashboardFilter) {
63
+ async function updateLabelElements(backend, workspaceId, attributeFiltersConfig, separators, dashboardFilter) {
62
64
  // Skip filters that are not attribute filters
63
65
  if (isDashboardAttributeFilter(dashboardFilter)) {
64
66
  const config = attributeFiltersConfig.find((c) => c.localIdentifier === dashboardFilter.attributeFilter.localIdentifier);
@@ -76,15 +78,29 @@ async function updateLabelElements(backend, workspaceId, attributeFiltersConfig,
76
78
  });
77
79
  const data = await query.query();
78
80
  const all = await data.all();
81
+ // the filter is only described to the user from here on, so it carries the titles to display
79
82
  return {
80
83
  ...dashboardFilter,
81
84
  attributeFilter: {
82
85
  ...dashboardFilter.attributeFilter,
83
86
  attributeElements: {
84
- values: all.map((a) => a.title),
87
+ values: elementsDisplayTitles(all, data.numberFormat, separators),
85
88
  },
86
89
  },
87
90
  };
88
91
  }
89
92
  return dashboardFilter;
90
93
  }
94
+ /**
95
+ * Titles to display for the elements: the raw titles, with the values of a numeric computed attribute
96
+ * formatted by the number format of the elements query result.
97
+ *
98
+ * @internal exported for tests
99
+ */
100
+ export function elementsDisplayTitles(elements, numberFormat, separators) {
101
+ if (!numberFormat) {
102
+ return elements.map((element) => element.title);
103
+ }
104
+ const formatter = createNumberFormatter(separators);
105
+ return elements.map((element) => formatAttributeValue(element.title, numberFormat, formatter));
106
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultDashboardAttributeFilter.d.ts","sourceRoot":"","sources":["../../../../src/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkD,MAAM,OAAO,CAAC;AAwE1F,OAAO,EAAE,KAAK,8BAA8B,EAAE,MAAM,YAAY,CAAC;AAKjE;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,8BAA8B,GAAG,YAAY,GAAG,IAAI,CAa1G;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAC9C,KAAK,EAAE,8BAA8B,GACtC,YAAY,GAAG,IAAI,CAErB"}
1
+ {"version":3,"file":"DefaultDashboardAttributeFilter.d.ts","sourceRoot":"","sources":["../../../../src/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkD,MAAM,OAAO,CAAC;AAyE1F,OAAO,EAAE,KAAK,8BAA8B,EAAE,MAAM,YAAY,CAAC;AAKjE;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,8BAA8B,GAAG,YAAY,GAAG,IAAI,CAa1G;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAC9C,KAAK,EAAE,8BAA8B,GACtC,YAAY,GAAG,IAAI,CAErB"}
@@ -9,7 +9,7 @@ import { useAttributes } from "../../../_staging/sharedHooks/useAttributes.js";
9
9
  import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
10
10
  import { useDashboardUserInteraction } from "../../../model/react/useDashboardUserInteraction.js";
11
11
  import { selectBackendCapabilities } from "../../../model/store/backendCapabilities/backendCapabilitiesSelectors.js";
12
- import { selectAvailableAttributeFilterSelectionTypes, selectEnableImmediateAttributeFilterDisplayAsLabelMigration, selectIsApplyFiltersAllAtOnceEnabledAndSet, selectLocale, } from "../../../model/store/config/configSelectors.js";
12
+ import { selectAvailableAttributeFilterSelectionTypes, selectEnableImmediateAttributeFilterDisplayAsLabelMigration, selectIsApplyFiltersAllAtOnceEnabledAndSet, selectLocale, selectSeparators, } from "../../../model/store/config/configSelectors.js";
13
13
  import { selectIsFilterFromCrossFilteringByLocalIdentifier } from "../../../model/store/drill/drillSelectors.js";
14
14
  import { selectIsInEditMode } from "../../../model/store/renderMode/renderModeSelectors.js";
15
15
  import { selectAttributeFilterConfigsModeMap, selectAttributeFilterConfigsModeMapByTab, selectAttributeFilterConfigsSelectionTypeMap, selectAttributeFilterConfigsSelectionTypeMapByTab, } from "../../../model/store/tabs/attributeFilterConfigs/attributeFilterConfigsSelectors.js";
@@ -54,6 +54,7 @@ function DefaultDashboardAttributeFilterInner(props) {
54
54
  const locale = useDashboardSelector(selectLocale);
55
55
  const isEditMode = useDashboardSelector(selectIsInEditMode);
56
56
  const capabilities = useDashboardSelector(selectBackendCapabilities);
57
+ const separators = useDashboardSelector(selectSeparators);
57
58
  // Extract common properties using wide utilities; narrow props only for standard filters
58
59
  const filterLocalId = dashboardAttributeFilterItemLocalIdentifier(filter);
59
60
  const filterTitle = dashboardAttributeFilterItemTitle(filter);
@@ -325,7 +326,7 @@ function DefaultDashboardAttributeFilterInner(props) {
325
326
  isApplyAllAtOnceEnabledAndSet,
326
327
  ]);
327
328
  const AttributeFilterComponent = props.AttributeFilterComponent ?? AttributeFilterButton;
328
- const filterContent = (_jsx(AttributeFilterComponent, { title: filterTitle, resetOnParentFilterChange: false, filter: isApplyAllAtOnceEnabledAndSet ? workingAttributeFilter : attributeFilter, displayAsLabel: displayAsLabel, overlayPositionType: overlayPositionType, onApply: (newFilter, isInverted, selectionMode, selectionTitles, displayAsLabel, isResultOfMigration, additionalProps) => {
329
+ const filterContent = (_jsx(AttributeFilterComponent, { title: filterTitle, separators: separators, resetOnParentFilterChange: false, filter: isApplyAllAtOnceEnabledAndSet ? workingAttributeFilter : attributeFilter, displayAsLabel: displayAsLabel, overlayPositionType: overlayPositionType, onApply: (newFilter, isInverted, selectionMode, selectionTitles, displayAsLabel, isResultOfMigration, additionalProps) => {
329
330
  const isSelectionInvalid = additionalProps?.isSelectionInvalid ?? false;
330
331
  const applyToWorkingOnly = additionalProps?.applyToWorkingOnly ?? false;
331
332
  // Convert the SDK-level filter to a dashboard filter item.
@@ -1 +1 @@
1
- {"version":3,"file":"AttributeListItemTooltip.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,KAAK,iBAAiB,EAA0B,MAAM,qBAAqB,CAAC;AAgBrF,UAAU,8BAA8B;IACpC,IAAI,EAAE,iBAAiB,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,IAAI,EAAE,EAAE,8BAA8B,GAAG,SAAS,CAkD5F"}
1
+ {"version":3,"file":"AttributeListItemTooltip.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/filterBar/attributeFilter/addAttributeFilter/attributeListItemTooltip/AttributeListItemTooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,KAAK,iBAAiB,EAA0B,MAAM,qBAAqB,CAAC;AAkBrF,UAAU,8BAA8B;IACpC,IAAI,EAAE,iBAAiB,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,IAAI,EAAE,EAAE,8BAA8B,GAAG,SAAS,CAoD5F"}