@dhis2/analytics 24.10.0 → 25.0.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 (86) 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 +447 -0
  9. package/build/cjs/components/DataDimension/Calculation/DataElementOption.js +77 -0
  10. package/build/cjs/components/DataDimension/Calculation/DataElementSelector.js +306 -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 +57 -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/TransferOption.js +13 -4
  32. package/build/cjs/components/styles/DimensionSelector.style.js +2 -2
  33. package/build/cjs/components/styles/TransferOption.style.js +2 -2
  34. package/build/cjs/index.js +6 -0
  35. package/build/cjs/locales/en/translations.json +32 -7
  36. package/build/cjs/modules/__tests__/expressions.spec.js +139 -0
  37. package/build/cjs/modules/__tests__/hash.spec.js +92 -0
  38. package/build/cjs/modules/__tests__/parseExpression.spec.js +46 -0
  39. package/build/cjs/modules/dataTypes.js +8 -1
  40. package/build/cjs/modules/dimensionListItem.js +82 -0
  41. package/build/cjs/modules/expressions.js +164 -0
  42. package/build/cjs/modules/hash.js +28 -0
  43. package/build/cjs/visualizations/config/generators/dhis/singleValue.js +2 -2
  44. package/build/es/__demo__/CalculationModal.stories.js +440 -0
  45. package/build/es/api/analytics/AnalyticsRequest.js +11 -1
  46. package/build/es/api/dimensions.js +1 -1
  47. package/build/es/api/expression.js +57 -0
  48. package/build/es/assets/DimensionItemIcons/CalculationIcon.js +13 -0
  49. package/build/es/assets/FormulaIcon.js +30 -0
  50. package/build/es/components/DataDimension/Calculation/CalculationModal.js +418 -0
  51. package/build/es/components/DataDimension/Calculation/DataElementOption.js +60 -0
  52. package/build/es/components/DataDimension/Calculation/DataElementSelector.js +280 -0
  53. package/build/es/components/DataDimension/Calculation/DndContext.js +194 -0
  54. package/build/es/components/DataDimension/Calculation/DragHandleIcon.js +11 -0
  55. package/build/es/components/DataDimension/Calculation/DraggingItem.js +40 -0
  56. package/build/es/components/DataDimension/Calculation/DropZone.js +43 -0
  57. package/build/es/components/DataDimension/Calculation/FormulaField.js +98 -0
  58. package/build/es/components/DataDimension/Calculation/FormulaItem.js +207 -0
  59. package/build/es/components/DataDimension/Calculation/MathOperatorSelector.js +41 -0
  60. package/build/es/components/DataDimension/Calculation/Operator.js +64 -0
  61. package/build/es/components/DataDimension/Calculation/styles/CalculationModal.style.js +4 -0
  62. package/build/es/components/DataDimension/Calculation/styles/DataElementOption.style.js +4 -0
  63. package/build/es/components/DataDimension/Calculation/styles/DataElementSelector.style.js +4 -0
  64. package/build/es/components/DataDimension/Calculation/styles/DraggingItem.style.js +4 -0
  65. package/build/es/components/DataDimension/Calculation/styles/DropZone.style.js +4 -0
  66. package/build/es/components/DataDimension/Calculation/styles/FormulaField.style.js +4 -0
  67. package/build/es/components/DataDimension/Calculation/styles/FormulaItem.style.js +4 -0
  68. package/build/es/components/DataDimension/Calculation/styles/MathOperatorSelector.style.js +4 -0
  69. package/build/es/components/DataDimension/Calculation/styles/Operator.style.js +4 -0
  70. package/build/es/components/DataDimension/DataDimension.js +21 -6
  71. package/build/es/components/DataDimension/DataTypeSelector.js +6 -4
  72. package/build/es/components/DataDimension/ItemSelector.js +111 -73
  73. package/build/es/components/TransferOption.js +14 -5
  74. package/build/es/components/styles/DimensionSelector.style.js +2 -2
  75. package/build/es/components/styles/TransferOption.style.js +2 -2
  76. package/build/es/index.js +1 -1
  77. package/build/es/locales/en/translations.json +32 -7
  78. package/build/es/modules/__tests__/expressions.spec.js +136 -0
  79. package/build/es/modules/__tests__/hash.spec.js +88 -0
  80. package/build/es/modules/__tests__/parseExpression.spec.js +43 -0
  81. package/build/es/modules/dataTypes.js +6 -0
  82. package/build/es/modules/dimensionListItem.js +61 -0
  83. package/build/es/modules/expressions.js +131 -0
  84. package/build/es/modules/hash.js +12 -0
  85. package/build/es/visualizations/config/generators/dhis/singleValue.js +2 -2
  86. package/package.json +6 -1
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+
3
+ var _expressions = require("../expressions.js");
4
+
5
+ const invalidTestExpressions = [{
6
+ message: 'Formula is empty. Add items to the formula from the lists on the left.',
7
+ expressions: ['']
8
+ }, // {
9
+ // message: 'Consecutive math operators',
10
+ // expressions: ['5+-', '5+++', '4+9-*', '5++9'],
11
+ // },
12
+ {
13
+ message: 'Consecutive data elements',
14
+ expressions: ['#{cYeuwXTCPkU}#{Jtf34kNZhzP}']
15
+ }, {
16
+ message: 'Starts or ends with a math operator',
17
+ expressions: ['+', '+1', '1-2/', '*((#{cYeuwXTCPkU}*#{Jtf34kNZhzP})))']
18
+ }, {
19
+ message: 'Empty parentheses',
20
+ expressions: ['#{cYeuwXTCPkU}*()']
21
+ }, {
22
+ message: 'Missing left parenthesis (',
23
+ expressions: [')', '5)', '((#{cYeuwXTCPkU}*#{P3jJH5Tu5VC.S34ULMcHMca})))']
24
+ }, {
25
+ message: 'Missing right parenthesis )',
26
+ expressions: ['(', '(5', '((#{cYeuwXTCPkU}*#{Jtf34kNZhzP})']
27
+ }];
28
+ const validTestExpressions = ['5+9', '((#{cYeuwXTCPkU}*#{Jtf34kNZhzP}))#{iKGjnOOaPlE}', '#{P3jJH5Tu5VC.S34ULMcHMca}*#{Jtf34kNZhzP}', '(5)+9', '(5+9)', '10/-5'];
29
+ describe('validateExpression', () => {
30
+ invalidTestExpressions.forEach(_ref => {
31
+ let {
32
+ expressions,
33
+ message
34
+ } = _ref;
35
+ expressions.forEach(exp => {
36
+ test("Fails: ".concat(message), () => {
37
+ expect((0, _expressions.validateExpression)(exp)).toEqual({
38
+ status: _expressions.INVALID_EXPRESSION,
39
+ message
40
+ });
41
+ });
42
+ });
43
+ });
44
+ validTestExpressions.forEach(exp => {
45
+ test("Passes validation: ".concat(exp), () => {
46
+ expect((0, _expressions.validateExpression)(exp)).toEqual(undefined);
47
+ });
48
+ });
49
+ });
50
+ describe('parseArrayToExpression', () => {
51
+ test('exp 1', () => {
52
+ const expressionArray = [{
53
+ label: 'abc123',
54
+ value: '#{abc123}',
55
+ type: _expressions.EXPRESSION_TYPE_DATA
56
+ }, {
57
+ label: '+',
58
+ value: '+',
59
+ type: _expressions.EXPRESSION_TYPE_OPERATOR
60
+ }, {
61
+ label: 'def456.xyz999',
62
+ value: '#{def456.xyz999}',
63
+ type: _expressions.EXPRESSION_TYPE_DATA
64
+ }, {
65
+ label: '/',
66
+ value: '/',
67
+ type: _expressions.EXPRESSION_TYPE_OPERATOR
68
+ }, {
69
+ label: '10',
70
+ value: '10',
71
+ type: _expressions.EXPRESSION_TYPE_NUMBER
72
+ }];
73
+ const expected = '#{abc123}+#{def456.xyz999}/10';
74
+ expect((0, _expressions.parseArrayToExpression)(expressionArray)).toEqual(expected);
75
+ });
76
+ });
77
+ describe('parseExpressionToArray', () => {
78
+ test('exp 1', () => {
79
+ const expression = '#{abc123}/10*99';
80
+ const expected = [{
81
+ label: 'abc123',
82
+ value: '#{abc123}',
83
+ type: _expressions.EXPRESSION_TYPE_DATA
84
+ }, {
85
+ label: '/',
86
+ value: '/',
87
+ type: _expressions.EXPRESSION_TYPE_OPERATOR
88
+ }, {
89
+ label: '10',
90
+ value: '10',
91
+ type: _expressions.EXPRESSION_TYPE_NUMBER
92
+ }, {
93
+ label: '×',
94
+ value: '*',
95
+ type: _expressions.EXPRESSION_TYPE_OPERATOR
96
+ }, {
97
+ label: '99',
98
+ value: '99',
99
+ type: _expressions.EXPRESSION_TYPE_NUMBER
100
+ }];
101
+ expect((0, _expressions.parseExpressionToArray)(expression)).toEqual(expected);
102
+ });
103
+ });
104
+ describe('getItemIdsFromExpression', () => {
105
+ test('exp 1', () => {
106
+ const expression = '#{abc123}/10*99';
107
+ const expected = ['abc123'];
108
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
109
+ });
110
+ test('exp 2', () => {
111
+ const expression = '#{abc123}/10*#{def456}';
112
+ const expected = ['abc123', 'def456'];
113
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
114
+ });
115
+ test('exp 3', () => {
116
+ const expression = '#{abc123}/10*#{def456.xyz999}';
117
+ const expected = ['abc123', 'def456.xyz999'];
118
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
119
+ });
120
+ test('exp 4', () => {
121
+ const expression = '#{abc123}/10*#{def456.xyz999}+#{ghi789}';
122
+ const expected = ['abc123', 'def456.xyz999', 'ghi789'];
123
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
124
+ });
125
+ test('exp 5', () => {
126
+ const expression = '#{abc123}/10*#{def456.xyz999}+#{ghi789}+#{jkl000}';
127
+ const expected = ['abc123', 'def456.xyz999', 'ghi789', 'jkl000'];
128
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
129
+ });
130
+ test('exp 6', () => {
131
+ const expression = '5/10';
132
+ const expected = [];
133
+ expect((0, _expressions.getItemIdsFromExpression)(expression)).toEqual(expected);
134
+ });
135
+ test('exp 6', () => {
136
+ const expected = [];
137
+ expect((0, _expressions.getItemIdsFromExpression)()).toEqual(expected);
138
+ });
139
+ });
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ var _dataTypes = require("../dataTypes.js");
4
+
5
+ var _hash = require("../hash.js");
6
+
7
+ describe('getHash', () => {
8
+ const textInput = 'Raymond Luxury Yacht';
9
+ it('accepts a string and returns a hash', () => {
10
+ const hash = (0, _hash.getHash)(textInput);
11
+ expect(typeof hash).toBe('string');
12
+ expect(hash).not.toBe(textInput);
13
+ });
14
+ it('is deterministic', () => {
15
+ expect((0, _hash.getHash)(textInput)).toBe((0, _hash.getHash)(textInput));
16
+ });
17
+ it('returns undefined for invalid input', () => {
18
+ const unsupportedTypes = ['', 1, true, null, undefined, {}, []];
19
+ unsupportedTypes.forEach(type => expect((0, _hash.getHash)(type)).toBe(undefined));
20
+ });
21
+ });
22
+ describe('getExpressionHashFromVisualization', () => {
23
+ const edi1 = {
24
+ id: 'OdiHJayrsKo',
25
+ dimensionItemType: _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM,
26
+ expression: '#{abc} * 10'
27
+ };
28
+ const edi2 = {
29
+ id: 'Uvn6LCg7dVU',
30
+ dimensionItemType: _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM,
31
+ expression: '#{abc} * 20'
32
+ };
33
+ const dxWithEdi = {
34
+ dimension: 'dx',
35
+ items: [edi1, edi2]
36
+ };
37
+ it('generates a hash (columns)', () => {
38
+ expect(typeof (0, _hash.getExpressionHashFromVisualization)({
39
+ columns: [dxWithEdi],
40
+ rows: [],
41
+ filters: []
42
+ })).toBe('string');
43
+ });
44
+ it('generates a hash (rows)', () => {
45
+ expect(typeof (0, _hash.getExpressionHashFromVisualization)({
46
+ columns: [],
47
+ rows: [dxWithEdi],
48
+ filters: []
49
+ })).toBe('string');
50
+ });
51
+ it('generates a hash (filters)', () => {
52
+ expect(typeof (0, _hash.getExpressionHashFromVisualization)({
53
+ columns: [],
54
+ rows: [],
55
+ filters: [dxWithEdi]
56
+ })).toBe('string');
57
+ });
58
+ it('does not generate a hash when there are no dimensions', () => {
59
+ expect((0, _hash.getExpressionHashFromVisualization)({
60
+ columns: [],
61
+ rows: [],
62
+ filters: []
63
+ })).toBe(undefined);
64
+ });
65
+ it('does not generate a hash when there are no EDI dimensions', () => {
66
+ expect((0, _hash.getExpressionHashFromVisualization)({
67
+ columns: [{
68
+ id: 'OdiHJayrsKo',
69
+ dimensionItemType: 'INDICATOR'
70
+ }],
71
+ rows: [],
72
+ filters: []
73
+ })).toBe(undefined);
74
+ });
75
+ it('sorts the edi objects by id before generating the hash to optimize caching', () => {
76
+ expect((0, _hash.getExpressionHashFromVisualization)({
77
+ columns: [{
78
+ dimension: 'dx',
79
+ items: [edi1, edi2]
80
+ }],
81
+ rows: [],
82
+ filters: []
83
+ })).toBe((0, _hash.getExpressionHashFromVisualization)({
84
+ columns: [{
85
+ dimension: 'dx',
86
+ items: [edi2, edi1]
87
+ }],
88
+ rows: [],
89
+ filters: []
90
+ }));
91
+ });
92
+ });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var _expressions = require("../expressions.js");
4
+
5
+ test('matches numbers and operators', () => {
6
+ expect((0, _expressions.parseExpression)('1+2-3*4/5')).toEqual(['1', '+', '2', '-', '3', '*', '4', '/', '5']);
7
+ });
8
+ test('matches #{} with letters, numbers and operators', () => {
9
+ expect((0, _expressions.parseExpression)('#{abc123}+100-200*300/400')).toEqual(['#{abc123}', '+', '100', '-', '200', '*', '300', '/', '400']);
10
+ });
11
+ test('matches numbers and operators with brackets', () => {
12
+ expect((0, _expressions.parseExpression)('1+(2-3)*4/5')).toEqual(['1', '+', '(', '2', '-', '3', ')', '*', '4', '/', '5']);
13
+ });
14
+ test('matches #{} with letters, numbers and operators with brackets', () => {
15
+ expect((0, _expressions.parseExpression)('(100-200)+#{abc123}*300/400')).toEqual(['(', '100', '-', '200', ')', '+', '#{abc123}', '*', '300', '/', '400']);
16
+ });
17
+ test('matches #{} with numbers and operators with brackets', () => {
18
+ expect((0, _expressions.parseExpression)('#{123}+(10-200)*3000/40000')).toEqual(['#{123}', '+', '(', '10', '-', '200', ')', '*', '3000', '/', '40000']);
19
+ });
20
+ test('matches #{} with letters and numbers only', () => {
21
+ expect((0, _expressions.parseExpression)('#{abc123}')).toEqual(['#{abc123}']);
22
+ });
23
+ test('matches #{} with numbers only', () => {
24
+ expect((0, _expressions.parseExpression)('#{123}')).toEqual(['#{123}']);
25
+ });
26
+ test('matches #{} with letters only', () => {
27
+ expect((0, _expressions.parseExpression)('#{abc}')).toEqual(['#{abc}']);
28
+ });
29
+ test('matches #{} with no input', () => {
30
+ expect((0, _expressions.parseExpression)('')).toEqual([]);
31
+ });
32
+ test('matches multiple #{} with operators', () => {
33
+ expect((0, _expressions.parseExpression)('#{abc123}+#{def456}')).toEqual(['#{abc123}', '+', '#{def456}']);
34
+ });
35
+ test('matches multiple #{} containing dots with operators', () => {
36
+ expect((0, _expressions.parseExpression)('#{abc123.xyz999}+#{def456.xyz999}')).toEqual(['#{abc123.xyz999}', '+', '#{def456.xyz999}']);
37
+ });
38
+ test('matches multiple #{} with operators with brackets', () => {
39
+ expect((0, _expressions.parseExpression)('(#{abc123}/#{def456})*#{ghi789}')).toEqual(['(', '#{abc123}', '/', '#{def456}', ')', '*', '#{ghi789}']);
40
+ });
41
+ test('matches on decimal numbers', () => {
42
+ expect((0, _expressions.parseExpression)('#{abc123}*1.2/#{def456}')).toEqual(['#{abc123}', '*', '1.2', '/', '#{def456}']);
43
+ });
44
+ test('matches on decimal numbers with #{} containing dots', () => {
45
+ expect((0, _expressions.parseExpression)('1.2+#{abc123.xyz999}')).toEqual(['1.2', '+', '#{abc123.xyz999}']);
46
+ });
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.dataTypeMap = exports.TOTALS = exports.SUB_GROUP_METRIC = exports.SUB_GROUP_DETAIL = exports.DIMENSION_TYPE_PROGRAM_INDICATOR = exports.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT = exports.DIMENSION_TYPE_PROGRAM_ATTRIBUTE = exports.DIMENSION_TYPE_PERIOD = exports.DIMENSION_TYPE_ORGANISATION_UNIT_GROUP_SET = exports.DIMENSION_TYPE_ORGANISATION_UNIT = exports.DIMENSION_TYPE_INDICATOR = exports.DIMENSION_TYPE_EVENT_DATA_ITEM = exports.DIMENSION_TYPE_DATA_SET = exports.DIMENSION_TYPE_DATA_ELEMENT_OPERAND = exports.DIMENSION_TYPE_DATA_ELEMENT_GROUP_SET = exports.DIMENSION_TYPE_DATA_ELEMENT = exports.DIMENSION_TYPE_DATA = exports.DIMENSION_TYPE_CATEGORY_OPTION_GROUP_SET = exports.DIMENSION_TYPE_CATEGORY = exports.DIMENSION_TYPE_ALL = exports.DETAIL = exports.DEFAULT_DATATYPE_ID = void 0;
6
+ exports.dataTypeMap = exports.TOTALS = exports.SUB_GROUP_METRIC = exports.SUB_GROUP_DETAIL = exports.DIMENSION_TYPE_PROGRAM_INDICATOR = exports.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT = exports.DIMENSION_TYPE_PROGRAM_ATTRIBUTE = exports.DIMENSION_TYPE_PERIOD = exports.DIMENSION_TYPE_ORGANISATION_UNIT_GROUP_SET = exports.DIMENSION_TYPE_ORGANISATION_UNIT = exports.DIMENSION_TYPE_INDICATOR = exports.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM = exports.DIMENSION_TYPE_EVENT_DATA_ITEM = exports.DIMENSION_TYPE_DATA_SET = exports.DIMENSION_TYPE_DATA_ELEMENT_OPERAND = exports.DIMENSION_TYPE_DATA_ELEMENT_GROUP_SET = exports.DIMENSION_TYPE_DATA_ELEMENT = exports.DIMENSION_TYPE_DATA = exports.DIMENSION_TYPE_CATEGORY_OPTION_GROUP_SET = exports.DIMENSION_TYPE_CATEGORY = exports.DIMENSION_TYPE_ALL = exports.DETAIL = exports.DEFAULT_DATATYPE_ID = void 0;
7
7
  exports.defaultGroupDetail = defaultGroupDetail;
8
8
  exports.defaultGroupId = defaultGroupId;
9
9
 
@@ -43,6 +43,8 @@ const DIMENSION_TYPE_ORGANISATION_UNIT = 'ORGANISATION_UNIT';
43
43
  exports.DIMENSION_TYPE_ORGANISATION_UNIT = DIMENSION_TYPE_ORGANISATION_UNIT;
44
44
  const DIMENSION_TYPE_ORGANISATION_UNIT_GROUP_SET = 'ORGANISATION_UNIT_GROUP_SET';
45
45
  exports.DIMENSION_TYPE_ORGANISATION_UNIT_GROUP_SET = DIMENSION_TYPE_ORGANISATION_UNIT_GROUP_SET;
46
+ const DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM = 'EXPRESSION_DIMENSION_ITEM';
47
+ exports.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM = DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM;
46
48
  const TOTALS = 'totals';
47
49
  exports.TOTALS = TOTALS;
48
50
  const DETAIL = 'detail';
@@ -120,6 +122,11 @@ const dataTypeMap = {
120
122
  getItemName: () => _index.default.t('Program indicator'),
121
123
  getGroupEmptyLabel: () => _index.default.t('No programs found'),
122
124
  getGroupLoadingLabel: () => _index.default.t('Loading programs')
125
+ },
126
+ [DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM]: {
127
+ id: DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM,
128
+ getName: () => _index.default.t('Calculations'),
129
+ getItemName: () => _index.default.t('Calculation')
123
130
  }
124
131
  };
125
132
  exports.dataTypeMap = dataTypeMap;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getTooltipText = exports.getIcon = void 0;
7
+
8
+ var _ui = require("@dhis2/ui");
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _DataElementIcon = _interopRequireDefault(require("../assets/DimensionItemIcons/DataElementIcon.js"));
13
+
14
+ var _GenericIcon = _interopRequireDefault(require("../assets/DimensionItemIcons/GenericIcon.js"));
15
+
16
+ var _CalculationIcon = _interopRequireDefault(require("./../assets/DimensionItemIcons/CalculationIcon.js"));
17
+
18
+ var _dataSets = require("./dataSets.js");
19
+
20
+ var _dataTypes = require("./dataTypes.js");
21
+
22
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
+
24
+ const getTooltipText = _ref => {
25
+ var _dataTypes$type;
26
+
27
+ let {
28
+ type,
29
+ expression
30
+ } = _ref;
31
+
32
+ if (type === _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM && expression) {
33
+ return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM].getItemName();
34
+ }
35
+
36
+ switch (type) {
37
+ case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT_OPERAND:
38
+ return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_DATA_ELEMENT].getItemName();
39
+
40
+ case _dataSets.REPORTING_RATE:
41
+ return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_DATA_SET].getItemName();
42
+
43
+ case _dataTypes.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT:
44
+ case _dataTypes.DIMENSION_TYPE_PROGRAM_ATTRIBUTE:
45
+ return _dataTypes.dataTypeMap[_dataTypes.DIMENSION_TYPE_EVENT_DATA_ITEM].getItemName();
46
+
47
+ default:
48
+ return (_dataTypes$type = _dataTypes.dataTypeMap[type]) === null || _dataTypes$type === void 0 ? void 0 : _dataTypes$type.getItemName();
49
+ }
50
+ };
51
+
52
+ exports.getTooltipText = getTooltipText;
53
+
54
+ const getIcon = type => {
55
+ switch (type) {
56
+ case _dataTypes.DIMENSION_TYPE_INDICATOR:
57
+ return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionIndicator16, null);
58
+
59
+ case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT_OPERAND:
60
+ case _dataTypes.DIMENSION_TYPE_DATA_ELEMENT:
61
+ return _DataElementIcon.default;
62
+
63
+ case _dataSets.REPORTING_RATE:
64
+ return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionDataSet16, null);
65
+
66
+ case _dataTypes.DIMENSION_TYPE_EVENT_DATA_ITEM:
67
+ case _dataTypes.DIMENSION_TYPE_PROGRAM_DATA_ELEMENT:
68
+ case _dataTypes.DIMENSION_TYPE_PROGRAM_ATTRIBUTE:
69
+ return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionEventDataItem16, null);
70
+
71
+ case _dataTypes.DIMENSION_TYPE_PROGRAM_INDICATOR:
72
+ return /*#__PURE__*/_react.default.createElement(_ui.IconDimensionProgramIndicator16, null);
73
+
74
+ case _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM:
75
+ return _CalculationIcon.default;
76
+
77
+ default:
78
+ return _GenericIcon.default;
79
+ }
80
+ };
81
+
82
+ exports.getIcon = getIcon;
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.validateExpression = exports.parseExpressionToArray = exports.parseExpression = exports.parseArrayToExpression = exports.getOperators = exports.getItemIdsFromExpression = exports.VALID_EXPRESSION = exports.INVALID_EXPRESSION = exports.EXPRESSION_TYPE_OPERATOR = exports.EXPRESSION_TYPE_NUMBER = exports.EXPRESSION_TYPE_DATA = void 0;
7
+
8
+ var _index = _interopRequireDefault(require("../locales/index.js"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ const EXPRESSION_TYPE_NUMBER = 'EXPRESSION_TYPE_NUMBER';
13
+ exports.EXPRESSION_TYPE_NUMBER = EXPRESSION_TYPE_NUMBER;
14
+ const EXPRESSION_TYPE_OPERATOR = 'EXPRESSION_TYPE_OPERATOR';
15
+ exports.EXPRESSION_TYPE_OPERATOR = EXPRESSION_TYPE_OPERATOR;
16
+ const EXPRESSION_TYPE_DATA = 'EXPRESSION_TYPE_DATA';
17
+ exports.EXPRESSION_TYPE_DATA = EXPRESSION_TYPE_DATA;
18
+ const VALID_EXPRESSION = 'OK';
19
+ exports.VALID_EXPRESSION = VALID_EXPRESSION;
20
+ const INVALID_EXPRESSION = 'ERROR';
21
+ exports.INVALID_EXPRESSION = INVALID_EXPRESSION;
22
+
23
+ const getOperators = () => [{
24
+ value: '+',
25
+ label: '+',
26
+ type: EXPRESSION_TYPE_OPERATOR
27
+ }, {
28
+ value: '-',
29
+ label: '-',
30
+ type: EXPRESSION_TYPE_OPERATOR
31
+ }, {
32
+ value: '*',
33
+ label: '×',
34
+ type: EXPRESSION_TYPE_OPERATOR
35
+ }, {
36
+ value: '/',
37
+ label: '/',
38
+ type: EXPRESSION_TYPE_OPERATOR
39
+ }, {
40
+ value: '(',
41
+ label: '(',
42
+ type: EXPRESSION_TYPE_OPERATOR
43
+ }, {
44
+ value: ')',
45
+ label: ')',
46
+ type: EXPRESSION_TYPE_OPERATOR
47
+ }, {
48
+ value: '',
49
+ label: _index.default.t('Number'),
50
+ type: EXPRESSION_TYPE_NUMBER
51
+ }];
52
+
53
+ exports.getOperators = getOperators;
54
+
55
+ const parseExpression = input => {
56
+ const regex = /(#{[a-zA-Z0-9#.]+}|[+\-*/()])|(\d+(\.\d+)?)/g;
57
+ return input.match(regex) || [];
58
+ };
59
+
60
+ exports.parseExpression = parseExpression;
61
+
62
+ const parseExpressionToArray = function () {
63
+ let expression = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
64
+ let metadata = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
65
+ return parseExpression(expression).map(value => {
66
+ if (value.startsWith('#{') && value.endsWith('}')) {
67
+ var _metadata$find;
68
+
69
+ const id = value.slice(2, -1);
70
+ const label = ((_metadata$find = metadata.find(item => item.id === id)) === null || _metadata$find === void 0 ? void 0 : _metadata$find.name) || id;
71
+ return {
72
+ value,
73
+ label,
74
+ type: EXPRESSION_TYPE_DATA
75
+ };
76
+ }
77
+
78
+ if (isNaN(value)) {
79
+ return {
80
+ value,
81
+ label: getOperators().find(op => op.value === value).label,
82
+ type: EXPRESSION_TYPE_OPERATOR
83
+ };
84
+ }
85
+
86
+ return {
87
+ value,
88
+ label: value,
89
+ type: EXPRESSION_TYPE_NUMBER
90
+ };
91
+ }) || [];
92
+ };
93
+
94
+ exports.parseExpressionToArray = parseExpressionToArray;
95
+
96
+ const parseArrayToExpression = function () {
97
+ let input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
98
+ return input.map(item => item.value).join('');
99
+ };
100
+
101
+ exports.parseArrayToExpression = parseArrayToExpression;
102
+
103
+ const validateExpression = expression => {
104
+ let result;
105
+ const leftParenthesisCount = expression.split('(').length - 1;
106
+ const rightParenthesisCount = expression.split(')').length - 1;
107
+
108
+ if (!expression) {
109
+ // empty formula
110
+ result = {
111
+ status: INVALID_EXPRESSION,
112
+ message: _index.default.t('Formula is empty. Add items to the formula from the lists on the left.')
113
+ }; // TODO: reimplement this but allow negative values, e.g. 10 / -5
114
+ // } else if (/[-+/*]{2,}/.test(expression)) {
115
+ // // two math operators next to each other
116
+ // result = {
117
+ // status: INVALID_EXPRESSION,
118
+ // message: i18n.t('Consecutive math operators'),
119
+ // }
120
+ } else if (/}#/.test(expression)) {
121
+ // two data elements next to each other
122
+ result = {
123
+ status: INVALID_EXPRESSION,
124
+ message: _index.default.t('Consecutive data elements')
125
+ };
126
+ } else if (/^[+\-*/]|[+\-*/]$/.test(expression)) {
127
+ // starting or ending with a math operator
128
+ result = {
129
+ status: INVALID_EXPRESSION,
130
+ message: _index.default.t('Starts or ends with a math operator')
131
+ };
132
+ } else if (/\(\)/.test(expression)) {
133
+ // contains an empty set of parentheses
134
+ result = {
135
+ status: INVALID_EXPRESSION,
136
+ message: _index.default.t('Empty parentheses')
137
+ };
138
+ } else if (leftParenthesisCount > rightParenthesisCount) {
139
+ // ( but no )
140
+ result = {
141
+ status: INVALID_EXPRESSION,
142
+ message: _index.default.t('Missing right parenthesis )')
143
+ };
144
+ } else if (rightParenthesisCount > leftParenthesisCount) {
145
+ // ) but no (
146
+ result = {
147
+ status: INVALID_EXPRESSION,
148
+ message: _index.default.t('Missing left parenthesis (')
149
+ };
150
+ }
151
+
152
+ return result;
153
+ };
154
+
155
+ exports.validateExpression = validateExpression;
156
+
157
+ const getItemIdsFromExpression = function () {
158
+ let expression = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
159
+ const regex = /#{([a-zA-Z0-9#]+.*?)}/g;
160
+ const matches = expression.match(regex);
161
+ return matches ? matches.map(match => match.slice(2, -1)) : [];
162
+ };
163
+
164
+ exports.getItemIdsFromExpression = getItemIdsFromExpression;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getHash = exports.getExpressionHashFromVisualization = void 0;
7
+
8
+ var _sha = _interopRequireDefault(require("crypto-js/sha1"));
9
+
10
+ var _dataTypes = require("./dataTypes.js");
11
+
12
+ var _layoutGetAllItems2 = require("./layout/layoutGetAllItems.js");
13
+
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+
16
+ const isValidValue = value => typeof value === 'string' && value.length;
17
+
18
+ const getHash = value => isValidValue(value) ? (0, _sha.default)(value).toString() : undefined;
19
+
20
+ exports.getHash = getHash;
21
+
22
+ const getExpressionHashFromVisualization = visualization => {
23
+ var _layoutGetAllItems;
24
+
25
+ return getHash((_layoutGetAllItems = (0, _layoutGetAllItems2.layoutGetAllItems)(visualization)) === null || _layoutGetAllItems === void 0 ? void 0 : _layoutGetAllItems.filter(item => item.dimensionItemType === _dataTypes.DIMENSION_TYPE_EXPRESSION_DIMENSION_ITEM && isValidValue(item.expression)).sort((i1, i2) => i1.id < i2.id ? -1 : i1.id > i2.id ? 1 : 0).map(edi => edi.expression).join(''));
26
+ };
27
+
28
+ exports.getExpressionHashFromVisualization = getExpressionHashFromVisualization;
@@ -151,8 +151,8 @@ const generateDVItem = (config, _ref3) => {
151
151
  const svg = document.createElementNS(svgNS, 'svg');
152
152
  svg.setAttribute('xmlns', svgNS);
153
153
  svg.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
154
- svg.setAttribute('width', '100%');
155
- svg.setAttribute('height', '100%');
154
+ svg.setAttribute('width', width);
155
+ svg.setAttribute('height', height);
156
156
  svg.setAttribute('data-test', 'visualization-container');
157
157
 
158
158
  if (backgroundColor) {