@dhis2/analytics 24.10.12 → 24.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [24.11.0](https://github.com/dhis2/analytics/compare/v24.10.13...v24.11.0) (2024-11-04)
2
+
3
+
4
+ ### Features
5
+
6
+ * **pivot-table:** truncate title and show full in tooltip ([#1579](https://github.com/dhis2/analytics/issues/1579)) ([#1622](https://github.com/dhis2/analytics/issues/1622)) ([f62b0c2](https://github.com/dhis2/analytics/commit/f62b0c21b76ac4139d451567ff07108160428996))
7
+
8
+ ## [24.10.13](https://github.com/dhis2/analytics/compare/v24.10.12...v24.10.13) (2024-06-27)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * apply legend to all numeric and boolean types (DHIS2-17611) [24.x] ([#1687](https://github.com/dhis2/analytics/issues/1687)) ([9e3f166](https://github.com/dhis2/analytics/commit/9e3f1668aaef693393e5a7a46dc839f9aad5bce5))
14
+
1
15
  ## [24.10.12](https://github.com/dhis2/analytics/compare/v24.10.11...v24.10.12) (2024-06-25)
2
16
 
3
17
 
@@ -1170,4 +1170,42 @@ ResizingPivotTable.propTypes = {
1170
1170
  data: degsData,
1171
1171
  visualization: visualization
1172
1172
  }));
1173
+ });
1174
+ (0, _react.storiesOf)('PivotTable', module).add('Truncated header cell', (_, _ref50) => {
1175
+ let {
1176
+ pivotTableOptions
1177
+ } = _ref50;
1178
+ const widths = [250, 200, 500];
1179
+ const [width, setWidth] = (0, _react2.useState)(250);
1180
+
1181
+ const toggleWidth = () => setWidth(currentWidth => {
1182
+ var _widths;
1183
+
1184
+ return (_widths = widths[widths.indexOf(currentWidth) + 1]) !== null && _widths !== void 0 ? _widths : widths[0];
1185
+ });
1186
+
1187
+ const visualization = { ..._narrativeVisualization.default,
1188
+ ...visualizationReset,
1189
+ ...pivotTableOptions,
1190
+ columns: _narrativeVisualization.default.filters,
1191
+ filters: _narrativeVisualization.default.columns,
1192
+ rowTotals: true,
1193
+ colTotals: true
1194
+ };
1195
+ const data = { ...narrativeData,
1196
+ rows: [narrativeData.rows[0]]
1197
+ };
1198
+ return /*#__PURE__*/_react2.default.createElement("div", {
1199
+ style: {
1200
+ width,
1201
+ height: 600,
1202
+ marginTop: 50,
1203
+ transition: 'width 1s'
1204
+ }
1205
+ }, /*#__PURE__*/_react2.default.createElement("button", {
1206
+ onClick: toggleWidth
1207
+ }, "Toggle width"), /*#__PURE__*/_react2.default.createElement(_index.PivotTable, {
1208
+ data: data,
1209
+ visualization: visualization
1210
+ }));
1173
1211
  });
@@ -7,6 +7,8 @@ exports.PivotTableTitleRow = void 0;
7
7
 
8
8
  var _style = _interopRequireDefault(require("styled-jsx/style"));
9
9
 
10
+ var _ui = require("@dhis2/ui");
11
+
10
12
  var _propTypes = _interopRequireDefault(require("prop-types"));
11
13
 
12
14
  var _react = _interopRequireWildcard(require("react"));
@@ -24,35 +26,73 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
24
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
27
 
26
28
  const PivotTableTitleRow = _ref => {
29
+ var _scrollPosition$x;
30
+
27
31
  let {
28
32
  title,
29
33
  scrollPosition,
30
- containerWidth,
31
- totalWidth
34
+ containerWidth
32
35
  } = _ref;
36
+ const containerRef = (0, _react.useRef)(null);
37
+ const [scrollWidth, setScrollWidth] = (0, _react.useState)(0);
38
+ const [isTitleTruncated, setIsTitleTruncated] = (0, _react.useState)(false);
33
39
  const engine = (0, _PivotTableEngineContext.usePivotTableEngine)();
34
40
  const columnCount = engine.width + engine.rowDepth;
35
- const [position, setPosition] = (0, _react.useState)(scrollPosition.x);
41
+ const maxWidth = containerWidth - (engine.cellPadding * 2 + 2);
42
+ const marginLeft = Math.max(0, (_scrollPosition$x = scrollPosition === null || scrollPosition === void 0 ? void 0 : scrollPosition.x) !== null && _scrollPosition$x !== void 0 ? _scrollPosition$x : 0);
36
43
  (0, _react.useEffect)(() => {
37
- setPosition(Math.max(0, Math.min(scrollPosition.x, totalWidth - containerWidth)));
38
- }, [containerWidth, scrollPosition.x, totalWidth]);
44
+ if (containerRef.current) {
45
+ /* Only set `scrollWidth` once, because during a CSS transition
46
+ * the reported value can sometimes be equal to `clientWidth`
47
+ * even though the text doesn't fit. Due to `white-space: nowrap`
48
+ * and `overflow: hidden` the `scrollWidth` should remain constant,
49
+ * so we can assume this initial value is correct. */
50
+ if (!scrollWidth) {
51
+ setScrollWidth(containerRef.current.scrollWidth);
52
+ }
53
+
54
+ const currentScrollWidth = scrollWidth !== null && scrollWidth !== void 0 ? scrollWidth : containerRef.current.scrollWidth;
55
+ const newIsTitleTruncated = currentScrollWidth > containerRef.current.clientWidth;
56
+
57
+ if (newIsTitleTruncated !== isTitleTruncated) {
58
+ setIsTitleTruncated(newIsTitleTruncated);
59
+ }
60
+ }
61
+ }, [containerWidth, scrollWidth, isTitleTruncated]);
39
62
  return /*#__PURE__*/_react.default.createElement("tr", {
40
63
  className: "jsx-".concat(_PivotTableStyle.cell.__hash)
41
64
  }, /*#__PURE__*/_react.default.createElement(_style.default, {
42
65
  id: _PivotTableStyle.cell.__hash
43
66
  }, _PivotTableStyle.cell), /*#__PURE__*/_react.default.createElement(_PivotTableCell.PivotTableCell, {
44
67
  isHeader: true,
45
- classes: ['column-header', 'title'],
68
+ classes: ['column-header', 'title-cell'],
46
69
  colSpan: columnCount
47
70
  }, /*#__PURE__*/_react.default.createElement("div", {
48
71
  style: {
49
- marginLeft: position,
50
- maxWidth: containerWidth,
51
- textAlign: 'center'
72
+ marginLeft,
73
+ maxWidth
52
74
  },
75
+ ref: containerRef,
53
76
  "data-test": "visualization-title",
54
- className: "jsx-".concat(_PivotTableStyle.cell.__hash)
55
- }, title)));
77
+ className: "jsx-".concat(_PivotTableStyle.cell.__hash) + " " + "title-cell-content"
78
+ }, isTitleTruncated ? /*#__PURE__*/_react.default.createElement(_ui.Tooltip, {
79
+ content: title
80
+ }, _ref2 => {
81
+ let {
82
+ ref: tooltipRef,
83
+ onMouseOver,
84
+ onMouseOut
85
+ } = _ref2;
86
+ return /*#__PURE__*/_react.default.createElement("div", {
87
+ ref: tooltipRef,
88
+ onMouseOver: onMouseOver,
89
+ onMouseOut: onMouseOut,
90
+ style: {
91
+ maxWidth
92
+ },
93
+ className: "jsx-".concat(_PivotTableStyle.cell.__hash) + " " + "title-cell-content"
94
+ }, title);
95
+ }) : title)));
56
96
  };
57
97
 
58
98
  exports.PivotTableTitleRow = PivotTableTitleRow;
@@ -61,6 +101,5 @@ PivotTableTitleRow.propTypes = {
61
101
  scrollPosition: _propTypes.default.shape({
62
102
  x: _propTypes.default.number.isRequired
63
103
  }).isRequired,
64
- title: _propTypes.default.string.isRequired,
65
- totalWidth: _propTypes.default.number.isRequired
104
+ title: _propTypes.default.string.isRequired
66
105
  };
@@ -28,18 +28,15 @@ const PivotTableTitleRows = _ref => {
28
28
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, engine.options.title ? /*#__PURE__*/_react.default.createElement(_PivotTableTitleRow.PivotTableTitleRow, {
29
29
  title: engine.options.title,
30
30
  scrollPosition: clippingResult.scrollPosition,
31
- containerWidth: width,
32
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
31
+ containerWidth: width
33
32
  }) : null, engine.options.subtitle ? /*#__PURE__*/_react.default.createElement(_PivotTableTitleRow.PivotTableTitleRow, {
34
33
  title: engine.options.subtitle,
35
34
  scrollPosition: clippingResult.scrollPosition,
36
- containerWidth: width,
37
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
35
+ containerWidth: width
38
36
  }) : null, (_engine$visualization = engine.visualization.filters) !== null && _engine$visualization !== void 0 && _engine$visualization.length ? /*#__PURE__*/_react.default.createElement(_PivotTableTitleRow.PivotTableTitleRow, {
39
37
  title: (0, _getFilterText.default)(engine.visualization.filters, engine.rawData.metaData),
40
38
  scrollPosition: clippingResult.scrollPosition,
41
- containerWidth: width,
42
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
39
+ containerWidth: width
43
40
  }) : null);
44
41
  };
45
42
 
@@ -56,10 +56,9 @@ const PivotTableValueCell = _ref => {
56
56
  ref: cellRef,
57
57
  classes: [cellContent.cellType, isClickable && 'clickable']
58
58
  });
59
- } // TODO: Add support for 'INTEGER' type (requires server changes)
59
+ }
60
60
 
61
-
62
- const legendStyle = cellContent.cellType === _pivotTableConstants.CELL_TYPE_VALUE && cellContent.valueType === _valueTypes.VALUE_TYPE_NUMBER ? (0, _applyLegendSet.applyLegendSet)(cellContent.rawValue, cellContent.dxDimension, engine) : undefined;
61
+ const legendStyle = cellContent.cellType === _pivotTableConstants.CELL_TYPE_VALUE && ((0, _valueTypes.isNumericValueType)(cellContent.valueType) || (0, _valueTypes.isBooleanValueType)(cellContent.valueType)) ? (0, _applyLegendSet.applyLegendSet)(cellContent.rawValue, cellContent.dxDimension, engine) : undefined;
63
62
  const width = engine.adaptiveClippingController.columns.sizes[engine.columnMap[column]].size;
64
63
  const height = engine.adaptiveClippingController.rows.sizes[engine.rowMap[row]].size;
65
64
  const style = { ...legendStyle,
@@ -12,9 +12,9 @@ var _pivotTableConstants = require("../../../modules/pivotTable/pivotTableConsta
12
12
  const table = ["div.pivot-table-container.jsx-3572446209{font-family:'Roboto',Arial,sans-serif;overflow:auto;color:".concat(_ui.colors.grey900, ";}"), "div.pivot-table-container.jsx-3572446209 table{border-spacing:0;white-space:nowrap;box-sizing:border-box;text-align:center;border:1px solid ".concat(_pivotTableConstants.BORDER_COLOR, ";border-width:1px 1px 0 0;}"), "div.pivot-table-container.jsx-3572446209 table.fixed-headers{border-width:0 0 0 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-headers tr td{border-width:0 1px 1px 0;}", "div.pivot-table-container.jsx-3572446209 table.fixed-column-headers{border-width:0 1px 0 0;}", "div.pivot-table-container.jsx-3572446209 table.fixed-column-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-column-headers tr td{border-width:0 0 1px 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-headers thead tr:first-of-type th,div.pivot-table-container.jsx-3572446209 table.fixed-column-headers thead tr:first-of-type th{border-top:1px solid ".concat(_pivotTableConstants.BORDER_COLOR, ";}"), "div.pivot-table-container.jsx-3572446209 table.fixed-row-headers{border-width:0 0 1px 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-row-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-row-headers tr td{border-width:1px 1px 0 0;}"];
13
13
  exports.table = table;
14
14
  table.__hash = "3572446209";
15
- const cell = ["td.jsx-2757248239,th.jsx-2757248239{box-sizing:border-box;font-weight:normal;overflow:hidden;text-overflow:ellipsis;border:1px solid ".concat(_pivotTableConstants.BORDER_COLOR, ";border-width:0 0 1px 1px;cursor:default;}"), "th.fixed-header.jsx-2757248239{position:-webkit-sticky;position:sticky;z-index:1;top:0;left:0;}", ".fontsize-SMALL.jsx-2757248239{font-size:".concat(_pivotTableConstants.FONT_SIZE_SMALL, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_SMALL, "px;}"), ".fontsize-NORMAL.jsx-2757248239{font-size:".concat(_pivotTableConstants.FONT_SIZE_NORMAL, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_NORMAL, "px;}"), ".fontsize-LARGE.jsx-2757248239{font-size:".concat(_pivotTableConstants.FONT_SIZE_LARGE, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_LARGE, "px;}"), ".displaydensity-COMPACT.jsx-2757248239{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".displaydensity-NORMAL.jsx-2757248239{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".displaydensity-COMFORTABLE.jsx-2757248239{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".column-header.jsx-2757248239{background-color:#dae6f8;}", "div.column-header-inner.jsx-2757248239{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}", ".title.jsx-2757248239{font-weight:bold;background-color:#cddaed;}", ".row-header.jsx-2757248239{background-color:#dae6f8;}", ".row-header-hierarchy.jsx-2757248239{text-align:left;}", ".empty-header.jsx-2757248239{background-color:#cddaed;}", ".total-header.jsx-2757248239{background-color:#bac6d8;}", ".value.jsx-2757248239{background-color:#ffffff;text-align:left;}", ".NUMBER.jsx-2757248239,.INTEGER.jsx-2757248239,.INTEGER_POSITIVE.jsx-2757248239,.INTEGER_NEGATIVE.jsx-2757248239,.INTEGER_ZERO_OR_POSITIVE.jsx-2757248239,.UNIT_INTERVAL.jsx-2757248239,.PERCENTAGE.jsx-2757248239,.BOOLEAN.jsx-2757248239,.TRUE_ONLY.jsx-2757248239{text-align:right;}", ".clickable.jsx-2757248239{cursor:pointer;}", ".value.jsx-2757248239:hover{background-color:#f3f3f3;}", ".subtotal.jsx-2757248239{background-color:#f4f4f4;}", ".total.jsx-2757248239{background-color:#d8d8d8;}", ".column-header-label.jsx-2757248239{overflow:hidden;text-overflow:ellipsis;}"];
15
+ const cell = ["td.jsx-3234356038,th.jsx-3234356038{box-sizing:border-box;font-weight:normal;overflow:hidden;text-overflow:ellipsis;border:1px solid ".concat(_pivotTableConstants.BORDER_COLOR, ";border-width:0 0 1px 1px;cursor:default;}"), "th.fixed-header.jsx-3234356038{position:-webkit-sticky;position:sticky;z-index:1;top:0;left:0;}", ".fontsize-SMALL.jsx-3234356038{font-size:".concat(_pivotTableConstants.FONT_SIZE_SMALL, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_SMALL, "px;}"), ".fontsize-NORMAL.jsx-3234356038{font-size:".concat(_pivotTableConstants.FONT_SIZE_NORMAL, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_NORMAL, "px;}"), ".fontsize-LARGE.jsx-3234356038{font-size:".concat(_pivotTableConstants.FONT_SIZE_LARGE, "px;line-height:").concat(_pivotTableConstants.FONT_SIZE_LARGE, "px;}"), ".displaydensity-COMPACT.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".displaydensity-NORMAL.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".displaydensity-COMFORTABLE.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".column-header.jsx-3234356038{background-color:#dae6f8;}", "div.column-header-inner.jsx-3234356038{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}", ".title-cell.jsx-3234356038{font-weight:bold;background-color:#cddaed;padding:0;}", ".title-cell-content.jsx-3234356038{text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}", ".title-cell.displaydensity-COMPACT.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".title-cell.displaydensity-NORMAL.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".title-cell.displaydensity-COMFORTABLE.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(_pivotTableConstants.DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".row-header.jsx-3234356038{background-color:#dae6f8;}", ".row-header-hierarchy.jsx-3234356038{text-align:left;}", ".empty-header.jsx-3234356038{background-color:#cddaed;}", ".total-header.jsx-3234356038{background-color:#bac6d8;}", ".value.jsx-3234356038{background-color:#ffffff;text-align:left;}", ".NUMBER.jsx-3234356038,.INTEGER.jsx-3234356038,.INTEGER_POSITIVE.jsx-3234356038,.INTEGER_NEGATIVE.jsx-3234356038,.INTEGER_ZERO_OR_POSITIVE.jsx-3234356038,.UNIT_INTERVAL.jsx-3234356038,.PERCENTAGE.jsx-3234356038,.BOOLEAN.jsx-3234356038,.TRUE_ONLY.jsx-3234356038{text-align:right;}", ".clickable.jsx-3234356038{cursor:pointer;}", ".value.jsx-3234356038:hover{background-color:#f3f3f3;}", ".subtotal.jsx-3234356038{background-color:#f4f4f4;}", ".total.jsx-3234356038{background-color:#d8d8d8;}", ".column-header-label.jsx-3234356038{overflow:hidden;text-overflow:ellipsis;}"];
16
16
  exports.cell = cell;
17
- cell.__hash = "2757248239";
17
+ cell.__hash = "3234356038";
18
18
  const sortIcon = [".fontsize-SMALL.jsx-2877616992{height:".concat(_pivotTableConstants.FONT_SIZE_SMALL, "px;margin-bottom:1px;margin-left:5px;}"), ".fontsize-NORMAL.jsx-2877616992{height:".concat(_pivotTableConstants.FONT_SIZE_NORMAL, "px;max-height:11px;margin-bottom:2px;margin-left:6px;}"), ".fontsize-LARGE.jsx-2877616992{height:".concat(_pivotTableConstants.FONT_SIZE_LARGE, "px;margin-bottom:2px;margin-left:7px;}")];
19
19
  exports.sortIcon = sortIcon;
20
20
  sortIcon.__hash = "2877616992";
@@ -63,7 +63,7 @@ const toFixedPrecisionString = (value, skipRounding) => {
63
63
  };
64
64
 
65
65
  const renderValue = (value, valueType, visualization) => {
66
- if (!(0, _valueTypes.isNumericValueType)(valueType) || value === undefined) {
66
+ if (!((0, _valueTypes.isNumericValueType)(valueType) || (0, _valueTypes.isBooleanValueType)(valueType)) || value === undefined) {
67
67
  return String(value).replace(/[^\S\n]+/, ' ');
68
68
  }
69
69
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isNumericValueType = exports.VALUE_TYPE_USERNAME = exports.VALUE_TYPE_URL = exports.VALUE_TYPE_UNIT_INTERVAL = exports.VALUE_TYPE_TRUE_ONLY = exports.VALUE_TYPE_TIME = exports.VALUE_TYPE_TEXT = exports.VALUE_TYPE_PHONE_NUMBER = exports.VALUE_TYPE_PERCENTAGE = exports.VALUE_TYPE_ORGANISATION_UNIT = exports.VALUE_TYPE_NUMBER = exports.VALUE_TYPE_LONG_TEXT = exports.VALUE_TYPE_LETTER = exports.VALUE_TYPE_INTEGER_ZERO_OR_POSITIVE = exports.VALUE_TYPE_INTEGER_POSITIVE = exports.VALUE_TYPE_INTEGER_NEGATIVE = exports.VALUE_TYPE_INTEGER = exports.VALUE_TYPE_EMAIL = exports.VALUE_TYPE_DATETIME = exports.VALUE_TYPE_DATE = exports.VALUE_TYPE_BOOLEAN = exports.VALUE_TYPE_AGE = void 0;
6
+ exports.isNumericValueType = exports.isBooleanValueType = exports.VALUE_TYPE_USERNAME = exports.VALUE_TYPE_URL = exports.VALUE_TYPE_UNIT_INTERVAL = exports.VALUE_TYPE_TRUE_ONLY = exports.VALUE_TYPE_TIME = exports.VALUE_TYPE_TEXT = exports.VALUE_TYPE_PHONE_NUMBER = exports.VALUE_TYPE_PERCENTAGE = exports.VALUE_TYPE_ORGANISATION_UNIT = exports.VALUE_TYPE_NUMBER = exports.VALUE_TYPE_LONG_TEXT = exports.VALUE_TYPE_LETTER = exports.VALUE_TYPE_INTEGER_ZERO_OR_POSITIVE = exports.VALUE_TYPE_INTEGER_POSITIVE = exports.VALUE_TYPE_INTEGER_NEGATIVE = exports.VALUE_TYPE_INTEGER = exports.VALUE_TYPE_EMAIL = exports.VALUE_TYPE_DATETIME = exports.VALUE_TYPE_DATE = exports.VALUE_TYPE_BOOLEAN = exports.VALUE_TYPE_AGE = void 0;
7
7
 
8
8
  /* These types match the types in the backend
9
9
  https://github.com/dhis2/dhis2-core/blob/master/dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/ValueType.java
@@ -51,7 +51,12 @@ exports.VALUE_TYPE_ORGANISATION_UNIT = VALUE_TYPE_ORGANISATION_UNIT;
51
51
  const VALUE_TYPE_AGE = 'AGE';
52
52
  exports.VALUE_TYPE_AGE = VALUE_TYPE_AGE;
53
53
  const NUMERIC_VALUE_TYPES = [VALUE_TYPE_NUMBER, VALUE_TYPE_UNIT_INTERVAL, VALUE_TYPE_PERCENTAGE, VALUE_TYPE_INTEGER, VALUE_TYPE_INTEGER_POSITIVE, VALUE_TYPE_INTEGER_NEGATIVE, VALUE_TYPE_INTEGER_ZERO_OR_POSITIVE];
54
+ const BOOLEAN_VALUE_TYPES = [VALUE_TYPE_BOOLEAN, VALUE_TYPE_TRUE_ONLY];
54
55
 
55
56
  const isNumericValueType = type => NUMERIC_VALUE_TYPES.includes(type);
56
57
 
57
- exports.isNumericValueType = isNumericValueType;
58
+ exports.isNumericValueType = isNumericValueType;
59
+
60
+ const isBooleanValueType = type => BOOLEAN_VALUE_TYPES.includes(type);
61
+
62
+ exports.isBooleanValueType = isBooleanValueType;
@@ -1118,4 +1118,42 @@ storiesOf('PivotTable', module).add('DEGS', (_, _ref49) => {
1118
1118
  data: degsData,
1119
1119
  visualization: visualization
1120
1120
  }));
1121
+ });
1122
+ storiesOf('PivotTable', module).add('Truncated header cell', (_, _ref50) => {
1123
+ let {
1124
+ pivotTableOptions
1125
+ } = _ref50;
1126
+ const widths = [250, 200, 500];
1127
+ const [width, setWidth] = useState(250);
1128
+
1129
+ const toggleWidth = () => setWidth(currentWidth => {
1130
+ var _widths;
1131
+
1132
+ return (_widths = widths[widths.indexOf(currentWidth) + 1]) !== null && _widths !== void 0 ? _widths : widths[0];
1133
+ });
1134
+
1135
+ const visualization = { ...narrativeVisualization,
1136
+ ...visualizationReset,
1137
+ ...pivotTableOptions,
1138
+ columns: narrativeVisualization.filters,
1139
+ filters: narrativeVisualization.columns,
1140
+ rowTotals: true,
1141
+ colTotals: true
1142
+ };
1143
+ const data = { ...narrativeData,
1144
+ rows: [narrativeData.rows[0]]
1145
+ };
1146
+ return /*#__PURE__*/React.createElement("div", {
1147
+ style: {
1148
+ width,
1149
+ height: 600,
1150
+ marginTop: 50,
1151
+ transition: 'width 1s'
1152
+ }
1153
+ }, /*#__PURE__*/React.createElement("button", {
1154
+ onClick: toggleWidth
1155
+ }, "Toggle width"), /*#__PURE__*/React.createElement(PivotTable, {
1156
+ data: data,
1157
+ visualization: visualization
1158
+ }));
1121
1159
  });
@@ -1,45 +1,83 @@
1
1
  import _JSXStyle from "styled-jsx/style";
2
+ import { Tooltip } from '@dhis2/ui';
2
3
  import PropTypes from 'prop-types';
3
- import React, { useState, useEffect } from 'react';
4
+ import React, { useRef, useState, useEffect } from 'react';
4
5
  import { PivotTableCell } from './PivotTableCell.js';
5
6
  import { usePivotTableEngine } from './PivotTableEngineContext.js';
6
7
  import { cell as cellStyle } from './styles/PivotTable.style.js';
7
8
  export const PivotTableTitleRow = _ref => {
9
+ var _scrollPosition$x;
10
+
8
11
  let {
9
12
  title,
10
13
  scrollPosition,
11
- containerWidth,
12
- totalWidth
14
+ containerWidth
13
15
  } = _ref;
16
+ const containerRef = useRef(null);
17
+ const [scrollWidth, setScrollWidth] = useState(0);
18
+ const [isTitleTruncated, setIsTitleTruncated] = useState(false);
14
19
  const engine = usePivotTableEngine();
15
20
  const columnCount = engine.width + engine.rowDepth;
16
- const [position, setPosition] = useState(scrollPosition.x);
21
+ const maxWidth = containerWidth - (engine.cellPadding * 2 + 2);
22
+ const marginLeft = Math.max(0, (_scrollPosition$x = scrollPosition === null || scrollPosition === void 0 ? void 0 : scrollPosition.x) !== null && _scrollPosition$x !== void 0 ? _scrollPosition$x : 0);
17
23
  useEffect(() => {
18
- setPosition(Math.max(0, Math.min(scrollPosition.x, totalWidth - containerWidth)));
19
- }, [containerWidth, scrollPosition.x, totalWidth]);
24
+ if (containerRef.current) {
25
+ /* Only set `scrollWidth` once, because during a CSS transition
26
+ * the reported value can sometimes be equal to `clientWidth`
27
+ * even though the text doesn't fit. Due to `white-space: nowrap`
28
+ * and `overflow: hidden` the `scrollWidth` should remain constant,
29
+ * so we can assume this initial value is correct. */
30
+ if (!scrollWidth) {
31
+ setScrollWidth(containerRef.current.scrollWidth);
32
+ }
33
+
34
+ const currentScrollWidth = scrollWidth !== null && scrollWidth !== void 0 ? scrollWidth : containerRef.current.scrollWidth;
35
+ const newIsTitleTruncated = currentScrollWidth > containerRef.current.clientWidth;
36
+
37
+ if (newIsTitleTruncated !== isTitleTruncated) {
38
+ setIsTitleTruncated(newIsTitleTruncated);
39
+ }
40
+ }
41
+ }, [containerWidth, scrollWidth, isTitleTruncated]);
20
42
  return /*#__PURE__*/React.createElement("tr", {
21
43
  className: "jsx-".concat(cellStyle.__hash)
22
44
  }, /*#__PURE__*/React.createElement(_JSXStyle, {
23
45
  id: cellStyle.__hash
24
46
  }, cellStyle), /*#__PURE__*/React.createElement(PivotTableCell, {
25
47
  isHeader: true,
26
- classes: ['column-header', 'title'],
48
+ classes: ['column-header', 'title-cell'],
27
49
  colSpan: columnCount
28
50
  }, /*#__PURE__*/React.createElement("div", {
29
51
  style: {
30
- marginLeft: position,
31
- maxWidth: containerWidth,
32
- textAlign: 'center'
52
+ marginLeft,
53
+ maxWidth
33
54
  },
55
+ ref: containerRef,
34
56
  "data-test": "visualization-title",
35
- className: "jsx-".concat(cellStyle.__hash)
36
- }, title)));
57
+ className: "jsx-".concat(cellStyle.__hash) + " " + "title-cell-content"
58
+ }, isTitleTruncated ? /*#__PURE__*/React.createElement(Tooltip, {
59
+ content: title
60
+ }, _ref2 => {
61
+ let {
62
+ ref: tooltipRef,
63
+ onMouseOver,
64
+ onMouseOut
65
+ } = _ref2;
66
+ return /*#__PURE__*/React.createElement("div", {
67
+ ref: tooltipRef,
68
+ onMouseOver: onMouseOver,
69
+ onMouseOut: onMouseOut,
70
+ style: {
71
+ maxWidth
72
+ },
73
+ className: "jsx-".concat(cellStyle.__hash) + " " + "title-cell-content"
74
+ }, title);
75
+ }) : title)));
37
76
  };
38
77
  PivotTableTitleRow.propTypes = {
39
78
  containerWidth: PropTypes.number.isRequired,
40
79
  scrollPosition: PropTypes.shape({
41
80
  x: PropTypes.number.isRequired
42
81
  }).isRequired,
43
- title: PropTypes.string.isRequired,
44
- totalWidth: PropTypes.number.isRequired
82
+ title: PropTypes.string.isRequired
45
83
  };
@@ -14,18 +14,15 @@ export const PivotTableTitleRows = _ref => {
14
14
  return /*#__PURE__*/React.createElement(React.Fragment, null, engine.options.title ? /*#__PURE__*/React.createElement(PivotTableTitleRow, {
15
15
  title: engine.options.title,
16
16
  scrollPosition: clippingResult.scrollPosition,
17
- containerWidth: width,
18
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
17
+ containerWidth: width
19
18
  }) : null, engine.options.subtitle ? /*#__PURE__*/React.createElement(PivotTableTitleRow, {
20
19
  title: engine.options.subtitle,
21
20
  scrollPosition: clippingResult.scrollPosition,
22
- containerWidth: width,
23
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
21
+ containerWidth: width
24
22
  }) : null, (_engine$visualization = engine.visualization.filters) !== null && _engine$visualization !== void 0 && _engine$visualization.length ? /*#__PURE__*/React.createElement(PivotTableTitleRow, {
25
23
  title: getFilterText(engine.visualization.filters, engine.rawData.metaData),
26
24
  scrollPosition: clippingResult.scrollPosition,
27
- containerWidth: width,
28
- totalWidth: engine.adaptiveClippingController.columns.totalSize + engine.adaptiveClippingController.columns.headerSize
25
+ containerWidth: width
29
26
  }) : null);
30
27
  };
31
28
  PivotTableTitleRows.propTypes = {
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import React, { useRef } from 'react';
3
3
  import { applyLegendSet } from '../../modules/pivotTable/applyLegendSet.js';
4
4
  import { CELL_TYPE_VALUE } from '../../modules/pivotTable/pivotTableConstants.js';
5
- import { VALUE_TYPE_NUMBER } from '../../modules/valueTypes.js';
5
+ import { isNumericValueType, isBooleanValueType } from '../../modules/valueTypes.js';
6
6
  import { PivotTableCell } from './PivotTableCell.js';
7
7
  import { PivotTableEmptyCell } from './PivotTableEmptyCell.js';
8
8
  import { usePivotTableEngine } from './PivotTableEngineContext.js';
@@ -35,10 +35,9 @@ export const PivotTableValueCell = _ref => {
35
35
  ref: cellRef,
36
36
  classes: [cellContent.cellType, isClickable && 'clickable']
37
37
  });
38
- } // TODO: Add support for 'INTEGER' type (requires server changes)
38
+ }
39
39
 
40
-
41
- const legendStyle = cellContent.cellType === CELL_TYPE_VALUE && cellContent.valueType === VALUE_TYPE_NUMBER ? applyLegendSet(cellContent.rawValue, cellContent.dxDimension, engine) : undefined;
40
+ const legendStyle = cellContent.cellType === CELL_TYPE_VALUE && (isNumericValueType(cellContent.valueType) || isBooleanValueType(cellContent.valueType)) ? applyLegendSet(cellContent.rawValue, cellContent.dxDimension, engine) : undefined;
42
41
  const width = engine.adaptiveClippingController.columns.sizes[engine.columnMap[column]].size;
43
42
  const height = engine.adaptiveClippingController.rows.sizes[engine.rowMap[row]].size;
44
43
  const style = { ...legendStyle,
@@ -2,7 +2,7 @@ import { colors } from '@dhis2/ui';
2
2
  import { BORDER_COLOR, DISPLAY_DENSITY_PADDING_COMPACT, DISPLAY_DENSITY_PADDING_NORMAL, DISPLAY_DENSITY_PADDING_COMFORTABLE, FONT_SIZE_SMALL, FONT_SIZE_NORMAL, FONT_SIZE_LARGE } from '../../../modules/pivotTable/pivotTableConstants.js';
3
3
  export const table = ["div.pivot-table-container.jsx-3572446209{font-family:'Roboto',Arial,sans-serif;overflow:auto;color:".concat(colors.grey900, ";}"), "div.pivot-table-container.jsx-3572446209 table{border-spacing:0;white-space:nowrap;box-sizing:border-box;text-align:center;border:1px solid ".concat(BORDER_COLOR, ";border-width:1px 1px 0 0;}"), "div.pivot-table-container.jsx-3572446209 table.fixed-headers{border-width:0 0 0 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-headers tr td{border-width:0 1px 1px 0;}", "div.pivot-table-container.jsx-3572446209 table.fixed-column-headers{border-width:0 1px 0 0;}", "div.pivot-table-container.jsx-3572446209 table.fixed-column-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-column-headers tr td{border-width:0 0 1px 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-headers thead tr:first-of-type th,div.pivot-table-container.jsx-3572446209 table.fixed-column-headers thead tr:first-of-type th{border-top:1px solid ".concat(BORDER_COLOR, ";}"), "div.pivot-table-container.jsx-3572446209 table.fixed-row-headers{border-width:0 0 1px 1px;}", "div.pivot-table-container.jsx-3572446209 table.fixed-row-headers tr th,div.pivot-table-container.jsx-3572446209 table.fixed-row-headers tr td{border-width:1px 1px 0 0;}"];
4
4
  table.__hash = "3572446209";
5
- export const cell = ["td.jsx-2757248239,th.jsx-2757248239{box-sizing:border-box;font-weight:normal;overflow:hidden;text-overflow:ellipsis;border:1px solid ".concat(BORDER_COLOR, ";border-width:0 0 1px 1px;cursor:default;}"), "th.fixed-header.jsx-2757248239{position:-webkit-sticky;position:sticky;z-index:1;top:0;left:0;}", ".fontsize-SMALL.jsx-2757248239{font-size:".concat(FONT_SIZE_SMALL, "px;line-height:").concat(FONT_SIZE_SMALL, "px;}"), ".fontsize-NORMAL.jsx-2757248239{font-size:".concat(FONT_SIZE_NORMAL, "px;line-height:").concat(FONT_SIZE_NORMAL, "px;}"), ".fontsize-LARGE.jsx-2757248239{font-size:".concat(FONT_SIZE_LARGE, "px;line-height:").concat(FONT_SIZE_LARGE, "px;}"), ".displaydensity-COMPACT.jsx-2757248239{padding:".concat(DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".displaydensity-NORMAL.jsx-2757248239{padding:".concat(DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".displaydensity-COMFORTABLE.jsx-2757248239{padding:".concat(DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".column-header.jsx-2757248239{background-color:#dae6f8;}", "div.column-header-inner.jsx-2757248239{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}", ".title.jsx-2757248239{font-weight:bold;background-color:#cddaed;}", ".row-header.jsx-2757248239{background-color:#dae6f8;}", ".row-header-hierarchy.jsx-2757248239{text-align:left;}", ".empty-header.jsx-2757248239{background-color:#cddaed;}", ".total-header.jsx-2757248239{background-color:#bac6d8;}", ".value.jsx-2757248239{background-color:#ffffff;text-align:left;}", ".NUMBER.jsx-2757248239,.INTEGER.jsx-2757248239,.INTEGER_POSITIVE.jsx-2757248239,.INTEGER_NEGATIVE.jsx-2757248239,.INTEGER_ZERO_OR_POSITIVE.jsx-2757248239,.UNIT_INTERVAL.jsx-2757248239,.PERCENTAGE.jsx-2757248239,.BOOLEAN.jsx-2757248239,.TRUE_ONLY.jsx-2757248239{text-align:right;}", ".clickable.jsx-2757248239{cursor:pointer;}", ".value.jsx-2757248239:hover{background-color:#f3f3f3;}", ".subtotal.jsx-2757248239{background-color:#f4f4f4;}", ".total.jsx-2757248239{background-color:#d8d8d8;}", ".column-header-label.jsx-2757248239{overflow:hidden;text-overflow:ellipsis;}"];
6
- cell.__hash = "2757248239";
5
+ export const cell = ["td.jsx-3234356038,th.jsx-3234356038{box-sizing:border-box;font-weight:normal;overflow:hidden;text-overflow:ellipsis;border:1px solid ".concat(BORDER_COLOR, ";border-width:0 0 1px 1px;cursor:default;}"), "th.fixed-header.jsx-3234356038{position:-webkit-sticky;position:sticky;z-index:1;top:0;left:0;}", ".fontsize-SMALL.jsx-3234356038{font-size:".concat(FONT_SIZE_SMALL, "px;line-height:").concat(FONT_SIZE_SMALL, "px;}"), ".fontsize-NORMAL.jsx-3234356038{font-size:".concat(FONT_SIZE_NORMAL, "px;line-height:").concat(FONT_SIZE_NORMAL, "px;}"), ".fontsize-LARGE.jsx-3234356038{font-size:".concat(FONT_SIZE_LARGE, "px;line-height:").concat(FONT_SIZE_LARGE, "px;}"), ".displaydensity-COMPACT.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".displaydensity-NORMAL.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".displaydensity-COMFORTABLE.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".column-header.jsx-3234356038{background-color:#dae6f8;}", "div.column-header-inner.jsx-3234356038{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}", ".title-cell.jsx-3234356038{font-weight:bold;background-color:#cddaed;padding:0;}", ".title-cell-content.jsx-3234356038{text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}", ".title-cell.displaydensity-COMPACT.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_COMPACT, "px;}"), ".title-cell.displaydensity-NORMAL.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_NORMAL, "px;}"), ".title-cell.displaydensity-COMFORTABLE.jsx-3234356038>.title-cell-content.jsx-3234356038{padding:".concat(DISPLAY_DENSITY_PADDING_COMFORTABLE, "px;}"), ".row-header.jsx-3234356038{background-color:#dae6f8;}", ".row-header-hierarchy.jsx-3234356038{text-align:left;}", ".empty-header.jsx-3234356038{background-color:#cddaed;}", ".total-header.jsx-3234356038{background-color:#bac6d8;}", ".value.jsx-3234356038{background-color:#ffffff;text-align:left;}", ".NUMBER.jsx-3234356038,.INTEGER.jsx-3234356038,.INTEGER_POSITIVE.jsx-3234356038,.INTEGER_NEGATIVE.jsx-3234356038,.INTEGER_ZERO_OR_POSITIVE.jsx-3234356038,.UNIT_INTERVAL.jsx-3234356038,.PERCENTAGE.jsx-3234356038,.BOOLEAN.jsx-3234356038,.TRUE_ONLY.jsx-3234356038{text-align:right;}", ".clickable.jsx-3234356038{cursor:pointer;}", ".value.jsx-3234356038:hover{background-color:#f3f3f3;}", ".subtotal.jsx-3234356038{background-color:#f4f4f4;}", ".total.jsx-3234356038{background-color:#d8d8d8;}", ".column-header-label.jsx-3234356038{overflow:hidden;text-overflow:ellipsis;}"];
6
+ cell.__hash = "3234356038";
7
7
  export const sortIcon = [".fontsize-SMALL.jsx-2877616992{height:".concat(FONT_SIZE_SMALL, "px;margin-bottom:1px;margin-left:5px;}"), ".fontsize-NORMAL.jsx-2877616992{height:".concat(FONT_SIZE_NORMAL, "px;max-height:11px;margin-bottom:2px;margin-left:6px;}"), ".fontsize-LARGE.jsx-2877616992{height:".concat(FONT_SIZE_LARGE, "px;margin-bottom:2px;margin-left:7px;}")];
8
8
  sortIcon.__hash = "2877616992";
@@ -1,5 +1,5 @@
1
1
  import { NUMBER_TYPE_ROW_PERCENTAGE, NUMBER_TYPE_COLUMN_PERCENTAGE } from './pivotTable/pivotTableConstants.js';
2
- import { isNumericValueType } from './valueTypes.js';
2
+ import { isNumericValueType, isBooleanValueType } from './valueTypes.js';
3
3
 
4
4
  const trimTrailingZeros = stringValue => stringValue.replace(/\.?0+$/, '');
5
5
 
@@ -53,7 +53,7 @@ const toFixedPrecisionString = (value, skipRounding) => {
53
53
  };
54
54
 
55
55
  export const renderValue = (value, valueType, visualization) => {
56
- if (!isNumericValueType(valueType) || value === undefined) {
56
+ if (!(isNumericValueType(valueType) || isBooleanValueType(valueType)) || value === undefined) {
57
57
  return String(value).replace(/[^\S\n]+/, ' ');
58
58
  }
59
59
 
@@ -23,4 +23,6 @@ export const VALUE_TYPE_DATETIME = 'DATETIME';
23
23
  export const VALUE_TYPE_ORGANISATION_UNIT = 'ORGANISATION_UNIT';
24
24
  export const VALUE_TYPE_AGE = 'AGE';
25
25
  const NUMERIC_VALUE_TYPES = [VALUE_TYPE_NUMBER, VALUE_TYPE_UNIT_INTERVAL, VALUE_TYPE_PERCENTAGE, VALUE_TYPE_INTEGER, VALUE_TYPE_INTEGER_POSITIVE, VALUE_TYPE_INTEGER_NEGATIVE, VALUE_TYPE_INTEGER_ZERO_OR_POSITIVE];
26
- export const isNumericValueType = type => NUMERIC_VALUE_TYPES.includes(type);
26
+ const BOOLEAN_VALUE_TYPES = [VALUE_TYPE_BOOLEAN, VALUE_TYPE_TRUE_ONLY];
27
+ export const isNumericValueType = type => NUMERIC_VALUE_TYPES.includes(type);
28
+ export const isBooleanValueType = type => BOOLEAN_VALUE_TYPES.includes(type);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/analytics",
3
- "version": "24.10.12",
3
+ "version": "24.11.0",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "exports": {