@dhis2/analytics 24.10.1 → 25.1.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/build/cjs/__demo__/CalculationModal.stories.js +448 -0
  3. package/build/cjs/api/analytics/AnalyticsRequest.js +12 -1
  4. package/build/cjs/api/dimensions.js +1 -1
  5. package/build/cjs/api/expression.js +67 -0
  6. package/build/cjs/assets/DimensionItemIcons/CalculationIcon.js +25 -0
  7. package/build/cjs/assets/FormulaIcon.js +40 -0
  8. package/build/cjs/components/DataDimension/Calculation/CalculationModal.js +448 -0
  9. package/build/cjs/components/DataDimension/Calculation/DataElementOption.js +78 -0
  10. package/build/cjs/components/DataDimension/Calculation/DataElementSelector.js +309 -0
  11. package/build/cjs/components/DataDimension/Calculation/DndContext.js +213 -0
  12. package/build/cjs/components/DataDimension/Calculation/DragHandleIcon.js +23 -0
  13. package/build/cjs/components/DataDimension/Calculation/DraggingItem.js +58 -0
  14. package/build/cjs/components/DataDimension/Calculation/DropZone.js +58 -0
  15. package/build/cjs/components/DataDimension/Calculation/FormulaField.js +121 -0
  16. package/build/cjs/components/DataDimension/Calculation/FormulaItem.js +232 -0
  17. package/build/cjs/components/DataDimension/Calculation/MathOperatorSelector.js +58 -0
  18. package/build/cjs/components/DataDimension/Calculation/Operator.js +81 -0
  19. package/build/cjs/components/DataDimension/Calculation/styles/CalculationModal.style.js +13 -0
  20. package/build/cjs/components/DataDimension/Calculation/styles/DataElementOption.style.js +13 -0
  21. package/build/cjs/components/DataDimension/Calculation/styles/DataElementSelector.style.js +13 -0
  22. package/build/cjs/components/DataDimension/Calculation/styles/DraggingItem.style.js +13 -0
  23. package/build/cjs/components/DataDimension/Calculation/styles/DropZone.style.js +13 -0
  24. package/build/cjs/components/DataDimension/Calculation/styles/FormulaField.style.js +13 -0
  25. package/build/cjs/components/DataDimension/Calculation/styles/FormulaItem.style.js +13 -0
  26. package/build/cjs/components/DataDimension/Calculation/styles/MathOperatorSelector.style.js +13 -0
  27. package/build/cjs/components/DataDimension/Calculation/styles/Operator.style.js +13 -0
  28. package/build/cjs/components/DataDimension/DataDimension.js +22 -6
  29. package/build/cjs/components/DataDimension/DataTypeSelector.js +5 -3
  30. package/build/cjs/components/DataDimension/ItemSelector.js +111 -73
  31. package/build/cjs/components/LegendKey/LegendKey.js +1 -1
  32. package/build/cjs/components/TransferOption.js +13 -4
  33. package/build/cjs/components/styles/DimensionSelector.style.js +2 -2
  34. package/build/cjs/components/styles/TransferOption.style.js +2 -2
  35. package/build/cjs/index.js +6 -0
  36. package/build/cjs/locales/en/translations.json +32 -7
  37. package/build/cjs/modules/__tests__/expressions.spec.js +139 -0
  38. package/build/cjs/modules/__tests__/hash.spec.js +92 -0
  39. package/build/cjs/modules/__tests__/parseExpression.spec.js +46 -0
  40. package/build/cjs/modules/dataTypes.js +8 -1
  41. package/build/cjs/modules/dimensionListItem.js +82 -0
  42. package/build/cjs/modules/expressions.js +164 -0
  43. package/build/cjs/modules/hash.js +28 -0
  44. package/build/cjs/visualizations/config/generators/dhis/singleValue.js +112 -58
  45. package/build/es/__demo__/CalculationModal.stories.js +440 -0
  46. package/build/es/api/analytics/AnalyticsRequest.js +11 -1
  47. package/build/es/api/dimensions.js +1 -1
  48. package/build/es/api/expression.js +57 -0
  49. package/build/es/assets/DimensionItemIcons/CalculationIcon.js +13 -0
  50. package/build/es/assets/FormulaIcon.js +30 -0
  51. package/build/es/components/DataDimension/Calculation/CalculationModal.js +419 -0
  52. package/build/es/components/DataDimension/Calculation/DataElementOption.js +61 -0
  53. package/build/es/components/DataDimension/Calculation/DataElementSelector.js +283 -0
  54. package/build/es/components/DataDimension/Calculation/DndContext.js +194 -0
  55. package/build/es/components/DataDimension/Calculation/DragHandleIcon.js +11 -0
  56. package/build/es/components/DataDimension/Calculation/DraggingItem.js +40 -0
  57. package/build/es/components/DataDimension/Calculation/DropZone.js +43 -0
  58. package/build/es/components/DataDimension/Calculation/FormulaField.js +98 -0
  59. package/build/es/components/DataDimension/Calculation/FormulaItem.js +207 -0
  60. package/build/es/components/DataDimension/Calculation/MathOperatorSelector.js +42 -0
  61. package/build/es/components/DataDimension/Calculation/Operator.js +64 -0
  62. package/build/es/components/DataDimension/Calculation/styles/CalculationModal.style.js +4 -0
  63. package/build/es/components/DataDimension/Calculation/styles/DataElementOption.style.js +4 -0
  64. package/build/es/components/DataDimension/Calculation/styles/DataElementSelector.style.js +4 -0
  65. package/build/es/components/DataDimension/Calculation/styles/DraggingItem.style.js +4 -0
  66. package/build/es/components/DataDimension/Calculation/styles/DropZone.style.js +4 -0
  67. package/build/es/components/DataDimension/Calculation/styles/FormulaField.style.js +4 -0
  68. package/build/es/components/DataDimension/Calculation/styles/FormulaItem.style.js +4 -0
  69. package/build/es/components/DataDimension/Calculation/styles/MathOperatorSelector.style.js +4 -0
  70. package/build/es/components/DataDimension/Calculation/styles/Operator.style.js +4 -0
  71. package/build/es/components/DataDimension/DataDimension.js +21 -6
  72. package/build/es/components/DataDimension/DataTypeSelector.js +6 -4
  73. package/build/es/components/DataDimension/ItemSelector.js +111 -73
  74. package/build/es/components/LegendKey/LegendKey.js +1 -1
  75. package/build/es/components/TransferOption.js +14 -5
  76. package/build/es/components/styles/DimensionSelector.style.js +2 -2
  77. package/build/es/components/styles/TransferOption.style.js +2 -2
  78. package/build/es/index.js +1 -1
  79. package/build/es/locales/en/translations.json +32 -7
  80. package/build/es/modules/__tests__/expressions.spec.js +136 -0
  81. package/build/es/modules/__tests__/hash.spec.js +88 -0
  82. package/build/es/modules/__tests__/parseExpression.spec.js +43 -0
  83. package/build/es/modules/dataTypes.js +6 -0
  84. package/build/es/modules/dimensionListItem.js +61 -0
  85. package/build/es/modules/expressions.js +131 -0
  86. package/build/es/modules/hash.js +12 -0
  87. package/build/es/visualizations/config/generators/dhis/singleValue.js +112 -58
  88. package/package.json +6 -1
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ var _appRuntime = require("@dhis2/app-runtime");
9
+
8
10
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
11
 
10
12
  var _react = _interopRequireDefault(require("react"));
@@ -20,15 +22,21 @@ const DataDimension = _ref => {
20
22
  onSelect,
21
23
  selectedDimensions,
22
24
  displayNameProp,
23
- infoBoxMessage
25
+ infoBoxMessage,
26
+ onCalculationSave
24
27
  } = _ref;
28
+ const {
29
+ serverVersion
30
+ } = (0, _appRuntime.useConfig)();
31
+ const supportsEDI = "".concat(serverVersion.major, ".").concat(serverVersion.minor, ".").concat(serverVersion.patch || 0) >= '2.40.0';
25
32
 
26
33
  const onSelectItems = selectedItem => onSelect({
27
34
  dimensionId: _predefinedDimensions.DIMENSION_ID_DATA,
28
35
  items: selectedItem.map(item => ({
29
36
  id: item.value,
30
37
  name: item.label,
31
- type: item.type
38
+ type: item.type,
39
+ expression: item.expression
32
40
  }))
33
41
  });
34
42
 
@@ -37,23 +45,31 @@ const DataDimension = _ref => {
37
45
  value: item.id,
38
46
  label: item.name,
39
47
  isActive: item.isActive,
40
- type: item.type
48
+ type: item.type,
49
+ expression: item.expression,
50
+ access: item.access
41
51
  })),
42
52
  onSelect: onSelectItems,
43
53
  displayNameProp: displayNameProp,
44
54
  infoBoxMessage: infoBoxMessage,
45
- dataTest: 'data-dimension'
55
+ dataTest: 'data-dimension',
56
+ supportsEDI: supportsEDI,
57
+ onEDISave: onCalculationSave
46
58
  });
47
59
  };
48
60
 
49
61
  DataDimension.propTypes = {
50
62
  displayNameProp: _propTypes.default.string.isRequired,
51
63
  selectedDimensions: _propTypes.default.arrayOf(_propTypes.default.shape({
64
+ expression: _propTypes.default.string,
52
65
  id: _propTypes.default.string,
53
- name: _propTypes.default.string
66
+ isActive: _propTypes.default.bool,
67
+ name: _propTypes.default.string,
68
+ type: _propTypes.default.string
54
69
  })).isRequired,
55
70
  onSelect: _propTypes.default.func.isRequired,
56
- infoBoxMessage: _propTypes.default.string
71
+ infoBoxMessage: _propTypes.default.string,
72
+ onCalculationSave: _propTypes.default.func
57
73
  };
58
74
  DataDimension.defaultProps = {
59
75
  selectedDimensions: [],
@@ -27,7 +27,8 @@ const DataTypeSelector = _ref => {
27
27
  let {
28
28
  currentDataType,
29
29
  onChange,
30
- dataTest
30
+ dataTest,
31
+ includeCalculations
31
32
  } = _ref;
32
33
  return /*#__PURE__*/_react.default.createElement("div", {
33
34
  className: "jsx-".concat(_DataTypeSelectorStyle.default.__hash) + " " + "container"
@@ -42,7 +43,7 @@ const DataTypeSelector = _ref => {
42
43
  key: _dataTypes.DIMENSION_TYPE_ALL,
43
44
  label: _index.default.t('All types'),
44
45
  dataTest: "".concat(dataTest, "-option-all")
45
- }), Object.values(_dataTypes.dataTypeMap).map(type => /*#__PURE__*/_react.default.createElement(_ui.SingleSelectOption, {
46
+ }), Object.values(_dataTypes.dataTypeMap).filter(type => type.id !== _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM || includeCalculations).map(type => /*#__PURE__*/_react.default.createElement(_ui.SingleSelectOption, {
46
47
  value: type.id,
47
48
  key: type.id,
48
49
  label: type.getName(),
@@ -55,7 +56,8 @@ const DataTypeSelector = _ref => {
55
56
  DataTypeSelector.propTypes = {
56
57
  currentDataType: _propTypes.default.string.isRequired,
57
58
  onChange: _propTypes.default.func.isRequired,
58
- dataTest: _propTypes.default.string
59
+ dataTest: _propTypes.default.string,
60
+ includeCalculations: _propTypes.default.bool
59
61
  };
60
62
  var _default = DataTypeSelector;
61
63
  exports.default = _default;
@@ -17,16 +17,14 @@ var _react = _interopRequireWildcard(require("react"));
17
17
 
18
18
  var _dimensions = require("../../api/dimensions.js");
19
19
 
20
- var _DataElementIcon = _interopRequireDefault(require("../../assets/DimensionItemIcons/DataElementIcon.js"));
21
-
22
- var _GenericIcon = _interopRequireDefault(require("../../assets/DimensionItemIcons/GenericIcon.js"));
23
-
24
20
  var _index = _interopRequireDefault(require("../../locales/index.js"));
25
21
 
26
22
  var _dataSets = require("../../modules/dataSets.js");
27
23
 
28
24
  var _dataTypes = require("../../modules/dataTypes.js");
29
25
 
26
+ var _dimensionListItem = require("../../modules/dimensionListItem.js");
27
+
30
28
  var _dimensionSelectorHelper = require("../../modules/dimensionSelectorHelper.js");
31
29
 
32
30
  var _utils = require("../../modules/utils.js");
@@ -35,6 +33,8 @@ var _DimensionSelectorStyle = _interopRequireDefault(require("../styles/Dimensio
35
33
 
36
34
  var _TransferOption = require("../TransferOption.js");
37
35
 
36
+ var _CalculationModal = _interopRequireDefault(require("./Calculation/CalculationModal.js"));
37
+
38
38
  var _DataTypeSelector = _interopRequireDefault(require("./DataTypeSelector.js"));
39
39
 
40
40
  var _GroupSelector = _interopRequireDefault(require("./GroupSelector.js"));
@@ -58,7 +58,8 @@ const LeftHeader = _ref => {
58
58
  subGroup,
59
59
  setSubGroup,
60
60
  displayNameProp,
61
- dataTest
61
+ dataTest,
62
+ supportsEDI
62
63
  } = _ref;
63
64
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
64
65
  className: "jsx-".concat(_DimensionSelectorStyle.default.__hash) + " " + "leftHeader"
@@ -78,8 +79,9 @@ const LeftHeader = _ref => {
78
79
  }), /*#__PURE__*/_react.default.createElement(_DataTypeSelector.default, {
79
80
  currentDataType: dataType,
80
81
  onChange: setDataType,
81
- dataTest: "".concat(dataTest, "-data-types-select-field")
82
- }), _dataTypes.dataTypeMap[dataType] && /*#__PURE__*/_react.default.createElement(_GroupSelector.default, {
82
+ dataTest: "".concat(dataTest, "-data-types-select-field"),
83
+ includeCalculations: supportsEDI
84
+ }), _dataTypes.dataTypeMap[dataType] && dataType !== _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM && /*#__PURE__*/_react.default.createElement(_GroupSelector.default, {
83
85
  dataType: dataType,
84
86
  displayNameProp: displayNameProp,
85
87
  currentGroup: group,
@@ -102,7 +104,8 @@ LeftHeader.propTypes = {
102
104
  setGroup: _propTypes.default.func,
103
105
  setSearchTerm: _propTypes.default.func,
104
106
  setSubGroup: _propTypes.default.func,
105
- subGroup: _propTypes.default.string
107
+ subGroup: _propTypes.default.string,
108
+ supportsEDI: _propTypes.default.bool
106
109
  };
107
110
 
108
111
  const EmptySelection = () => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("p", {
@@ -238,7 +241,9 @@ const ItemSelector = _ref5 => {
238
241
  rightFooter,
239
242
  displayNameProp,
240
243
  infoBoxMessage,
241
- dataTest
244
+ dataTest,
245
+ supportsEDI,
246
+ onEDISave
242
247
  } = _ref5;
243
248
  const [state, setState] = (0, _react.useState)({
244
249
  searchTerm: '',
@@ -249,6 +254,7 @@ const ItemSelector = _ref5 => {
249
254
  loading: true,
250
255
  nextPage: 1
251
256
  });
257
+ const [currentCalculation, setCurrentCalculation] = (0, _react.useState)();
252
258
  const dataEngine = (0, _appRuntime.useDataEngine)();
253
259
 
254
260
  const setSearchTerm = searchTerm => setState(state => ({ ...state,
@@ -259,7 +265,7 @@ const ItemSelector = _ref5 => {
259
265
  filter
260
266
  }));
261
267
 
262
- const debouncedSearchTerm = (0, _utils.useDebounce)(state.searchTerm, 200);
268
+ const debouncedSearchTerm = (0, _utils.useDebounce)(state.searchTerm, 500);
263
269
 
264
270
  const fetchItems = async page => {
265
271
  var _result$dimensionItem;
@@ -284,7 +290,8 @@ const ItemSelector = _ref5 => {
284
290
  label: "".concat(item.name, " - ").concat(metric.getName()),
285
291
  value: "".concat(item.id, ".").concat(metric.id),
286
292
  disabled: item.disabled,
287
- type: item.dimensionItemType
293
+ type: item.dimensionItemType,
294
+ expression: item.expression
288
295
  });
289
296
  } else {
290
297
  _dataSets.DATA_SETS_CONSTANTS.forEach(metric => {
@@ -292,7 +299,8 @@ const ItemSelector = _ref5 => {
292
299
  label: "".concat(item.name, " - ").concat(metric.getName()),
293
300
  value: "".concat(item.id, ".").concat(metric.id),
294
301
  disabled: item.disabled,
295
- type: item.dimensionItemType
302
+ type: item.dimensionItemType,
303
+ expression: item.expression
296
304
  });
297
305
  });
298
306
  }
@@ -301,7 +309,8 @@ const ItemSelector = _ref5 => {
301
309
  label: item.name,
302
310
  value: item.id,
303
311
  disabled: item.disabled,
304
- type: item.dimensionItemType
312
+ type: item.dimensionItemType,
313
+ expression: item.expression
305
314
  });
306
315
  }
307
316
  });
@@ -310,11 +319,11 @@ const ItemSelector = _ref5 => {
310
319
  options: page > 1 ? [...state.options, ...newOptions] : newOptions,
311
320
  nextPage: result.nextPage
312
321
  }));
313
- /* The following handles a very specific edge-case where the user can select all items from a
314
- page and then reopen the modal. Usually Transfer triggers the onEndReached when the end of
315
- the page is reached (scrolling down) or if too few items are on the left side (e.g. selecting
316
- 49 items from page 1, leaving only 1 item on the left side). However, due to the way Transfer
317
- works, if 0 items are available, more items are fetched, but all items are already selected
322
+ /* The following handles a very specific edge-case where the user can select all items from a
323
+ page and then reopen the modal. Usually Transfer triggers the onEndReached when the end of
324
+ the page is reached (scrolling down) or if too few items are on the left side (e.g. selecting
325
+ 49 items from page 1, leaving only 1 item on the left side). However, due to the way Transfer
326
+ works, if 0 items are available, more items are fetched, but all items are already selected
318
327
  (leaving 0 items on the left side still), the onReachedEnd won't trigger. Hence the code below:
319
328
  IF there is a next page AND some options were just fetched AND you have the same or more
320
329
  selected items than fetched items AND all fetched items are already selected -> fetch more!
@@ -339,7 +348,10 @@ const ItemSelector = _ref5 => {
339
348
  return {
340
349
  value,
341
350
  label: matchingItem.label,
342
- type: matchingItem.type
351
+ type: matchingItem.type,
352
+ ...(matchingItem.expression ? {
353
+ expression: matchingItem.expression
354
+ } : {})
343
355
  };
344
356
  }));
345
357
  };
@@ -361,55 +373,52 @@ const ItemSelector = _ref5 => {
361
373
  return (_find = [...state.options, ...selectedItems].find(item => item.value === value)) === null || _find === void 0 ? void 0 : _find.type;
362
374
  };
363
375
 
364
- const getTooltipText = itemType => {
365
- var _dataTypes$itemType;
376
+ const onSaveCalculation = async _ref6 => {
377
+ let {
378
+ id,
379
+ name,
380
+ expression,
381
+ isNew
382
+ } = _ref6;
383
+ onEDISave({
384
+ id,
385
+ name,
386
+ expression,
387
+ type: _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM
388
+ }); // close the modal
389
+
390
+ setCurrentCalculation(); // reload the list of options
366
391
 
367
- switch (itemType) {
368
- case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT_OPERAND:
369
- return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_DATA_ELEMENT].getItemName();
370
-
371
- case _dataSets.REPORTING_RATE:
372
- return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_DATA_SET].getItemName();
373
-
374
- case _dataTypes.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT:
375
- case _dataTypes.DIMENSION_TYPE_PROGRAM_ATTRIBUTE:
376
- return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_EVENT_DATA_ITEM].getItemName();
392
+ fetchItems(1);
377
393
 
378
- default:
379
- return (_dataTypes$itemType = _dataTypes.dataTypeMap[itemType]) === null || _dataTypes$itemType === void 0 ? void 0 : _dataTypes$itemType.getItemName();
394
+ if (isNew) {
395
+ // select the new calculation
396
+ onSelect([...selectedItems, {
397
+ value: id,
398
+ label: name,
399
+ expression,
400
+ type: _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM
401
+ }]);
380
402
  }
381
403
  };
382
404
 
383
- const getIcon = itemType => {
384
- switch (itemType) {
385
- case _dataTypes.DIMENSION_TYPE_INDICATOR:
386
- return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionIndicator16, null);
405
+ const onDeleteCalculation = _ref7 => {
406
+ let {
407
+ id
408
+ } = _ref7;
409
+ // close the modal
410
+ setCurrentCalculation(); // reload the list of options
387
411
 
388
- case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT_OPERAND:
389
- case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT:
390
- return _DataElementIcon.default;
412
+ fetchItems(1); // unselect the deleted calculation
391
413
 
392
- case _dataSets.REPORTING_RATE:
393
- return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionDataSet16, null);
394
-
395
- case _dataTypes.DIMENSION_TYPE_EVENT_DATA_ITEM:
396
- case _dataTypes.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT:
397
- case _dataTypes.DIMENSION_TYPE_PROGRAM_ATTRIBUTE:
398
- return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionEventDataItem16, null);
399
-
400
- case _dataTypes.DIMENSION_TYPE_PROGRAM_INDICATOR:
401
- return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionProgramIndicator16, null);
402
-
403
- default:
404
- return _GenericIcon.default;
405
- }
414
+ onSelect([...selectedItems.filter(item => item.value !== id)]);
406
415
  };
407
416
 
408
- return /*#__PURE__*/_react.default.createElement(_ui.Transfer, {
409
- onChange: _ref6 => {
417
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_ui.Transfer, {
418
+ onChange: _ref8 => {
410
419
  let {
411
420
  selected
412
- } = _ref6;
421
+ } = _ref8;
413
422
  return onChange(selected);
414
423
  },
415
424
  selected: selectedItems.map(item => item.value),
@@ -449,8 +458,16 @@ const ItemSelector = _ref5 => {
449
458
  searchTerm: state.searchTerm,
450
459
  setSearchTerm: setSearchTerm,
451
460
  displayNameProp: displayNameProp,
452
- dataTest: "".concat(dataTest, "-left-header")
461
+ dataTest: "".concat(dataTest, "-left-header"),
462
+ supportsEDI: supportsEDI
453
463
  }),
464
+ leftFooter: supportsEDI ? /*#__PURE__*/_react.default.createElement("div", {
465
+ className: "jsx-".concat(_DimensionSelectorStyle.default.__hash) + " " + "calculation-button"
466
+ }, /*#__PURE__*/_react.default.createElement(_ui.Button, {
467
+ icon: /*#__PURE__*/_react.default.createElement(_ui.IconAdd24, null),
468
+ onClick: () => setCurrentCalculation({}),
469
+ small: true
470
+ }, _index.default.t('Calculation'))) : undefined,
454
471
  enableOrderChange: true,
455
472
  height: _dimensionSelectorHelper.TRANSFER_HEIGHT,
456
473
  optionsWidth: _dimensionSelectorHelper.TRANSFER_OPTIONS_WIDTH,
@@ -460,20 +477,38 @@ const ItemSelector = _ref5 => {
460
477
  infoText: infoBoxMessage
461
478
  }),
462
479
  rightFooter: rightFooter,
463
- renderOption: props => /*#__PURE__*/_react.default.createElement(_TransferOption.TransferOption, _extends({}, props, {
464
- active: isActive(props.value
465
- /* eslint-disable-line react/prop-types */
466
- ),
467
- icon: getIcon(getItemType(props.value
468
- /* eslint-disable-line react/prop-types */
469
- )),
470
- tooltipText: state.filter.dataType === _dataTypes.DIMENSION_TYPE_ALL ? getTooltipText(getItemType(props.value
471
- /* eslint-disable-line react/prop-types */
472
- )) : undefined,
473
- dataTest: "".concat(dataTest, "-transfer-option")
474
- })),
480
+ renderOption: props => {
481
+ var _props$access;
482
+
483
+ return /*#__PURE__*/_react.default.createElement(_TransferOption.TransferOption
484
+ /* eslint-disable react/prop-types */
485
+ , _extends({}, props, {
486
+ active: isActive(props.value),
487
+ icon: (0, _dimensionListItem.getIcon)(getItemType(props.value)),
488
+ tooltipText: (0, _dimensionListItem.getTooltipText)({
489
+ type: getItemType(props.value),
490
+ expression: props.expression
491
+ }),
492
+ dataTest: "".concat(dataTest, "-transfer-option"),
493
+ onEditClick: getItemType(props.value) === _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM && !(((_props$access = props.access) === null || _props$access === void 0 ? void 0 : _props$access.write) === false) && supportsEDI ? () => setCurrentCalculation({
494
+ id: props.value,
495
+ name: props.label,
496
+ expression: props.expression
497
+ }) : undefined
498
+ /* eslint-enable react/prop-types */
499
+
500
+ }));
501
+ },
475
502
  dataTest: "".concat(dataTest, "-transfer")
476
- });
503
+ }), currentCalculation && supportsEDI && /*#__PURE__*/_react.default.createElement(_CalculationModal.default, {
504
+ calculation: currentCalculation,
505
+ onSave: onSaveCalculation,
506
+ onClose: () => setCurrentCalculation(),
507
+ onDelete: onDeleteCalculation,
508
+ displayNameProp: displayNameProp
509
+ }), /*#__PURE__*/_react.default.createElement(_style.default, {
510
+ id: _DimensionSelectorStyle.default.__hash
511
+ }, _DimensionSelectorStyle.default));
477
512
  };
478
513
 
479
514
  ItemSelector.propTypes = {
@@ -487,8 +522,11 @@ ItemSelector.propTypes = {
487
522
  label: _propTypes.default.string.isRequired,
488
523
  value: _propTypes.default.string.isRequired,
489
524
  isActive: _propTypes.default.bool,
490
- type: _propTypes.default.string
491
- }))
525
+ type: _propTypes.default.string,
526
+ expression: _propTypes.default.string
527
+ })),
528
+ supportsEDI: _propTypes.default.bool,
529
+ onEDISave: _propTypes.default.func
492
530
  };
493
531
  ItemSelector.defaultProps = {
494
532
  selectedItems: []
@@ -22,7 +22,7 @@ const LegendKey = _ref => {
22
22
  legendSets
23
23
  } = _ref;
24
24
  return legendSets.length ? /*#__PURE__*/_react.default.createElement("div", {
25
- "data-test": 'legend-key-container',
25
+ "data-test": "legend-key-container",
26
26
  className: "jsx-".concat(_LegendKeyStyle.default.__hash) + " " + "container"
27
27
  }, legendSets.map((legendSet, index) => /*#__PURE__*/_react.default.createElement("div", {
28
28
  key: legendSet.id,
@@ -31,10 +31,12 @@ const TransferOption = _ref => {
31
31
  icon,
32
32
  active,
33
33
  tooltipText,
34
- dataTest
34
+ dataTest,
35
+ onEditClick
35
36
  } = _ref;
36
37
 
37
38
  const renderContent = () => /*#__PURE__*/_react.default.createElement("div", {
39
+ "data-test": "".concat(dataTest, "-content"),
38
40
  onClick: event => {
39
41
  if (disabled) {
40
42
  return;
@@ -55,7 +57,6 @@ const TransferOption = _ref => {
55
57
  value
56
58
  }, event);
57
59
  },
58
- "data-test": "".concat(dataTest, "-content"),
59
60
  className: "jsx-".concat(_TransferOptionStyle.default.__hash) + " " + ((0, _classnames.default)('chip', {
60
61
  highlighted,
61
62
  disabled,
@@ -66,7 +67,14 @@ const TransferOption = _ref => {
66
67
  className: "jsx-".concat(_TransferOptionStyle.default.__hash) + " " + "icon"
67
68
  }, icon), /*#__PURE__*/_react.default.createElement("span", {
68
69
  className: "jsx-".concat(_TransferOptionStyle.default.__hash) + " " + "label"
69
- }, label), /*#__PURE__*/_react.default.createElement(_style.default, {
70
+ }, label), onEditClick && /*#__PURE__*/_react.default.createElement("span", {
71
+ onClick: e => {
72
+ e.stopPropagation();
73
+ onEditClick();
74
+ },
75
+ "data-test": "".concat(dataTest, "-edit-button"),
76
+ className: "jsx-".concat(_TransferOptionStyle.default.__hash) + " " + "edit"
77
+ }, /*#__PURE__*/_react.default.createElement(_ui.IconEdit16, null)), /*#__PURE__*/_react.default.createElement(_style.default, {
70
78
  id: _TransferOptionStyle.default.__hash
71
79
  }, _TransferOptionStyle.default));
72
80
 
@@ -95,5 +103,6 @@ TransferOption.propTypes = {
95
103
  selected: _propTypes.default.bool,
96
104
  tooltipText: _propTypes.default.string,
97
105
  onClick: _propTypes.default.func,
98
- onDoubleClick: _propTypes.default.func
106
+ onDoubleClick: _propTypes.default.func,
107
+ onEditClick: _propTypes.default.func
99
108
  };
@@ -7,7 +7,7 @@ exports.default = void 0;
7
7
 
8
8
  var _ui = require("@dhis2/ui");
9
9
 
10
- const _defaultExport = [".filterContainer.jsx-2057111968{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:".concat(_ui.spacers.dp12, ";margin-top:").concat(_ui.spacers.dp8, ";}"), ".emptyList.jsx-2057111968{text-align:center;font-size:14px;line-height:16px;margin:".concat(_ui.spacers.dp24, " 0 0;color:").concat(_ui.colors.grey700, ";}"), ".rightHeader.jsx-2057111968{font-size:14px;font-weight:400;}", ".leftHeader.jsx-2057111968{padding:".concat(_ui.spacers.dp12, " ").concat(_ui.spacers.dp4, ";}"), ".info-container.jsx-2057111968{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:".concat(_ui.spacers.dp8, ";padding:").concat(_ui.spacers.dp8, ";background-color:").concat(_ui.colors.grey200, ";border-radius:3px;}"), ".info-text.jsx-2057111968{padding-left:".concat(_ui.spacers.dp8, ";color:").concat(_ui.colors.grey900, ";font-size:12px;line-height:16px;}")];
11
- _defaultExport.__hash = "2057111968";
10
+ const _defaultExport = [".filterContainer.jsx-336728173{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:".concat(_ui.spacers.dp12, ";margin-top:").concat(_ui.spacers.dp8, ";}"), ".emptyList.jsx-336728173{text-align:center;font-size:14px;line-height:16px;margin:".concat(_ui.spacers.dp24, " 0 0;color:").concat(_ui.colors.grey700, ";}"), ".rightHeader.jsx-336728173{font-size:14px;font-weight:400;}", ".leftHeader.jsx-336728173{padding:".concat(_ui.spacers.dp12, " ").concat(_ui.spacers.dp4, ";}"), ".info-container.jsx-336728173{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:".concat(_ui.spacers.dp8, ";padding:").concat(_ui.spacers.dp8, ";background-color:").concat(_ui.colors.grey200, ";border-radius:3px;}"), ".info-text.jsx-336728173{padding-left:".concat(_ui.spacers.dp8, ";color:").concat(_ui.colors.grey900, ";font-size:12px;line-height:16px;}"), ".calculation-button.jsx-336728173{margin:".concat(_ui.spacers.dp8, " 0;}")];
11
+ _defaultExport.__hash = "336728173";
12
12
  var _default = _defaultExport;
13
13
  exports.default = _default;
@@ -7,7 +7,7 @@ exports.default = void 0;
7
7
 
8
8
  var _ui = require("@dhis2/ui");
9
9
 
10
- const _defaultExport = [".wrapper.jsx-3199850788:last-child{margin-bottom:".concat(_ui.spacers.dp4, ";}"), ".chip.jsx-3199850788{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;background:".concat(_ui.colors.grey200, ";font-size:14px;line-height:16px;padding:2px ").concat(_ui.spacers.dp8, " 2px ").concat(_ui.spacers.dp4, ";margin:").concat(_ui.spacers.dp4, " ").concat(_ui.spacers.dp8, " 0 ").concat(_ui.spacers.dp8, ";border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}"), ".chip.jsx-3199850788:hover{background:".concat(_ui.colors.grey300, ";}"), ".selected.jsx-3199850788{background:".concat(_ui.theme.secondary100, ";color:").concat(_ui.theme.secondary900, ";}"), ".selected.jsx-3199850788 .icon path{fill:".concat(_ui.theme.secondary700, ";}"), ".selected.jsx-3199850788:hover{background:#c9edeb;}", ".highlighted.jsx-3199850788,.highlighted.jsx-3199850788:hover{background:".concat(_ui.theme.secondary800, ";color:").concat(_ui.colors.white, ";}"), ".highlighted.jsx-3199850788 .icon path{fill:".concat(_ui.colors.white, ";}"), ".disabled.jsx-3199850788{opacity:0.3;cursor:not-allowed;}", ".inactive.jsx-3199850788{opacity:0.3;}", ".icon.jsx-3199850788,.label.jsx-3199850788{line-height:18px;}", ".icon.jsx-3199850788{margin-right:".concat(_ui.spacers.dp4, ";display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;vertical-align:text-bottom;padding-top:1px;}"), ".label.jsx-3199850788{font-size:14px;}"];
11
- _defaultExport.__hash = "3199850788";
10
+ const _defaultExport = [".wrapper.jsx-1302852599:last-child{margin-bottom:".concat(_ui.spacers.dp4, ";}"), ".chip.jsx-1302852599{display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;background:".concat(_ui.colors.grey200, ";font-size:14px;line-height:16px;padding:2px ").concat(_ui.spacers.dp8, " 2px ").concat(_ui.spacers.dp4, ";margin:").concat(_ui.spacers.dp4, " ").concat(_ui.spacers.dp8, " 0 ").concat(_ui.spacers.dp8, ";border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}"), ".chip.jsx-1302852599:hover{background:".concat(_ui.colors.grey300, ";}"), ".selected.jsx-1302852599{background:".concat(_ui.theme.secondary100, ";color:").concat(_ui.theme.secondary900, ";}"), ".selected.jsx-1302852599 .icon path{fill:".concat(_ui.theme.secondary700, ";}"), ".selected.jsx-1302852599:hover{background:#c9edeb;}", ".highlighted.jsx-1302852599,.highlighted.jsx-1302852599:hover{background:".concat(_ui.theme.secondary800, ";color:").concat(_ui.colors.white, ";}"), ".highlighted.jsx-1302852599 .icon path{fill:".concat(_ui.colors.white, ";}"), ".disabled.jsx-1302852599{opacity:0.3;cursor:not-allowed;}", ".inactive.jsx-1302852599{opacity:0.3;}", ".icon.jsx-1302852599,.label.jsx-1302852599{line-height:18px;}", ".icon.jsx-1302852599{margin-right:".concat(_ui.spacers.dp4, ";display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;vertical-align:text-bottom;padding-top:1px;}"), ".label.jsx-1302852599{font-size:14px;}", ".edit.jsx-1302852599{height:16px;margin-top:1px;margin-left:".concat(_ui.spacers.dp8, ";cursor:pointer;}"), ".edit.jsx-1302852599:hover{background-color:rgba(0,0,0,0.12);outline:1px solid rgba(0,0,0,0.12);border-radius:3px;}", ".highlighted.jsx-1302852599 .edit.jsx-1302852599:hover{background-color:rgba(255,255,255,0.12);outline:1px solid rgba(255,255,255,0.12);}"];
11
+ _defaultExport.__hash = "1302852599";
12
12
  var _default = _defaultExport;
13
13
  exports.default = _default;
@@ -231,6 +231,12 @@ Object.defineProperty(exports, "DIMENSION_TYPE_EVENT_DATA_ITEM", {
231
231
  return _dataTypes.DIMENSION_TYPE_EVENT_DATA_ITEM;
232
232
  }
233
233
  });
234
+ Object.defineProperty(exports, "DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM", {
235
+ enumerable: true,
236
+ get: function () {
237
+ return _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM;
238
+ }
239
+ });
234
240
  Object.defineProperty(exports, "DIMENSION_TYPE_INDICATOR", {
235
241
  enumerable: true,
236
242
  get: function () {
@@ -21,27 +21,45 @@
21
21
  "About this visualization": "About this visualization",
22
22
  "This app could not retrieve required data.": "This app could not retrieve required data.",
23
23
  "Network error": "Network error",
24
- "Data Type": "Data Type",
25
- "All types": "All types",
24
+ "Data / Edit calculation": "Data / Edit calculation",
25
+ "Data / New calculation": "Data / New calculation",
26
+ "Remove item": "Remove item",
27
+ "Check formula": "Check formula",
28
+ "Calculation name": "Calculation name",
29
+ "Shown in table headers and chart axes/legends": "Shown in table headers and chart axes/legends",
30
+ "Delete calculation": "Delete calculation",
31
+ "Cancel": "Cancel",
32
+ "The calculation can only be saved with a valid formula": "The calculation can only be saved with a valid formula",
33
+ "Add a name to save this calculation": "Add a name to save this calculation",
34
+ "Save calculation": "Save calculation",
35
+ "Are you sure you want to delete this calculation? It may be used by other visualizations.": "Are you sure you want to delete this calculation? It may be used by other visualizations.",
36
+ "Yes, delete": "Yes, delete",
26
37
  "Totals only": "Totals only",
27
38
  "Details only": "Details only",
39
+ "Loading": "Loading",
40
+ "Data elements": "Data elements",
41
+ "Search by data element name": "Search by data element name",
42
+ "No data elements found for \"{{- searchTerm}}\"": "No data elements found for \"{{- searchTerm}}\"",
43
+ "No data elements found": "No data elements found",
44
+ "Drag items here, or double click in the list, to start building a calculation formula": "Drag items here, or double click in the list, to start building a calculation formula",
45
+ "Math operators": "Math operators",
46
+ "Data Type": "Data Type",
47
+ "All types": "All types",
28
48
  "Disaggregation": "Disaggregation",
29
49
  "No data": "No data",
30
- "Loading": "Loading",
31
50
  "Search by data item name": "Search by data item name",
32
51
  "No items selected": "No items selected",
33
52
  "Selected Items": "Selected Items",
34
53
  "No indicators found": "No indicators found",
35
- "No data elements found": "No data elements found",
36
54
  "No data sets found": "No data sets found",
37
55
  "No event data items found": "No event data items found",
38
56
  "No program indicators found": "No program indicators found",
39
57
  "No indicators found for \"{{- searchTerm}}\"": "No indicators found for \"{{- searchTerm}}\"",
40
- "No data elements found for \"{{- searchTerm}}\"": "No data elements found for \"{{- searchTerm}}\"",
41
58
  "No data sets found for \"{{- searchTerm}}\"": "No data sets found for \"{{- searchTerm}}\"",
42
59
  "No event data items found for \"{{- searchTerm}}\"": "No event data items found for \"{{- searchTerm}}\"",
43
60
  "No program indicators found for \"{{- searchTerm}}\"": "No program indicators found for \"{{- searchTerm}}\"",
44
61
  "Nothing found for \"{{- searchTerm}}\"": "Nothing found for \"{{- searchTerm}}\"",
62
+ "Calculation": "Calculation",
45
63
  "Metric type": "Metric type",
46
64
  "All metrics": "All metrics",
47
65
  "Move to {{axisName}}": "Move to {{axisName}}",
@@ -63,7 +81,6 @@
63
81
  "Nothing found for {{- searchTerm}}": "Nothing found for {{- searchTerm}}",
64
82
  "Delete {{fileType}}": "Delete {{fileType}}",
65
83
  "This {{fileType}} and related interpretations will be deleted. Continue?": "This {{fileType}} and related interpretations will be deleted. Continue?",
66
- "Cancel": "Cancel",
67
84
  "Delete": "Delete",
68
85
  "File": "File",
69
86
  "New": "New",
@@ -278,7 +295,6 @@
278
295
  "Indicator": "Indicator",
279
296
  "No indicator groups found": "No indicator groups found",
280
297
  "Loading indicator groups": "Loading indicator groups",
281
- "Data elements": "Data elements",
282
298
  "Data element group": "Data element group",
283
299
  "Data element": "Data element",
284
300
  "No data element groups found": "No data element groups found",
@@ -294,6 +310,15 @@
294
310
  "Loading programs": "Loading programs",
295
311
  "Program indicators": "Program indicators",
296
312
  "Program indicator": "Program indicator",
313
+ "Calculations": "Calculations",
314
+ "Number": "Number",
315
+ "Formula is empty. Add items to the formula from the lists on the left.": "Formula is empty. Add items to the formula from the lists on the left.",
316
+ "Consecutive math operators": "Consecutive math operators",
317
+ "Consecutive data elements": "Consecutive data elements",
318
+ "Starts or ends with a math operator": "Starts or ends with a math operator",
319
+ "Empty parentheses": "Empty parentheses",
320
+ "Missing right parenthesis )": "Missing right parenthesis )",
321
+ "Missing left parenthesis (": "Missing left parenthesis (",
297
322
  "Extra Small": "Extra Small",
298
323
  "Small": "Small",
299
324
  "Regular": "Regular",