@dhis2/analytics 20.4.6 → 20.4.10

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,31 @@
1
+ ## [20.4.10](https://github.com/dhis2/analytics/compare/v20.4.9...v20.4.10) (2021-09-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix fixed row header with more than 2 cells DHIS2-11835 ([#1053](https://github.com/dhis2/analytics/issues/1053)) ([d0771d9](https://github.com/dhis2/analytics/commit/d0771d9187ea2e1c14ac6e2d2b083eb737e77f52))
7
+
8
+ ## [20.4.9](https://github.com/dhis2/analytics/compare/v20.4.8...v20.4.9) (2021-09-21)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * use lighter grey for No data text in SV ([#1050](https://github.com/dhis2/analytics/issues/1050)) ([ff16657](https://github.com/dhis2/analytics/commit/ff1665778131fad0eefcbc2ac609ee83fc097f6d))
14
+
15
+ ## [20.4.8](https://github.com/dhis2/analytics/compare/v20.4.7...v20.4.8) (2021-09-20)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * show no data text instead of undefined ([#1048](https://github.com/dhis2/analytics/issues/1048)) ([a4f577d](https://github.com/dhis2/analytics/commit/a4f577d6a8fec3183a5430d241a1f752d6e0f217))
21
+
22
+ ## [20.4.7](https://github.com/dhis2/analytics/compare/v20.4.6...v20.4.7) (2021-09-15)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * adjust row clipping partitions after sorting [DHIS2-11724] ([#1036](https://github.com/dhis2/analytics/issues/1036)) ([c455140](https://github.com/dhis2/analytics/commit/c4551407032389b721394e1aa16f57637f987012))
28
+
1
29
  ## [20.4.6](https://github.com/dhis2/analytics/compare/v20.4.5...v20.4.6) (2021-09-14)
2
30
 
3
31
 
@@ -43,7 +43,8 @@ const PivotTableRowHeaderCell = ({
43
43
  style: {
44
44
  width,
45
45
  height,
46
- left: rowLevel > 0 ? engine.adaptiveClippingController.columns.headerSizes[rowLevel - 1] : 0
46
+ left: rowLevel > 0 ? // calculate the width of all row header cells on the left of current cell
47
+ engine.adaptiveClippingController.columns.headerSizes.slice(0, rowLevel).reduce((width, acc) => acc += width, 0) : 0
47
48
  },
48
49
  dataTest: "visualization-row-header"
49
50
  }, header.label)
@@ -79,10 +79,7 @@ class AdaptiveClippingController {
79
79
  }
80
80
 
81
81
  finalizeAxis(axis) {
82
- axis.totalSize = 0;
83
82
  axis.headerSize = 0;
84
- let nextPartitionPx = 0;
85
- axis.partitions = [];
86
83
  const isColumn = axis.orientation === 'column';
87
84
  const map = isColumn ? this.engine.columnMap : this.engine.rowMap;
88
85
 
@@ -123,21 +120,35 @@ class AdaptiveClippingController {
123
120
  }
124
121
  }
125
122
  });
126
- const cellSize = this.getCellSize((_axis$sizes$index2 = axis.sizes[index]) === null || _axis$sizes$index2 === void 0 ? void 0 : _axis$sizes$index2.size);
127
123
  axis.sizes[index] = {
128
- pre: axis.totalSize,
129
- size: cellSize
124
+ pre: 0,
125
+ size: this.getCellSize((_axis$sizes$index2 = axis.sizes[index]) === null || _axis$sizes$index2 === void 0 ? void 0 : _axis$sizes$index2.size)
130
126
  };
127
+ });
128
+ this.populateAxisPartitions(axis);
129
+ }
131
130
 
131
+ populateAxisPartitions(axis) {
132
+ axis.totalSize = 0;
133
+ axis.partitions = [];
134
+ let nextPartitionPx = 0;
135
+ const isColumn = axis.orientation === 'column';
136
+ const map = isColumn ? this.engine.columnMap : this.engine.rowMap;
137
+ map.forEach((index, mapIndex) => {
132
138
  if (axis.totalSize >= nextPartitionPx) {
133
139
  axis.partitions.push(mapIndex);
134
140
  nextPartitionPx += _pivotTableConstants.CLIPPED_AXIS_PARTITION_SIZE_PX;
135
141
  }
136
142
 
137
- axis.totalSize += cellSize;
143
+ axis.sizes[index].pre = axis.totalSize;
144
+ axis.totalSize += axis.sizes[index].size;
138
145
  });
139
146
  }
140
147
 
148
+ resetRowPartitions() {
149
+ this.populateAxisPartitions(this.rows);
150
+ }
151
+
141
152
  finalize() {
142
153
  if (this.engine.visualization.showDimensionLabels) {
143
154
  const columnDimensionCount = this.engine.dimensionLookup.columnHeaders.length;
@@ -1009,10 +1009,12 @@ class PivotTableEngine {
1009
1009
 
1010
1010
  return valueA.renderedValue.localeCompare(valueB.renderedValue) * order;
1011
1011
  });
1012
+ this.adaptiveClippingController.resetRowPartitions();
1012
1013
  }
1013
1014
 
1014
1015
  clearSort() {
1015
1016
  this.resetRowMap();
1017
+ this.adaptiveClippingController.resetRowPartitions();
1016
1018
  }
1017
1019
 
1018
1020
  }
@@ -29,8 +29,8 @@ function _default({
29
29
  });
30
30
  const metaData = store.data[0].metaData;
31
31
  const config = {
32
- value: data[0] === undefined ? extraOptions.noData.text : data[0],
33
- formattedValue: (0, _value.default)(data[0], layout, metaData, extraOptions),
32
+ value: data[0],
33
+ formattedValue: data[0] === undefined ? extraOptions.noData.text : (0, _value.default)(data[0], layout, metaData),
34
34
  title: (0, _title.default)(layout, metaData, extraOptions.dashboard),
35
35
  subtitle: (0, _subtitle.default)(layout, metaData, extraOptions.dashboard)
36
36
  };
@@ -11,13 +11,13 @@ var _pivotTableConstants = require("../../../../../modules/pivotTable/pivotTable
11
11
 
12
12
  var _renderValue = require("../../../../../modules/pivotTable/renderValue");
13
13
 
14
- function _default(value, layout, metaData, extraOptions) {
14
+ function _default(value, layout, metaData) {
15
15
  const valueType = metaData.items[metaData.dimensions.dx[0]].valueType;
16
16
  const indicatorType = metaData.items[metaData.dimensions.dx[0]].indicatorType;
17
17
  let formattedValue = (0, _renderValue.renderValue)(value, valueType || _pivotTableConstants.VALUE_TYPE_TEXT, {
18
18
  digitGroupSeparator: layout.digitGroupSeparator,
19
19
  skipRounding: layout.skipRounding
20
- }) || extraOptions.noData.text; // only show the percentage symbol for per cent
20
+ }); // only show the percentage symbol for per cent
21
21
  // for other factors, show the full text under the value
22
22
 
23
23
  if ((indicatorType === null || indicatorType === void 0 ? void 0 : indicatorType.factor) === _.INDICATOR_FACTOR_100) {
@@ -13,7 +13,14 @@ var _legends = require("../../../../modules/legends");
13
13
 
14
14
  const svgNS = 'http://www.w3.org/2000/svg';
15
15
 
16
- const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
16
+ const generateValueSVG = ({
17
+ value,
18
+ formattedValue,
19
+ subText,
20
+ legendSet,
21
+ noData,
22
+ y
23
+ }) => {
17
24
  const textSize = 300;
18
25
  const svgValue = document.createElementNS(svgNS, 'svg');
19
26
  svgValue.setAttribute('xmlns', svgNS);
@@ -23,7 +30,14 @@ const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
23
30
  svgValue.setAttribute('y', y);
24
31
  }
25
32
 
26
- const fillColor = legendSet ? (0, _legends.getColorByValueFromLegendSet)(legendSet, value) : _ui.colors.grey900;
33
+ let fillColor = _ui.colors.grey900;
34
+
35
+ if (legendSet) {
36
+ fillColor = (0, _legends.getColorByValueFromLegendSet)(legendSet, value);
37
+ } else if (formattedValue === noData.text) {
38
+ fillColor = _ui.colors.grey600;
39
+ }
40
+
27
41
  const textNode = document.createElementNS(svgNS, 'text');
28
42
  textNode.setAttribute('text-anchor', 'middle');
29
43
  textNode.setAttribute('font-size', textSize);
@@ -58,7 +72,10 @@ const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
58
72
  return svgValue;
59
73
  };
60
74
 
61
- const generateDashboardItem = (config, legendSet) => {
75
+ const generateDashboardItem = (config, {
76
+ legendSet,
77
+ noData
78
+ }) => {
62
79
  const container = document.createElement('div');
63
80
  container.setAttribute('style', 'display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%');
64
81
  const titleStyle = 'font-size: 12px; color: #666;';
@@ -78,7 +95,14 @@ const generateDashboardItem = (config, legendSet) => {
78
95
  container.appendChild(subtitle);
79
96
  }
80
97
 
81
- container.appendChild(generateValueSVG(config.value, config.formattedValue, config.subText, legendSet, 40));
98
+ container.appendChild(generateValueSVG({
99
+ value: config.value,
100
+ formattedValue: config.formattedValue,
101
+ subText: config.subText,
102
+ legendSet,
103
+ noData,
104
+ y: 40
105
+ }));
82
106
  return container;
83
107
  };
84
108
 
@@ -110,7 +134,12 @@ const getXFromTextAlign = textAlign => {
110
134
  }
111
135
  };
112
136
 
113
- const generateDVItem = (config, legendSet, parentEl, fontStyle) => {
137
+ const generateDVItem = (config, {
138
+ legendSet,
139
+ parentEl,
140
+ fontStyle,
141
+ noData
142
+ }) => {
114
143
  const parentElBBox = parentEl.getBoundingClientRect();
115
144
  const width = parentElBBox.width;
116
145
  const height = parentElBBox.height;
@@ -154,18 +183,34 @@ const generateDVItem = (config, legendSet, parentEl, fontStyle) => {
154
183
  svg.appendChild(subtitle);
155
184
  }
156
185
 
157
- svg.appendChild(generateValueSVG(config.value, config.formattedValue, config.subText, legendSet, 20));
186
+ svg.appendChild(generateValueSVG({
187
+ value: config.value,
188
+ formattedValue: config.formattedValue,
189
+ subText: config.subText,
190
+ legendSet,
191
+ noData,
192
+ y: 20
193
+ }));
158
194
  return svg;
159
195
  };
160
196
 
161
197
  function _default(config, parentEl, {
162
198
  dashboard,
163
199
  legendSets,
164
- fontStyle
200
+ fontStyle,
201
+ noData
165
202
  }) {
166
203
  const legendSet = legendSets[0];
167
204
  parentEl.style.overflow = 'hidden';
168
205
  parentEl.style.display = 'flex';
169
206
  parentEl.style.justifyContent = 'center';
170
- return dashboard ? generateDashboardItem(config, legendSet) : generateDVItem(config, legendSet, parentEl, fontStyle);
207
+ return dashboard ? generateDashboardItem(config, {
208
+ legendSet,
209
+ noData
210
+ }) : generateDVItem(config, {
211
+ legendSet,
212
+ parentEl,
213
+ fontStyle,
214
+ noData
215
+ });
171
216
  }
@@ -45,17 +45,17 @@ function _default({
45
45
  onError("No visualization implementation for format ".concat(outputFormat));
46
46
  }
47
47
 
48
- this.getConfig = () => {
49
- const DEFAULT_EXTRA_OPTIONS = {
50
- colors: _colors.theme1,
51
- noData: {
52
- text: _index.default.t('No data')
53
- },
54
- resetZoom: {
55
- text: _index.default.t('Reset zoom')
56
- }
57
- };
48
+ const DEFAULT_EXTRA_OPTIONS = {
49
+ colors: _colors.theme1,
50
+ noData: {
51
+ text: _index.default.t('No data')
52
+ },
53
+ resetZoom: {
54
+ text: _index.default.t('Reset zoom')
55
+ }
56
+ };
58
57
 
58
+ this.getConfig = () => {
59
59
  const config = _adapter({
60
60
  layout: _validator({
61
61
  layout,
@@ -77,6 +77,7 @@ function _default({
77
77
  };
78
78
 
79
79
  this.createVisualization = () => _generator(this.getConfig(), el, { ...extraOptions,
80
+ noData: DEFAULT_EXTRA_OPTIONS.noData,
80
81
  fontStyle: layout.fontStyle
81
82
  });
82
83
  }
@@ -29,7 +29,8 @@ export const PivotTableRowHeaderCell = ({
29
29
  style: {
30
30
  width,
31
31
  height,
32
- left: rowLevel > 0 ? engine.adaptiveClippingController.columns.headerSizes[rowLevel - 1] : 0
32
+ left: rowLevel > 0 ? // calculate the width of all row header cells on the left of current cell
33
+ engine.adaptiveClippingController.columns.headerSizes.slice(0, rowLevel).reduce((width, acc) => acc += width, 0) : 0
33
34
  },
34
35
  dataTest: "visualization-row-header"
35
36
  }, header.label)
@@ -70,10 +70,7 @@ export class AdaptiveClippingController {
70
70
  }
71
71
 
72
72
  finalizeAxis(axis) {
73
- axis.totalSize = 0;
74
73
  axis.headerSize = 0;
75
- let nextPartitionPx = 0;
76
- axis.partitions = [];
77
74
  const isColumn = axis.orientation === 'column';
78
75
  const map = isColumn ? this.engine.columnMap : this.engine.rowMap;
79
76
 
@@ -114,21 +111,35 @@ export class AdaptiveClippingController {
114
111
  }
115
112
  }
116
113
  });
117
- const cellSize = this.getCellSize((_axis$sizes$index2 = axis.sizes[index]) === null || _axis$sizes$index2 === void 0 ? void 0 : _axis$sizes$index2.size);
118
114
  axis.sizes[index] = {
119
- pre: axis.totalSize,
120
- size: cellSize
115
+ pre: 0,
116
+ size: this.getCellSize((_axis$sizes$index2 = axis.sizes[index]) === null || _axis$sizes$index2 === void 0 ? void 0 : _axis$sizes$index2.size)
121
117
  };
118
+ });
119
+ this.populateAxisPartitions(axis);
120
+ }
122
121
 
122
+ populateAxisPartitions(axis) {
123
+ axis.totalSize = 0;
124
+ axis.partitions = [];
125
+ let nextPartitionPx = 0;
126
+ const isColumn = axis.orientation === 'column';
127
+ const map = isColumn ? this.engine.columnMap : this.engine.rowMap;
128
+ map.forEach((index, mapIndex) => {
123
129
  if (axis.totalSize >= nextPartitionPx) {
124
130
  axis.partitions.push(mapIndex);
125
131
  nextPartitionPx += CLIPPED_AXIS_PARTITION_SIZE_PX;
126
132
  }
127
133
 
128
- axis.totalSize += cellSize;
134
+ axis.sizes[index].pre = axis.totalSize;
135
+ axis.totalSize += axis.sizes[index].size;
129
136
  });
130
137
  }
131
138
 
139
+ resetRowPartitions() {
140
+ this.populateAxisPartitions(this.rows);
141
+ }
142
+
132
143
  finalize() {
133
144
  if (this.engine.visualization.showDimensionLabels) {
134
145
  const columnDimensionCount = this.engine.dimensionLookup.columnHeaders.length;
@@ -994,10 +994,12 @@ export class PivotTableEngine {
994
994
 
995
995
  return valueA.renderedValue.localeCompare(valueB.renderedValue) * order;
996
996
  });
997
+ this.adaptiveClippingController.resetRowPartitions();
997
998
  }
998
999
 
999
1000
  clearSort() {
1000
1001
  this.resetRowMap();
1002
+ this.adaptiveClippingController.resetRowPartitions();
1001
1003
  }
1002
1004
 
1003
1005
  }
@@ -14,8 +14,8 @@ export default function ({
14
14
  });
15
15
  const metaData = store.data[0].metaData;
16
16
  const config = {
17
- value: data[0] === undefined ? extraOptions.noData.text : data[0],
18
- formattedValue: getValue(data[0], layout, metaData, extraOptions),
17
+ value: data[0],
18
+ formattedValue: data[0] === undefined ? extraOptions.noData.text : getValue(data[0], layout, metaData),
19
19
  title: getTitle(layout, metaData, extraOptions.dashboard),
20
20
  subtitle: getSubtitle(layout, metaData, extraOptions.dashboard)
21
21
  };
@@ -1,13 +1,13 @@
1
1
  import { INDICATOR_FACTOR_100 } from '../';
2
2
  import { VALUE_TYPE_TEXT } from '../../../../../modules/pivotTable/pivotTableConstants';
3
3
  import { renderValue } from '../../../../../modules/pivotTable/renderValue';
4
- export default function (value, layout, metaData, extraOptions) {
4
+ export default function (value, layout, metaData) {
5
5
  const valueType = metaData.items[metaData.dimensions.dx[0]].valueType;
6
6
  const indicatorType = metaData.items[metaData.dimensions.dx[0]].indicatorType;
7
7
  let formattedValue = renderValue(value, valueType || VALUE_TYPE_TEXT, {
8
8
  digitGroupSeparator: layout.digitGroupSeparator,
9
9
  skipRounding: layout.skipRounding
10
- }) || extraOptions.noData.text; // only show the percentage symbol for per cent
10
+ }); // only show the percentage symbol for per cent
11
11
  // for other factors, show the full text under the value
12
12
 
13
13
  if ((indicatorType === null || indicatorType === void 0 ? void 0 : indicatorType.factor) === INDICATOR_FACTOR_100) {
@@ -3,7 +3,14 @@ import { FONT_STYLE_VISUALIZATION_TITLE, FONT_STYLE_VISUALIZATION_SUBTITLE, FONT
3
3
  import { getColorByValueFromLegendSet } from '../../../../modules/legends';
4
4
  const svgNS = 'http://www.w3.org/2000/svg';
5
5
 
6
- const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
6
+ const generateValueSVG = ({
7
+ value,
8
+ formattedValue,
9
+ subText,
10
+ legendSet,
11
+ noData,
12
+ y
13
+ }) => {
7
14
  const textSize = 300;
8
15
  const svgValue = document.createElementNS(svgNS, 'svg');
9
16
  svgValue.setAttribute('xmlns', svgNS);
@@ -13,7 +20,14 @@ const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
13
20
  svgValue.setAttribute('y', y);
14
21
  }
15
22
 
16
- const fillColor = legendSet ? getColorByValueFromLegendSet(legendSet, value) : colors.grey900;
23
+ let fillColor = colors.grey900;
24
+
25
+ if (legendSet) {
26
+ fillColor = getColorByValueFromLegendSet(legendSet, value);
27
+ } else if (formattedValue === noData.text) {
28
+ fillColor = colors.grey600;
29
+ }
30
+
17
31
  const textNode = document.createElementNS(svgNS, 'text');
18
32
  textNode.setAttribute('text-anchor', 'middle');
19
33
  textNode.setAttribute('font-size', textSize);
@@ -48,7 +62,10 @@ const generateValueSVG = (value, formattedValue, subText, legendSet, y) => {
48
62
  return svgValue;
49
63
  };
50
64
 
51
- const generateDashboardItem = (config, legendSet) => {
65
+ const generateDashboardItem = (config, {
66
+ legendSet,
67
+ noData
68
+ }) => {
52
69
  const container = document.createElement('div');
53
70
  container.setAttribute('style', 'display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%');
54
71
  const titleStyle = 'font-size: 12px; color: #666;';
@@ -68,7 +85,14 @@ const generateDashboardItem = (config, legendSet) => {
68
85
  container.appendChild(subtitle);
69
86
  }
70
87
 
71
- container.appendChild(generateValueSVG(config.value, config.formattedValue, config.subText, legendSet, 40));
88
+ container.appendChild(generateValueSVG({
89
+ value: config.value,
90
+ formattedValue: config.formattedValue,
91
+ subText: config.subText,
92
+ legendSet,
93
+ noData,
94
+ y: 40
95
+ }));
72
96
  return container;
73
97
  };
74
98
 
@@ -100,7 +124,12 @@ const getXFromTextAlign = textAlign => {
100
124
  }
101
125
  };
102
126
 
103
- const generateDVItem = (config, legendSet, parentEl, fontStyle) => {
127
+ const generateDVItem = (config, {
128
+ legendSet,
129
+ parentEl,
130
+ fontStyle,
131
+ noData
132
+ }) => {
104
133
  const parentElBBox = parentEl.getBoundingClientRect();
105
134
  const width = parentElBBox.width;
106
135
  const height = parentElBBox.height;
@@ -144,18 +173,34 @@ const generateDVItem = (config, legendSet, parentEl, fontStyle) => {
144
173
  svg.appendChild(subtitle);
145
174
  }
146
175
 
147
- svg.appendChild(generateValueSVG(config.value, config.formattedValue, config.subText, legendSet, 20));
176
+ svg.appendChild(generateValueSVG({
177
+ value: config.value,
178
+ formattedValue: config.formattedValue,
179
+ subText: config.subText,
180
+ legendSet,
181
+ noData,
182
+ y: 20
183
+ }));
148
184
  return svg;
149
185
  };
150
186
 
151
187
  export default function (config, parentEl, {
152
188
  dashboard,
153
189
  legendSets,
154
- fontStyle
190
+ fontStyle,
191
+ noData
155
192
  }) {
156
193
  const legendSet = legendSets[0];
157
194
  parentEl.style.overflow = 'hidden';
158
195
  parentEl.style.display = 'flex';
159
196
  parentEl.style.justifyContent = 'center';
160
- return dashboard ? generateDashboardItem(config, legendSet) : generateDVItem(config, legendSet, parentEl, fontStyle);
197
+ return dashboard ? generateDashboardItem(config, {
198
+ legendSet,
199
+ noData
200
+ }) : generateDVItem(config, {
201
+ legendSet,
202
+ parentEl,
203
+ fontStyle,
204
+ noData
205
+ });
161
206
  }
@@ -31,17 +31,17 @@ export default function ({
31
31
  onError("No visualization implementation for format ".concat(outputFormat));
32
32
  }
33
33
 
34
- this.getConfig = () => {
35
- const DEFAULT_EXTRA_OPTIONS = {
36
- colors: theme1,
37
- noData: {
38
- text: i18n.t('No data')
39
- },
40
- resetZoom: {
41
- text: i18n.t('Reset zoom')
42
- }
43
- };
34
+ const DEFAULT_EXTRA_OPTIONS = {
35
+ colors: theme1,
36
+ noData: {
37
+ text: i18n.t('No data')
38
+ },
39
+ resetZoom: {
40
+ text: i18n.t('Reset zoom')
41
+ }
42
+ };
44
43
 
44
+ this.getConfig = () => {
45
45
  const config = _adapter({
46
46
  layout: _validator({
47
47
  layout,
@@ -63,6 +63,7 @@ export default function ({
63
63
  };
64
64
 
65
65
  this.createVisualization = () => _generator(this.getConfig(), el, { ...extraOptions,
66
+ noData: DEFAULT_EXTRA_OPTIONS.noData,
66
67
  fontStyle: layout.fontStyle
67
68
  });
68
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/analytics",
3
- "version": "20.4.6",
3
+ "version": "20.4.10",
4
4
  "main": "./build/cjs/index.js",
5
5
  "module": "./build/es/index.js",
6
6
  "exports": {