@gooddata/sdk-ui-pivot 11.57.0-alpha.5 → 11.57.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/esm/impl/data/resultTypes.d.ts.map +1 -1
  2. package/esm/impl/data/resultTypes.js +1 -0
  3. package/esm/impl/structure/headers/TotalHeaderCell.js +1 -1
  4. package/esm/locales.d.ts.map +1 -1
  5. package/esm/locales.js +6 -0
  6. package/esm/next/PivotTableNext.d.ts.map +1 -1
  7. package/esm/next/PivotTableNext.js +2 -1
  8. package/esm/next/components/Cell/AttributeCell.d.ts.map +1 -1
  9. package/esm/next/components/Cell/AttributeCell.js +38 -25
  10. package/esm/next/components/Header/MeasureHeader.d.ts.map +1 -1
  11. package/esm/next/components/Header/MeasureHeader.js +18 -4
  12. package/esm/next/components/Header/PivotGroupHeader.d.ts.map +1 -1
  13. package/esm/next/components/Header/PivotGroupHeader.js +17 -4
  14. package/esm/next/components/Header/utils/constructAggregationsMenuItems.d.ts.map +1 -1
  15. package/esm/next/components/Header/utils/constructAggregationsMenuItems.js +3 -2
  16. package/esm/next/components/TotalLabel/RenameTotalLabelPopover.d.ts +19 -0
  17. package/esm/next/components/TotalLabel/RenameTotalLabelPopover.d.ts.map +1 -0
  18. package/esm/next/components/TotalLabel/RenameTotalLabelPopover.js +19 -0
  19. package/esm/next/components/TotalLabel/TotalActionMenu.d.ts +10 -0
  20. package/esm/next/components/TotalLabel/TotalActionMenu.d.ts.map +1 -0
  21. package/esm/next/components/TotalLabel/TotalActionMenu.js +33 -0
  22. package/esm/next/context/PivotTablePropsContext.d.ts +1 -1
  23. package/esm/next/context/PivotTablePropsContext.d.ts.map +1 -1
  24. package/esm/next/context/PivotTablePropsContext.js +1 -0
  25. package/esm/next/context/TotalLabelContext.d.ts +13 -0
  26. package/esm/next/context/TotalLabelContext.d.ts.map +1 -0
  27. package/esm/next/context/TotalLabelContext.js +74 -0
  28. package/esm/next/features/aggregations/totalLabelTarget.d.ts +35 -0
  29. package/esm/next/features/aggregations/totalLabelTarget.d.ts.map +1 -0
  30. package/esm/next/features/aggregations/totalLabelTarget.js +111 -0
  31. package/esm/next/features/aggregations/totals.d.ts +33 -0
  32. package/esm/next/features/aggregations/totals.d.ts.map +1 -0
  33. package/esm/next/features/aggregations/totals.js +73 -0
  34. package/esm/next/features/columns/shared.d.ts.map +1 -1
  35. package/esm/next/features/columns/shared.js +11 -3
  36. package/esm/next/features/transposition/totalHeaderColDef.d.ts.map +1 -1
  37. package/esm/next/features/transposition/totalHeaderColDef.js +7 -2
  38. package/esm/next/hooks/header/useHeaderCellAriaLabel.d.ts +11 -2
  39. package/esm/next/hooks/header/useHeaderCellAriaLabel.d.ts.map +1 -1
  40. package/esm/next/hooks/header/useHeaderCellAriaLabel.js +30 -6
  41. package/esm/next/hooks/header/useHeaderMenu.d.ts +2 -0
  42. package/esm/next/hooks/header/useHeaderMenu.d.ts.map +1 -1
  43. package/esm/next/hooks/header/useHeaderMenu.js +3 -2
  44. package/esm/next/hooks/header/useHeaderMenuSorting.d.ts +1 -1
  45. package/esm/next/hooks/header/useHeaderMenuSorting.d.ts.map +1 -1
  46. package/esm/next/hooks/header/useHeaderMenuSorting.js +7 -4
  47. package/esm/next/hooks/header/useTotalLabelHeader.d.ts +29 -0
  48. package/esm/next/hooks/header/useTotalLabelHeader.d.ts.map +1 -0
  49. package/esm/next/hooks/header/useTotalLabelHeader.js +48 -0
  50. package/esm/next/hooks/totals/useUpdateTotals.d.ts.map +1 -1
  51. package/esm/next/hooks/totals/useUpdateTotals.js +10 -4
  52. package/esm/next/hooks/useInteractionProps.d.ts.map +1 -1
  53. package/esm/next/hooks/useInteractionProps.js +45 -10
  54. package/esm/next/types/menu.d.ts +9 -0
  55. package/esm/next/types/menu.d.ts.map +1 -1
  56. package/esm/next/types/menu.js +1 -1
  57. package/esm/sdk-ui-pivot.d.ts +9 -0
  58. package/esm/tableState.d.ts.map +1 -1
  59. package/esm/tableState.js +1 -0
  60. package/package.json +16 -16
  61. package/styles/css/main.css +18 -0
  62. package/styles/css/main.css.map +1 -1
  63. package/styles/css/pivotTableNext.css +18 -0
  64. package/styles/css/pivotTableNext.css.map +1 -1
  65. package/styles/scss/pivotTableNext.scss +27 -0
@@ -1 +1 @@
1
- {"version":3,"file":"resultTypes.d.ts","sourceRoot":"","sources":["../../../src/impl/data/resultTypes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACrB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnB;;OAEG;IACH,aAAa,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC;KACjC,CAAC;IAEF;;OAEG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAEvC;;OAEG;IACH,yBAAyB,CAAC,EAAE,oBAAoB,CAAC;IAEjD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAEnE"}
1
+ {"version":3,"file":"resultTypes.d.ts","sourceRoot":"","sources":["../../../src/impl/data/resultTypes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACrB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnB;;OAEG;IACH,aAAa,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC;KACjC,CAAC;IAEF;;OAEG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAEvC;;OAEG;IACH,yBAAyB,CAAC,EAAE,oBAAoB,CAAC;IAEjD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAEnE"}
@@ -1,3 +1,4 @@
1
+ // (C) 2007-2026 GoodData Corporation
1
2
  import { ROW_TOTAL } from "../base/constants.js";
2
3
  export function isGridTotalsRow(obj) {
3
4
  return obj?.type === ROW_TOTAL;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // (C) 2023-2025 GoodData Corporation
2
+ // (C) 2023-2026 GoodData Corporation
3
3
  import cx from "classnames";
4
4
  import { HEADER_LABEL_CLASS } from "../../base/constants.js";
5
5
  export const ALIGN_LEFT = "left";
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAwCrD,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAOlE,CAAC"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CA8CrD,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAOlE,CAAC"}
package/esm/locales.js CHANGED
@@ -10,6 +10,12 @@ export const messages = defineMessages({
10
10
  med: { id: "visualizations.totals.dropdown.title.med" },
11
11
  "disabled.mvf": { id: "visualizations.totals.dropdown.tooltip.nat.disabled.mvf" },
12
12
  "disabled.ranking": { id: "visualizations.totals.dropdown.tooltip.nat.disabled.ranking" },
13
+ // Total label rename action menu / popover
14
+ totalLabelActions: { id: "visualizations.totals.labelActions.aria" },
15
+ renameTotalLabel: { id: "visualizations.totals.renameLabel.action" },
16
+ resetTotalLabel: { id: "visualizations.totals.resetLabel.action" },
17
+ cancel: { id: "cancel" },
18
+ save: { id: "save" },
13
19
  // Aggregations menu
14
20
  aggregationsSection: { id: "visualizations.menu.aggregations" },
15
21
  rowsSection: { id: "visualizations.menu.aggregations.rows" },
@@ -1 +1 @@
1
- {"version":3,"file":"PivotTableNext.d.ts","sourceRoot":"","sources":["../../src/next/PivotTableNext.tsx"],"names":[],"mappings":"AAwCA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,2CAKzD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,wBAAwB,2CAoB3E"}
1
+ {"version":3,"file":"PivotTableNext.d.ts","sourceRoot":"","sources":["../../src/next/PivotTableNext.tsx"],"names":[],"mappings":"AAyCA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,2CAKzD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,wBAAwB,2CAoB3E"}
@@ -20,6 +20,7 @@ import { PivotTablePropsProvider, usePivotTableProps } from "./context/PivotTabl
20
20
  import { PivotTableSizingProvider, usePivotTableSizingActions } from "./context/PivotTableSizingContext.js";
21
21
  import { RuntimeErrorProvider, useRuntimeError } from "./context/RuntimeErrorContext.js";
22
22
  import { TableReadyProvider } from "./context/TableReadyContext.js";
23
+ import { TotalLabelProvider } from "./context/TotalLabelContext.js";
23
24
  import { b } from "./features/styling/bem.js";
24
25
  import { useInitExecution } from "./hooks/init/useInitExecution.js";
25
26
  import { useInitExecutionResult } from "./hooks/init/useInitExecutionResult.js";
@@ -78,7 +79,7 @@ function PivotTableNextWithInitialization() {
78
79
  return _jsx(ErrorComponent, { error: error });
79
80
  }
80
81
  const { initialExecutionResult, initialDataView } = result;
81
- return (_jsx(InitialExecutionContextProvider, { initialExecutionResult: initialExecutionResult, initialDataView: initialDataView, children: _jsx(TableReadyProvider, { children: _jsx(DrillableItemsRefProvider, { children: _jsx(ColumnDefsProvider, { children: _jsx(HeaderMenuProvider, { children: _jsx(RenderPivotTableNextAgGrid, {}) }) }) }) }) }));
82
+ return (_jsx(InitialExecutionContextProvider, { initialExecutionResult: initialExecutionResult, initialDataView: initialDataView, children: _jsx(TableReadyProvider, { children: _jsx(DrillableItemsRefProvider, { children: _jsx(ColumnDefsProvider, { children: _jsx(TotalLabelProvider, { children: _jsx(HeaderMenuProvider, { children: _jsx(RenderPivotTableNextAgGrid, {}) }) }) }) }) }) }));
82
83
  }
83
84
  function RenderPivotTableNextAgGrid() {
84
85
  const agGridReactProps = useAgGridReactProps();
@@ -1 +1 @@
1
- {"version":3,"file":"AttributeCell.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Cell/AttributeCell.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACH,KAAK,+BAA+B,EAKvC,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAmB9D;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,mBAAmB,GAAG;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB,2CAwEJ"}
1
+ {"version":3,"file":"AttributeCell.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Cell/AttributeCell.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACH,KAAK,+BAA+B,EAKvC,MAAM,kBAAkB,CAAC;AAa1B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAmB9D;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,mBAAmB,GAAG;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB,2CA8FJ"}
@@ -1,6 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2025-2026 GoodData Corporation
3
+ import { useCallback, useEffect, useRef } from "react";
2
4
  import { emptyHeaderTitleFromIntl, isTableGrandTotalHeaderValue, isTableTotalHeaderValue, } from "@gooddata/sdk-ui";
3
- import { getAttributeColIds, shouldGroupAttribute } from "../../features/columns/shared.js";
5
+ import { useTotalLabelContext } from "../../context/TotalLabelContext.js";
6
+ import { isFirstTotalHeaderAttributeCell, resolveTotalLabelTargetFromTotalLabelCellData, } from "../../features/aggregations/totalLabelTarget.js";
7
+ import { shouldGroupAttribute } from "../../features/columns/shared.js";
4
8
  import { e } from "../../features/styling/bem.js";
5
9
  import { getPivotCellAttributeImageTestIdProps, getPivotCellTestIdPropsFromCellTypes, } from "../../testing/dataTestIdGenerators.js";
6
10
  import { ImageCell } from "./ImageCell.js";
@@ -27,31 +31,40 @@ export function AttributeCell(params) {
27
31
  const colId = params.colId;
28
32
  const columnDefinition = params.columnDefinition;
29
33
  const dataTestIdProps = getPivotCellTestIdPropsFromCellTypes(params.cellTypes);
34
+ const cellData = params.data?.cellDataByColId?.[colId];
35
+ const isTotalHeaderCell = !!cellData && (isTableTotalHeaderValue(cellData) || isTableGrandTotalHeaderValue(cellData));
36
+ const isFirstTotalCell = isTotalHeaderCell && isFirstTotalHeaderAttributeCell(params.data, colId);
37
+ const { enabled: totalLabelsEditable } = useTotalLabelContext();
38
+ const isRenameableTotalCell = totalLabelsEditable && isFirstTotalCell && !!resolveTotalLabelTargetFromTotalLabelCellData(cellData);
39
+ const cellRef = useRef(null);
40
+ const setCellRef = useCallback((node) => {
41
+ cellRef.current = node;
42
+ }, []);
43
+ useEffect(() => {
44
+ const gridCell = cellRef.current?.closest("[role='gridcell']");
45
+ if (!gridCell || !isRenameableTotalCell) {
46
+ return;
47
+ }
48
+ const previousAriaHasPopup = gridCell.getAttribute("aria-haspopup");
49
+ gridCell.setAttribute("aria-haspopup", "menu");
50
+ return () => {
51
+ if (previousAriaHasPopup === null) {
52
+ gridCell.removeAttribute("aria-haspopup");
53
+ }
54
+ else {
55
+ gridCell.setAttribute("aria-haspopup", previousAriaHasPopup);
56
+ }
57
+ };
58
+ }, [isRenameableTotalCell]);
30
59
  // 1) Empty value handling - must be checked first
31
60
  if (!value) {
32
61
  return _jsx("span", { ...dataTestIdProps, children: emptyHeaderTitleFromIntl(params.intl) });
33
62
  }
34
- // 2) Total/grand-total header visibility: render title only in the first attribute column
35
- // If this is a total/grand-total header cell, render the title only in the first
36
- // attribute column that carries the total header in this row. Hide it in others.
37
- const cellData = params.data?.cellDataByColId?.[colId];
38
- const isTotalHeaderCell = !!cellData && (isTableTotalHeaderValue(cellData) || isTableGrandTotalHeaderValue(cellData));
39
- if (isTotalHeaderCell) {
40
- const attributeColIds = getAttributeColIds(params.data);
41
- // Find the first attribute column (by columnIndex) that has a total/grand-total header in this row
42
- const firstTotalAttrColId = attributeColIds
43
- .filter((id) => {
44
- const c = params.data?.cellDataByColId?.[id];
45
- return !!c && (isTableTotalHeaderValue(c) || isTableGrandTotalHeaderValue(c));
46
- })
47
- .sort((a, b) => {
48
- const ai = params.data.cellDataByColId[a].columnDefinition.columnIndex;
49
- const bi = params.data.cellDataByColId[b].columnDefinition.columnIndex;
50
- return ai - bi;
51
- })[0];
52
- if (firstTotalAttrColId && firstTotalAttrColId !== params.colId) {
53
- return _jsx("span", { ...dataTestIdProps });
54
- }
63
+ // If this is a total/grand-total header cell, render the title only in the first attribute
64
+ // column that carries the total header in this row (see isFirstTotalHeaderAttributeCell) -
65
+ // hide it in the others.
66
+ if (isTotalHeaderCell && !isFirstTotalCell) {
67
+ return _jsx("span", { ...dataTestIdProps });
55
68
  }
56
69
  // 3) Image rendering: check if attribute should be rendered as an image
57
70
  // Images respect text wrapping configuration:
@@ -62,17 +75,17 @@ export function AttributeCell(params) {
62
75
  const isImage = labelType === IMAGE_LABEL_TYPE;
63
76
  if (isImage) {
64
77
  const primaryLabelValue = getPrimaryLabelValue(cellData);
65
- return (_jsx("div", { className: e("cell-image-wrapper"), ...getPivotCellAttributeImageTestIdProps(), children: _jsx(ImageCell, { src: value, alt: primaryLabelValue }) }));
78
+ return (_jsx("div", { ref: setCellRef, className: e("cell-image-wrapper"), ...getPivotCellAttributeImageTestIdProps(), children: _jsx(ImageCell, { src: value, alt: primaryLabelValue }) }));
66
79
  }
67
80
  // 4) Grouping suppression: do not render repeating attribute values
68
81
  const rowIndex = params.node.rowIndex;
69
82
  const previousRow = rowIndex ? params.api.getDisplayedRowAtIndex(rowIndex - 1) : null;
70
83
  if (!previousRow?.data) {
71
- return _jsx("span", { ...dataTestIdProps, children: value });
84
+ return (_jsx("span", { ref: setCellRef, ...dataTestIdProps, children: value }));
72
85
  }
73
86
  const shouldGroup = shouldGroupAttribute(params, previousRow, columnDefinition);
74
87
  if (shouldGroup) {
75
88
  return _jsx("span", { ...dataTestIdProps });
76
89
  }
77
- return _jsx("span", { ...dataTestIdProps, children: value });
90
+ return (_jsx("span", { ref: setCellRef, ...dataTestIdProps, children: value }));
78
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MeasureHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/MeasureHeader.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAYtF;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,2CAiHvD"}
1
+ {"version":3,"file":"MeasureHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/MeasureHeader.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAYtF;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,2CAiIvD"}
@@ -3,11 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { isGrandTotalColumnDefinition, isSubtotalColumnDefinition } from "@gooddata/sdk-ui";
5
5
  import { useColumnDefs } from "../../context/ColumnDefsContext.js";
6
+ import { resolveTotalLabelTargetFromColumnDefinition } from "../../features/aggregations/totalLabelTarget.js";
6
7
  import { e } from "../../features/styling/bem.js";
7
8
  import { useHeaderCellAriaLabel } from "../../hooks/header/useHeaderCellAriaLabel.js";
8
9
  import { useHeaderMenu } from "../../hooks/header/useHeaderMenu.js";
9
10
  import { useHeaderMenuKeyboard } from "../../hooks/header/useHeaderMenuKeyboard.js";
10
11
  import { useHeaderSpaceKey } from "../../hooks/header/useHeaderSpaceKey.js";
12
+ import { useTotalLabelHeader } from "../../hooks/header/useTotalLabelHeader.js";
11
13
  import { useIsTransposed } from "../../hooks/shared/useIsTransposed.js";
12
14
  import { getPivotHeaderClickableAreaTestIdProps, getPivotHeaderTestIdProps, getPivotHeaderTextTestIdProps, } from "../../testing/dataTestIdGenerators.js";
13
15
  import { HeaderMenu } from "./HeaderCell/HeaderMenu.js";
@@ -24,6 +26,14 @@ export function MeasureHeader(params) {
24
26
  const { isPivoted } = useColumnDefs();
25
27
  const colDef = params.column.getColDef();
26
28
  const columnDefinition = colDef?.context?.columnDefinition;
29
+ // In a non-transposed table, a total/subtotal column's leaf headers still carry the total's
30
+ // column definition (that's how they pick up total/subtotal styling), but they display the
31
+ // measure name, not the total's own label - PivotGroupHeader owns that label there. Only in a
32
+ // transposed table does this header cell represent the total's own label.
33
+ const totalLabelTarget = isTransposed
34
+ ? resolveTotalLabelTargetFromColumnDefinition(columnDefinition)
35
+ : undefined;
36
+ const { label: customTotalLabel, onClick: handleTotalLabelClick } = useTotalLabelHeader(params.eGridHeader, totalLabelTarget);
27
37
  const isValueColDef = isValueColumnDef(columnDefinition);
28
38
  const columnScope = getColumnScope(columnDefinition);
29
39
  const pivotAttributeDescriptors = getPivotAttributeDescriptors(columnScope);
@@ -47,16 +57,20 @@ export function MeasureHeader(params) {
47
57
  allowDrilling,
48
58
  includeHeaderWrapping,
49
59
  includeCellWrapping,
50
- }, { measureIdentifiers: measureIdentifier ? [measureIdentifier] : [], pivotAttributeDescriptors }, params);
51
- useHeaderSpaceKey(params, handleHeaderClick);
60
+ }, {
61
+ measureIdentifiers: measureIdentifier ? [measureIdentifier] : [],
62
+ pivotAttributeDescriptors,
63
+ ariaLabelDisplayNameOverride: customTotalLabel,
64
+ }, params);
65
+ useHeaderSpaceKey(params, handleTotalLabelClick ?? handleHeaderClick);
52
66
  useHeaderMenuKeyboard(params, () => {
53
67
  setIsKeyboardTriggered(true);
54
68
  setIsMenuOpen(true);
55
69
  }, hasMenuItems);
56
- useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel);
70
+ useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel, params.displayName);
57
71
  return (_jsx(HeaderKeyboardHint, { eGridHeader: params.eGridHeader, enabled: isFirstDisplayedColumn(params), canSort: allowSorting, canOpenMenu: hasMenuItems, children: _jsxs("div", { className: e("header-cell", {
58
72
  "is-menu-open": isMenuOpen,
59
- }), ...getPivotHeaderTestIdProps({ isTotal, isSubtotal }), children: [_jsxs("div", { className: "gd-header-content", "aria-hidden": "true", children: [_jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children: params.displayName }), !!colDef.sortable && sortDirection ? (_jsx(SortIndicator, { sortDirection: sortDirection, sortIndex: sortIndex })) : null] }), !!colDef.sortable && (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })), hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
73
+ }), onClick: handleTotalLabelClick, ...getPivotHeaderTestIdProps({ isTotal, isSubtotal }), children: [_jsxs("div", { className: "gd-header-content", "aria-hidden": "true", children: [_jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children: customTotalLabel ?? params.displayName }), !!colDef.sortable && sortDirection ? (_jsx(SortIndicator, { sortDirection: sortDirection, sortIndex: sortIndex })) : null] }), !!colDef.sortable && (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })), hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
60
74
  setIsMenuOpen(opened);
61
75
  if (!opened) {
62
76
  setIsKeyboardTriggered(false);
@@ -1 +1 @@
1
- {"version":3,"file":"PivotGroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/PivotGroupHeader.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAA6B,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAKhG,UAAU,qBAAsB,SAAQ,uBAAuB;IAC3D,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,2CAwI7D"}
1
+ {"version":3,"file":"PivotGroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/PivotGroupHeader.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAA6B,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAKhG,UAAU,qBAAsB,SAAQ,uBAAuB;IAC3D,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,2CAuJ7D"}
@@ -2,12 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2025-2026 GoodData Corporation
3
3
  import { useCallback, useState } from "react";
4
4
  import { isGrandTotalColumnDefinition, isSubtotalColumnDefinition } from "@gooddata/sdk-ui";
5
+ import { resolveTotalLabelTargetFromColumnDefinition } from "../../features/aggregations/totalLabelTarget.js";
5
6
  import { e } from "../../features/styling/bem.js";
6
7
  import { useHeaderCellAriaLabel } from "../../hooks/header/useHeaderCellAriaLabel.js";
7
8
  import { useHeaderGroupDrilling } from "../../hooks/header/useHeaderGroupDrilling.js";
8
9
  import { useHeaderMenu } from "../../hooks/header/useHeaderMenu.js";
9
10
  import { useHeaderMenuKeyboard } from "../../hooks/header/useHeaderMenuKeyboard.js";
10
11
  import { useHeaderSpaceKey } from "../../hooks/header/useHeaderSpaceKey.js";
12
+ import { useTotalLabelHeader } from "../../hooks/header/useTotalLabelHeader.js";
11
13
  import { useIsTransposed } from "../../hooks/shared/useIsTransposed.js";
12
14
  import { getPivotHeaderClickableAreaTestIdProps, getPivotHeaderTestIdProps, getPivotHeaderTextTestIdProps, } from "../../testing/dataTestIdGenerators.js";
13
15
  import { HeaderMenu } from "./HeaderCell/HeaderMenu.js";
@@ -21,6 +23,7 @@ export function PivotGroupHeader(params) {
21
23
  const isTransposed = useIsTransposed();
22
24
  const colGroupDef = params.columnGroup.getColGroupDef();
23
25
  const columnDefinition = colGroupDef?.context?.columnDefinition;
26
+ const resolvedTotalLabelTarget = resolveTotalLabelTargetFromColumnDefinition(columnDefinition);
24
27
  const isValueColDef = isValueColumnDef(columnDefinition);
25
28
  const columnScope = getColumnScope(columnDefinition);
26
29
  const pivotAttributeDescriptors = getPivotAttributeDescriptors(columnScope);
@@ -35,6 +38,15 @@ export function PivotGroupHeader(params) {
35
38
  colGroupDef.children?.some((child) => isSubtotalColumnDefinition(child.context?.columnDefinition));
36
39
  const isTotalHeader = isTotal || isTotalGroup;
37
40
  const isSubtotalHeader = isSubtotal || isSubtotalGroup;
41
+ // shouldSkipHeaderName blanks nested repeats of a total group ("Sum" > "Sum"); they must not
42
+ // become rename targets. When transposed, MeasureHeader's own leaf cell owns the total's label
43
+ // instead (see its comment) - this group must not also show/target it, or both cells end up
44
+ // showing "Sum" at once.
45
+ const hasVisibleHeaderName = colGroupDef?.headerName !== undefined;
46
+ const ownsTotalLabel = hasVisibleHeaderName && (!isTransposed || !(isTotalHeader || isSubtotalHeader));
47
+ const totalLabelTarget = ownsTotalLabel ? resolvedTotalLabelTarget : undefined;
48
+ const { label: customTotalLabel, onClick: handleTotalLabelClick } = useTotalLabelHeader(params.eGridHeader, totalLabelTarget);
49
+ const visibleHeaderText = ownsTotalLabel ? (customTotalLabel ?? params.displayName) : "";
38
50
  const allowAggregations = params.pivotGroupDepth !== 0 && // Not description level of the pivoting group
39
51
  isValueColDef &&
40
52
  !isTransposed;
@@ -55,6 +67,7 @@ export function PivotGroupHeader(params) {
55
67
  }, {
56
68
  measureIdentifiers: params.measureIdentifiers,
57
69
  pivotAttributeDescriptors,
70
+ ariaLabelDisplayNameOverride: customTotalLabel,
58
71
  }, params);
59
72
  const hasMenuItems = aggregationsItems.length > 0 || textWrappingItems.length > 0;
60
73
  const { handleHeaderDrill, isDrillable } = useHeaderGroupDrilling(params);
@@ -64,20 +77,20 @@ export function PivotGroupHeader(params) {
64
77
  handleHeaderDrill(e);
65
78
  }
66
79
  }, [isDrillable, handleHeaderDrill]);
67
- useHeaderSpaceKey(params, handleHeaderClick);
80
+ useHeaderSpaceKey(params, handleTotalLabelClick ?? handleHeaderClick);
68
81
  useHeaderMenuKeyboard(params, () => {
69
82
  setIsKeyboardTriggered(true);
70
83
  setIsMenuOpen(true);
71
84
  }, hasMenuItems);
72
- useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel);
85
+ useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel, params.displayName);
73
86
  return (_jsxs("div", { className: e("header-cell", {
74
87
  "is-menu-open": isMenuOpen,
75
88
  drillable: isDrillable,
76
- }), ...getPivotHeaderTestIdProps({
89
+ }), onClick: handleTotalLabelClick, ...getPivotHeaderTestIdProps({
77
90
  drillable: isDrillable,
78
91
  isTotal: isTotalHeader,
79
92
  isSubtotal: isSubtotalHeader,
80
- }), children: [_jsx("div", { className: "gd-header-content", "aria-hidden": "true", children: _jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children: params.displayName }) }), isDrillable ? (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })) : null, hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
93
+ }), children: [_jsx("div", { className: "gd-header-content", "aria-hidden": "true", children: _jsx("span", { className: "gd-header-text", ...(ownsTotalLabel ? getPivotHeaderTextTestIdProps() : {}), children: visibleHeaderText }) }), isDrillable ? (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })) : null, hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
81
94
  setIsMenuOpen(opened);
82
95
  if (!opened) {
83
96
  setIsKeyboardTriggered(false);
@@ -1 +1 @@
1
- {"version":3,"file":"constructAggregationsMenuItems.d.ts","sourceRoot":"","sources":["../../../../../src/next/components/Header/utils/constructAggregationsMenuItems.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACH,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,MAAM,EAGd,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACH,KAAK,qBAAqB,EAE1B,KAAK,KAAK,EACb,MAAM,wBAAwB,CAAC;AA+HhC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,uBACnB,MAAM,EAAE,oBACV,MAAM,EAAE,uBACL,MAAM,EAAE,QACvB,UAAU,EAAE,WACT,UAAU,EAAE,2BACI,oBAAoB,EAAE,6BACpB,oBAAoB,EAAE,QAC3C,KAAK,QACL,SAAS,oBACG,MAAM,KACzB,qBAAqB,EAkDvB,CAAC"}
1
+ {"version":3,"file":"constructAggregationsMenuItems.d.ts","sourceRoot":"","sources":["../../../../../src/next/components/Header/utils/constructAggregationsMenuItems.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACH,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,MAAM,EAGd,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACH,KAAK,qBAAqB,EAE1B,KAAK,KAAK,EACb,MAAM,wBAAwB,CAAC;AA+HhC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,uBACnB,MAAM,EAAE,oBACV,MAAM,EAAE,uBACL,MAAM,EAAE,QACvB,UAAU,EAAE,WACT,UAAU,EAAE,2BACI,oBAAoB,EAAE,6BACpB,oBAAoB,EAAE,QAC3C,KAAK,QACL,SAAS,oBACG,MAAM,KACzB,qBAAqB,EAkDvB,CAAC"}
@@ -1,8 +1,9 @@
1
1
  // (C) 2025-2026 GoodData Corporation
2
- import { compact, isEqual } from "lodash-es";
2
+ import { compact } from "lodash-es";
3
3
  import { newTotal, } from "@gooddata/sdk-model";
4
4
  import { messages } from "../../../../locales.js";
5
5
  import { DEFAULT_TOTAL_FUNCTIONS } from "../../../constants/internal.js";
6
+ import { areTotalsSameDefinition } from "../../../features/aggregations/totals.js";
6
7
  import { getPreviousAttributeHeaderName } from "./common.js";
7
8
  /**
8
9
  * Creates total item for the main attribute.
@@ -129,5 +130,5 @@ function isTotalActive(currentTotals, totalDefinitions) {
129
130
  if (totalDefinitions.length === 0) {
130
131
  return false;
131
132
  }
132
- return totalDefinitions.every((totalDef) => currentTotals.some((currentTotal) => isEqual(currentTotal, totalDef)));
133
+ return totalDefinitions.every((totalDef) => currentTotals.some((currentTotal) => areTotalsSameDefinition(currentTotal, totalDef)));
133
134
  }
@@ -0,0 +1,19 @@
1
+ import { type IntlShape } from "react-intl";
2
+ export interface IRenameTotalLabelPopoverProps {
3
+ intl: IntlShape;
4
+ anchor: HTMLElement;
5
+ /** Current custom label (alias), or empty string when none is set. */
6
+ currentLabel: string;
7
+ /** Localized default function-name label, shown as the input placeholder. */
8
+ defaultLabel: string;
9
+ /** Called with the new label, or `undefined` to reset to the default. */
10
+ onSave: (newLabel: string | undefined) => void;
11
+ onCancel: () => void;
12
+ }
13
+ /**
14
+ * Small popover anchored to the selected total. Holds a label input (with the default label as
15
+ * placeholder fallback), plus Reset to default / Cancel / Save controls. Saving an empty value is
16
+ * treated as a reset to the default label.
17
+ */
18
+ export declare function RenameTotalLabelPopover({ intl, anchor, currentLabel, defaultLabel, onSave, onCancel, }: IRenameTotalLabelPopoverProps): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=RenameTotalLabelPopover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenameTotalLabelPopover.d.ts","sourceRoot":"","sources":["../../../../src/next/components/TotalLabel/RenameTotalLabelPopover.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5C,MAAM,WAAW,6BAA6B;IAC1C,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC/C,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,QAAQ,GACX,EAAE,6BAA6B,2CA2D/B"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2025-2026 GoodData Corporation
3
+ import { useState } from "react";
4
+ import { Button, Input, Overlay, UiFocusTrap } from "@gooddata/sdk-ui-kit";
5
+ import { messages } from "../../../locales.js";
6
+ const MAX_LABEL_LENGTH = 255;
7
+ /**
8
+ * Small popover anchored to the selected total. Holds a label input (with the default label as
9
+ * placeholder fallback), plus Reset to default / Cancel / Save controls. Saving an empty value is
10
+ * treated as a reset to the default label.
11
+ */
12
+ export function RenameTotalLabelPopover({ intl, anchor, currentLabel, defaultLabel, onSave, onCancel, }) {
13
+ const [value, setValue] = useState(currentLabel);
14
+ const save = () => {
15
+ const trimmed = value.trim();
16
+ onSave(trimmed.length > 0 ? trimmed : undefined);
17
+ };
18
+ return (_jsx(Overlay, { className: "overlay", alignTo: anchor, alignPoints: [{ align: "bl tl" }, { align: "tl bl" }, { align: "br tr" }], closeOnOutsideClick: true, closeOnParentScroll: true, closeOnEscape: true, onClose: onCancel, children: _jsxs(UiFocusTrap, { root: _jsx("div", { className: "gd-pivot-table-rename-total-popover s-pivot-table-rename-total-popover", role: "dialog", "aria-label": intl.formatMessage(messages["renameTotalLabel"]) }), children: [_jsx(Input, { className: "s-rename-total-label-input", value: value, placeholder: defaultLabel, autofocus: true, isSmall: true, maxlength: MAX_LABEL_LENGTH, accessibilityConfig: { ariaLabel: intl.formatMessage(messages["renameTotalLabel"]) }, onChange: (newValue) => setValue(String(newValue)), onEnterKeyPress: save, onEscKeyPress: onCancel }), _jsxs("div", { className: "gd-pivot-table-rename-total-popover-buttons", children: [_jsx(Button, { className: "gd-button-link s-rename-total-reset", value: intl.formatMessage(messages["resetTotalLabel"]), onClick: () => onSave(undefined) }), _jsx(Button, { className: "gd-button-secondary s-rename-total-cancel", value: intl.formatMessage(messages["cancel"]), onClick: onCancel }), _jsx(Button, { className: "gd-button-action s-rename-total-save", value: intl.formatMessage(messages["save"]), onClick: save })] })] }) }));
19
+ }
@@ -0,0 +1,10 @@
1
+ import { type IntlShape } from "react-intl";
2
+ export interface ITotalActionMenuProps {
3
+ intl: IntlShape;
4
+ anchor: HTMLElement;
5
+ onRenameClick: () => void;
6
+ onResetClick: () => void;
7
+ onClose: () => void;
8
+ }
9
+ export declare function TotalActionMenu({ intl, anchor, onRenameClick, onResetClick, onClose, }: ITotalActionMenuProps): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=TotalActionMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TotalActionMenu.d.ts","sourceRoot":"","sources":["../../../../src/next/components/TotalLabel/TotalActionMenu.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAKD,wBAAgB,eAAe,CAAC,EAC5B,IAAI,EACJ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,OAAO,GACV,EAAE,qBAAqB,2CAsDvB"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2025-2026 GoodData Corporation
3
+ import { useId, useMemo } from "react";
4
+ import { Overlay, UiMenu } from "@gooddata/sdk-ui-kit";
5
+ import { messages } from "../../../locales.js";
6
+ export function TotalActionMenu({ intl, anchor, onRenameClick, onResetClick, onClose, }) {
7
+ const menuId = useId();
8
+ const items = useMemo(() => [
9
+ {
10
+ type: "interactive",
11
+ id: "rename",
12
+ stringTitle: intl.formatMessage(messages["renameTotalLabel"]),
13
+ data: "rename",
14
+ },
15
+ {
16
+ type: "interactive",
17
+ id: "reset",
18
+ stringTitle: intl.formatMessage(messages["resetTotalLabel"]),
19
+ data: "reset",
20
+ },
21
+ ], [intl]);
22
+ return (_jsx(Overlay, { alignTo: anchor, alignPoints: [{ align: "bl tl" }, { align: "tl bl" }, { align: "br tr" }], closeOnOutsideClick: true, closeOnParentScroll: true, closeOnEscape: true, closeOnMouseDrag: true, onClose: onClose, children: _jsx(UiMenu, { items: items, onSelect: (item) => {
23
+ if (item.data === "rename") {
24
+ onRenameClick();
25
+ }
26
+ else {
27
+ onResetClick();
28
+ }
29
+ }, onClose: onClose, shouldCloseOnSelect: false, size: "small", dataTestId: "pivot-table-total-action-menu", itemDataTestId: (item) => item.type === "interactive" ? `total-action-${item.data}` : undefined, ariaAttributes: {
30
+ id: menuId,
31
+ "aria-label": intl.formatMessage(messages["totalLabelActions"]),
32
+ } }) }));
33
+ }
@@ -13,7 +13,7 @@ export declare function PivotTablePropsProvider({ children, ...props }: ICorePiv
13
13
  type WithRequired<T, K extends keyof T> = T & Required<Pick<T, K>>;
14
14
  type ColumnSizingDefaults = "columnWidths";
15
15
  type ColumnSizingWithDefaults = WithRequired<IColumnSizing, ColumnSizingDefaults>;
16
- type MenuDefaults = "aggregations" | "aggregationsSubMenu" | "aggregationsSubMenuForRows" | "aggregationTypes";
16
+ type MenuDefaults = "aggregations" | "aggregationsSubMenu" | "aggregationsSubMenuForRows" | "aggregationTypes" | "totalLabelsEditable";
17
17
  type MenuWithDefaults = WithRequired<IMenu, MenuDefaults>;
18
18
  type ConfigDefaults = "measureGroupDimension" | "columnHeadersPosition" | "columnSizing" | "textWrapping" | "menu";
19
19
  type ConfigWithDefaults = WithRequired<PivotTableNextConfigWithConditionalFormatting, ConfigDefaults> & {
@@ -1 +1 @@
1
- {"version":3,"file":"PivotTablePropsContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/PivotTablePropsContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgB5D,OAAO,EAAE,KAAK,6CAA6C,EAAE,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,QAAQ,EACR,GAAG,KAAK,EACX,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAEpD;AAED,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG,cAAc,CAAC;AAE3C,KAAK,wBAAwB,GAAG,YAAY,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;AAElF,KAAK,YAAY,GACX,cAAc,GACd,qBAAqB,GACrB,4BAA4B,GAC5B,kBAAkB,CAAC;AAEzB,KAAK,gBAAgB,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;AAE1D,KAAK,cAAc,GACb,uBAAuB,GACvB,uBAAuB,GACvB,cAAc,GACd,cAAc,GACd,MAAM,CAAC;AAEb,KAAK,kBAAkB,GAAG,YAAY,CAAC,6CAA6C,EAAE,cAAc,CAAC,GAAG;IACpG,YAAY,EAAE,wBAAwB,CAAC;IACvC,IAAI,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,iBAAiB,GAChB,MAAM,GACN,SAAS,GACT,UAAU,GACV,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,gBAAgB,CAAC;AAEvB,KAAK,qBAAqB,GAAG,YAAY,CAAC,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;AAEvF,KAAK,2BAA2B,GAAG,qBAAqB,GAAG;IACvD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,wBAAwB,GAAG,2BAA2B,CAmCxG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,2BAA2B,CAgChE"}
1
+ {"version":3,"file":"PivotTablePropsContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/PivotTablePropsContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgB5D,OAAO,EAAE,KAAK,6CAA6C,EAAE,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,QAAQ,EACR,GAAG,KAAK,EACX,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAEpD;AAED,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG,cAAc,CAAC;AAE3C,KAAK,wBAAwB,GAAG,YAAY,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;AAElF,KAAK,YAAY,GACX,cAAc,GACd,qBAAqB,GACrB,4BAA4B,GAC5B,kBAAkB,GAClB,qBAAqB,CAAC;AAE5B,KAAK,gBAAgB,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;AAE1D,KAAK,cAAc,GACb,uBAAuB,GACvB,uBAAuB,GACvB,cAAc,GACd,cAAc,GACd,MAAM,CAAC;AAEb,KAAK,kBAAkB,GAAG,YAAY,CAAC,6CAA6C,EAAE,cAAc,CAAC,GAAG;IACpG,YAAY,EAAE,wBAAwB,CAAC;IACvC,IAAI,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,iBAAiB,GAChB,MAAM,GACN,SAAS,GACT,UAAU,GACV,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,gBAAgB,CAAC;AAEvB,KAAK,qBAAqB,GAAG,YAAY,CAAC,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;AAEvF,KAAK,2BAA2B,GAAG,qBAAqB,GAAG;IACvD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,wBAAwB,GAAG,2BAA2B,CAoCxG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,2BAA2B,CAgChE"}
@@ -43,6 +43,7 @@ export function applyPivotTableDefaultProps(props) {
43
43
  aggregationsSubMenu: props.config?.menu?.aggregationsSubMenu ?? false,
44
44
  aggregationsSubMenuForRows: props.config?.menu?.aggregationsSubMenuForRows ?? false,
45
45
  aggregationTypes: props.config?.menu?.aggregationTypes ?? DEFAULT_TOTAL_FUNCTIONS,
46
+ totalLabelsEditable: props.config?.menu?.totalLabelsEditable ?? false,
46
47
  },
47
48
  },
48
49
  execConfig: props.execConfig ?? EMPTY_OBJECT,
@@ -0,0 +1,13 @@
1
+ import { type ReactNode } from "react";
2
+ import { type IOpenTotalLabelMenuRequest, type ITotalLabelTarget } from "../features/aggregations/totalLabelTarget.js";
3
+ interface ITotalLabelContextValue {
4
+ enabled: boolean;
5
+ openTotalLabelMenu: (request: IOpenTotalLabelMenuRequest) => void;
6
+ getCustomTotalLabel: (target: ITotalLabelTarget | undefined) => string | undefined;
7
+ }
8
+ export declare function TotalLabelProvider({ children }: {
9
+ children: ReactNode;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ export declare function useTotalLabelContext(): ITotalLabelContextValue;
12
+ export {};
13
+ //# sourceMappingURL=TotalLabelContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TotalLabelContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/TotalLabelContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAwE,MAAM,OAAO,CAAC;AAS7G,OAAO,EACH,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,EAEzB,MAAM,8CAA8C,CAAC;AAYtD,UAAU,uBAAuB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAClE,mBAAmB,EAAE,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;CACtF;AAID,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAiHvE;AAED,wBAAgB,oBAAoB,IAAI,uBAAuB,CAO9D"}
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
4
+ import { useIntl } from "react-intl";
5
+ import { bucketsFind } from "@gooddata/sdk-model";
6
+ import { totalTypeMessages } from "../../locales.js";
7
+ import { RenameTotalLabelPopover } from "../components/TotalLabel/RenameTotalLabelPopover.js";
8
+ import { TotalActionMenu } from "../components/TotalLabel/TotalActionMenu.js";
9
+ import { getTotalAliasForTarget, } from "../features/aggregations/totalLabelTarget.js";
10
+ import { updateTotalAlias } from "../features/aggregations/totals.js";
11
+ import { useCurrentDataView } from "./CurrentDataViewContext.js";
12
+ import { usePivotTableProps } from "./PivotTablePropsContext.js";
13
+ const TotalLabelContext = createContext(undefined);
14
+ export function TotalLabelProvider({ children }) {
15
+ const intl = useIntl();
16
+ const { config, execution, pushData } = usePivotTableProps();
17
+ const { currentDataView } = useCurrentDataView();
18
+ const [menuState, setMenuState] = useState(null);
19
+ const enabled = config.menu.totalLabelsEditable === true;
20
+ const getTotalsForWrite = useCallback((bucketType) => bucketsFind(execution.definition.buckets, bucketType)?.totals ?? [], [execution]);
21
+ const getCustomTotalLabel = useCallback((target) => {
22
+ if (!target) {
23
+ return undefined;
24
+ }
25
+ return getTotalAliasForTarget(currentDataView?.definition.buckets ?? [], target);
26
+ }, [currentDataView]);
27
+ const openTotalLabelMenu = useCallback((request) => {
28
+ if (!enabled) {
29
+ return;
30
+ }
31
+ const currentLabel = getCustomTotalLabel(request) ?? "";
32
+ setMenuState({
33
+ ...request,
34
+ currentLabel,
35
+ defaultLabel: intl.formatMessage(totalTypeMessages[request.type]),
36
+ mode: "menu",
37
+ });
38
+ }, [enabled, getCustomTotalLabel, intl]);
39
+ const closeMenu = useCallback(() => {
40
+ const anchor = menuState?.anchor;
41
+ setMenuState(null);
42
+ anchor?.focus();
43
+ }, [menuState?.anchor]);
44
+ useEffect(() => {
45
+ if (!enabled && menuState) {
46
+ closeMenu();
47
+ }
48
+ }, [enabled, menuState, closeMenu]);
49
+ const saveLabel = useCallback((newLabel) => {
50
+ if (!menuState || !pushData || !enabled) {
51
+ closeMenu();
52
+ return;
53
+ }
54
+ const updatedTotals = updateTotalAlias(getTotalsForWrite(menuState.bucketType), menuState.type, menuState.attributeIdentifier, newLabel, menuState.measureIdentifier);
55
+ if (updatedTotals) {
56
+ pushData({
57
+ properties: {
58
+ totals: updatedTotals,
59
+ bucketType: menuState.bucketType,
60
+ },
61
+ });
62
+ }
63
+ closeMenu();
64
+ }, [closeMenu, enabled, getTotalsForWrite, menuState, pushData]);
65
+ const contextValue = useMemo(() => ({ enabled, openTotalLabelMenu, getCustomTotalLabel }), [enabled, getCustomTotalLabel, openTotalLabelMenu]);
66
+ return (_jsxs(TotalLabelContext.Provider, { value: contextValue, children: [children, menuState?.mode === "menu" ? (_jsx(TotalActionMenu, { intl: intl, anchor: menuState.anchor, onRenameClick: () => setMenuState((current) => (current ? { ...current, mode: "popover" } : null)), onResetClick: () => saveLabel(undefined), onClose: closeMenu })) : null, menuState?.mode === "popover" ? (_jsx(RenameTotalLabelPopover, { intl: intl, anchor: menuState.anchor, currentLabel: menuState.currentLabel, defaultLabel: menuState.defaultLabel, onSave: saveLabel, onCancel: closeMenu })) : null] }));
67
+ }
68
+ export function useTotalLabelContext() {
69
+ const context = useContext(TotalLabelContext);
70
+ if (!context) {
71
+ throw new Error("useTotalLabelContext must be used within TotalLabelProvider");
72
+ }
73
+ return context;
74
+ }
@@ -0,0 +1,35 @@
1
+ import { type IBucket, type TotalType } from "@gooddata/sdk-model";
2
+ import { type DataViewFacade, type ITableColumnDefinition, type ITableDataValue } from "@gooddata/sdk-ui";
3
+ import { type AgGridRowData } from "../../types/internal.js";
4
+ import { type TotalLabelBucketType } from "./totals.js";
5
+ export interface ITotalLabelTarget {
6
+ type: TotalType;
7
+ attributeIdentifier: string;
8
+ bucketType: TotalLabelBucketType;
9
+ measureIdentifier?: string;
10
+ }
11
+ export interface IOpenTotalLabelMenuRequest extends ITotalLabelTarget {
12
+ anchor: HTMLElement;
13
+ }
14
+ /**
15
+ * Resolves the total definition represented by a total/grand-total label cell (e.g. the "Sum A" or
16
+ * "RowSum Σ" cell, not a numeric value cell). Only grand-total and subtotal rows ever produce these
17
+ * label cells - every "totalHeader"/"grandTotalHeader" cell sdk-ui emits comes from a
18
+ * grandTotalRow/subtotalRow mapper, so its rowDefinition is always a total row. Total/subtotal
19
+ * *column* headers (not cell data) are resolved separately by resolveTotalLabelTargetFromColumnDefinition.
20
+ */
21
+ export declare function resolveTotalLabelTargetFromTotalLabelCellData(cellData: ITableDataValue | undefined): ITotalLabelTarget | undefined;
22
+ export declare function isFirstTotalHeaderAttributeCell(rowData: AgGridRowData | undefined, colId: string): boolean;
23
+ /**
24
+ * Resolves the total definition represented by a total or subtotal column.
25
+ */
26
+ export declare function resolveTotalLabelTargetFromColumnDefinition(columnDefinition: ITableColumnDefinition | undefined): ITotalLabelTarget | undefined;
27
+ export declare function getTotalAliasForTarget(buckets: IBucket[], target: ITotalLabelTarget): string | undefined;
28
+ /**
29
+ * Resolves the custom label (alias) for the total a cell represents, if it has one. A single source
30
+ * of truth for both applying the alias to a cell's formatted value and for later deciding whether
31
+ * that formatted value is already-final custom text that must not be run through total-type
32
+ * translation (an alias that happens to equal a raw type key, e.g. "sum", must still display verbatim).
33
+ */
34
+ export declare function resolveCustomTotalLabel(cellData: ITableDataValue | undefined, dataView: DataViewFacade): string | undefined;
35
+ //# sourceMappingURL=totalLabelTarget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totalLabelTarget.d.ts","sourceRoot":"","sources":["../../../../src/next/features/aggregations/totalLabelTarget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAEH,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAYvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,KAAK,oBAAoB,EAAqC,MAAM,aAAa,CAAC;AAE3F,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,oBAAoB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA2B,SAAQ,iBAAiB;IACjE,MAAM,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,6CAA6C,CACzD,QAAQ,EAAE,eAAe,GAAG,SAAS,GACtC,iBAAiB,GAAG,SAAS,CAuC/B;AAaD,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB1G;AAED;;GAEG;AACH,wBAAgB,2CAA2C,CACvD,gBAAgB,EAAE,sBAAsB,GAAG,SAAS,GACrD,iBAAiB,GAAG,SAAS,CAwB/B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAGxG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,QAAQ,EAAE,cAAc,GACzB,MAAM,GAAG,SAAS,CAOpB"}