@dhis2/analytics 25.0.0 → 25.1.1
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 +14 -0
- package/build/cjs/components/DataDimension/Calculation/CalculationModal.js +2 -1
- package/build/cjs/components/DataDimension/Calculation/DataElementOption.js +1 -0
- package/build/cjs/components/DataDimension/Calculation/DataElementSelector.js +6 -3
- package/build/cjs/components/DataDimension/Calculation/FormulaField.js +2 -2
- package/build/cjs/components/DataDimension/Calculation/MathOperatorSelector.js +1 -0
- package/build/cjs/components/LegendKey/LegendKey.js +1 -1
- package/build/cjs/visualizations/config/generators/dhis/singleValue.js +121 -63
- package/build/es/components/DataDimension/Calculation/CalculationModal.js +2 -1
- package/build/es/components/DataDimension/Calculation/DataElementOption.js +1 -0
- package/build/es/components/DataDimension/Calculation/DataElementSelector.js +6 -3
- package/build/es/components/DataDimension/Calculation/FormulaField.js +2 -2
- package/build/es/components/DataDimension/Calculation/MathOperatorSelector.js +1 -0
- package/build/es/components/LegendKey/LegendKey.js +1 -1
- package/build/es/visualizations/config/generators/dhis/singleValue.js +121 -63
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [25.1.1](https://github.com/dhis2/analytics/compare/v25.1.0...v25.1.1) (2023-05-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* address various SV SVG issues after KFMT ([#1456](https://github.com/dhis2/analytics/issues/1456)) ([f0ee1f1](https://github.com/dhis2/analytics/commit/f0ee1f16450e1b6de7c879c8ecdeec7c1d7c89fb))
|
|
7
|
+
|
|
8
|
+
# [25.1.0](https://github.com/dhis2/analytics/compare/v25.0.0...v25.1.0) (2023-04-27)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* icon in SV visualization DHIS2-10496 ([#1440](https://github.com/dhis2/analytics/issues/1440)) ([e6563ca](https://github.com/dhis2/analytics/commit/e6563cacc5e901a04d5432330b09b685936ddd70))
|
|
14
|
+
|
|
1
15
|
# [25.0.0](https://github.com/dhis2/analytics/compare/v24.10.1...v25.0.0) (2023-04-24)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -415,7 +415,8 @@ const CalculationModal = _ref => {
|
|
|
415
415
|
loading: isCreatingCalculation || isUpdatingCalculation || isSavingCalculation,
|
|
416
416
|
dataTest: "save-button"
|
|
417
417
|
}, _index.default.t('Save calculation')))))), showDeletePrompt && /*#__PURE__*/_react.default.createElement(_ui.Modal, {
|
|
418
|
-
small: true
|
|
418
|
+
small: true,
|
|
419
|
+
dataTest: "calculation-delete-modal"
|
|
419
420
|
}, /*#__PURE__*/_react.default.createElement(_ui.ModalTitle, null, _index.default.t('Delete calculation')), /*#__PURE__*/_react.default.createElement(_ui.ModalContent, null, _index.default.t('Are you sure you want to delete this calculation? It may be used by other visualizations.')), /*#__PURE__*/_react.default.createElement(_ui.ModalActions, null, /*#__PURE__*/_react.default.createElement(_ui.ButtonStrip, {
|
|
420
421
|
end: true
|
|
421
422
|
}, /*#__PURE__*/_react.default.createElement(_ui.Button, {
|
|
@@ -58,6 +58,7 @@ const DataElementOption = _ref => {
|
|
|
58
58
|
className: "jsx-".concat(_DataElementOptionStyle.default.__hash) + " " + (listeners && listeners.className != null && listeners.className || attributes && attributes.className != null && attributes.className || "draggable-item")
|
|
59
59
|
}), /*#__PURE__*/_react.default.createElement("div", {
|
|
60
60
|
onDoubleClick: () => onDoubleClick(data),
|
|
61
|
+
"data-test": "data-element-option",
|
|
61
62
|
className: "jsx-".concat(_DataElementOptionStyle.default.__hash) + " " + "chip"
|
|
62
63
|
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
63
64
|
className: "jsx-".concat(_DataElementOptionStyle.default.__hash) + " " + "icon"
|
|
@@ -76,11 +76,13 @@ const GroupSelector = _ref => {
|
|
|
76
76
|
}, defaultGroup ? /*#__PURE__*/_react.default.createElement(_ui.SingleSelectOption, {
|
|
77
77
|
value: defaultGroup.id,
|
|
78
78
|
key: defaultGroup.id,
|
|
79
|
-
label: defaultGroup.getName()
|
|
79
|
+
label: defaultGroup.getName(),
|
|
80
|
+
dataTest: "data-element-group-select-option-".concat(defaultGroup.id)
|
|
80
81
|
}) : null, !loading ? groups.map(group => /*#__PURE__*/_react.default.createElement(_ui.SingleSelectOption, {
|
|
81
82
|
value: group.id,
|
|
82
83
|
key: group.id,
|
|
83
|
-
label: group.name
|
|
84
|
+
label: group.name,
|
|
85
|
+
dataTest: "data-element-group-select-option-".concat(group.id)
|
|
84
86
|
})) : null), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
85
87
|
id: _DataElementSelectorStyle.default.__hash
|
|
86
88
|
}, _DataElementSelectorStyle.default));
|
|
@@ -108,7 +110,8 @@ const DisaggregationSelector = _ref2 => {
|
|
|
108
110
|
}, Object.entries(options).map(option => /*#__PURE__*/_react.default.createElement(_ui.SingleSelectOption, {
|
|
109
111
|
value: option[0],
|
|
110
112
|
key: option[0],
|
|
111
|
-
label: option[1]
|
|
113
|
+
label: option[1],
|
|
114
|
+
dataTest: "data-element-disaggregation-select-option-".concat(option[0])
|
|
112
115
|
}))), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
113
116
|
id: _DataElementSelectorStyle.default.__hash
|
|
114
117
|
}, _DataElementSelectorStyle.default));
|
|
@@ -35,7 +35,7 @@ const FORMULA_BOX_ID = 'formulabox';
|
|
|
35
35
|
exports.FORMULA_BOX_ID = FORMULA_BOX_ID;
|
|
36
36
|
|
|
37
37
|
const Placeholder = () => /*#__PURE__*/_react.default.createElement("div", {
|
|
38
|
-
"data-test":
|
|
38
|
+
"data-test": "placeholder",
|
|
39
39
|
className: "jsx-".concat(_FormulaFieldStyle.default.__hash) + " " + "placeholder"
|
|
40
40
|
}, /*#__PURE__*/_react.default.createElement(_FormulaIcon.default, null), /*#__PURE__*/_react.default.createElement("span", {
|
|
41
41
|
className: "jsx-".concat(_FormulaFieldStyle.default.__hash) + " " + "help-text"
|
|
@@ -67,7 +67,7 @@ const FormulaField = _ref => {
|
|
|
67
67
|
className: "jsx-".concat(_FormulaFieldStyle.default.__hash) + " " + "border"
|
|
68
68
|
}), /*#__PURE__*/_react.default.createElement("div", {
|
|
69
69
|
ref: setLastDropzoneRef,
|
|
70
|
-
"data-test":
|
|
70
|
+
"data-test": "formula-field",
|
|
71
71
|
className: "jsx-".concat(_FormulaFieldStyle.default.__hash) + " " + "formula-field"
|
|
72
72
|
}, loading && /*#__PURE__*/_react.default.createElement(_ui.Center, null, /*#__PURE__*/_react.default.createElement(_ui.CircularLoader, {
|
|
73
73
|
small: true
|
|
@@ -30,6 +30,7 @@ const MathOperatorSelector = _ref => {
|
|
|
30
30
|
}, /*#__PURE__*/_react.default.createElement("h4", {
|
|
31
31
|
className: "jsx-".concat(_MathOperatorSelectorStyle.default.__hash) + " " + "sub-header"
|
|
32
32
|
}, _index.default.t('Math operators')), /*#__PURE__*/_react.default.createElement("div", {
|
|
33
|
+
"data-test": "operators-list",
|
|
33
34
|
className: "jsx-".concat(_MathOperatorSelectorStyle.default.__hash) + " " + "operators"
|
|
34
35
|
}, (0, _expressions.getOperators)().map((_ref2, index) => {
|
|
35
36
|
let {
|
|
@@ -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":
|
|
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,
|
|
@@ -11,62 +11,95 @@ var _fontStyle = require("../../../../modules/fontStyle.js");
|
|
|
11
11
|
|
|
12
12
|
var _legends = require("../../../../modules/legends.js");
|
|
13
13
|
|
|
14
|
-
const svgNS = 'http://www.w3.org/2000/svg';
|
|
14
|
+
const svgNS = 'http://www.w3.org/2000/svg'; // Compute text width before rendering
|
|
15
|
+
// Not exactly precise but close enough
|
|
16
|
+
|
|
17
|
+
const getTextWidth = (text, font) => {
|
|
18
|
+
const canvas = document.createElement('canvas');
|
|
19
|
+
const context = canvas.getContext('2d');
|
|
20
|
+
context.font = font;
|
|
21
|
+
return context.measureText(text).width;
|
|
22
|
+
};
|
|
15
23
|
|
|
16
24
|
const generateValueSVG = _ref => {
|
|
17
25
|
let {
|
|
18
26
|
formattedValue,
|
|
19
27
|
subText,
|
|
20
28
|
valueColor,
|
|
29
|
+
textColor,
|
|
30
|
+
icon,
|
|
21
31
|
noData,
|
|
22
|
-
|
|
32
|
+
containerWidth,
|
|
33
|
+
containerHeight
|
|
23
34
|
} = _ref;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
const ratio = containerHeight / containerWidth;
|
|
36
|
+
const iconSize = 300;
|
|
37
|
+
const iconPadding = 50;
|
|
38
|
+
const textSize = iconSize * 0.85;
|
|
39
|
+
const textWidth = getTextWidth(formattedValue, "".concat(textSize, "px Roboto"));
|
|
40
|
+
const subTextSize = 40;
|
|
41
|
+
const showIcon = icon && formattedValue !== noData.text;
|
|
42
|
+
let viewBoxWidth = textWidth;
|
|
43
|
+
|
|
44
|
+
if (showIcon) {
|
|
45
|
+
viewBoxWidth += iconSize + iconPadding;
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
const viewBoxHeight = viewBoxWidth * ratio;
|
|
49
|
+
const svgValue = document.createElementNS(svgNS, 'svg');
|
|
50
|
+
svgValue.setAttribute('viewBox', "0 0 ".concat(viewBoxWidth, " ").concat(viewBoxHeight));
|
|
51
|
+
svgValue.setAttribute('width', '95%');
|
|
52
|
+
svgValue.setAttribute('height', '95%');
|
|
53
|
+
svgValue.setAttribute('x', '50%');
|
|
54
|
+
svgValue.setAttribute('y', '50%');
|
|
55
|
+
svgValue.setAttribute('style', 'overflow: visible');
|
|
33
56
|
let fillColor = _ui.colors.grey900;
|
|
34
57
|
|
|
35
58
|
if (valueColor) {
|
|
36
59
|
fillColor = valueColor;
|
|
37
60
|
} else if (formattedValue === noData.text) {
|
|
38
61
|
fillColor = _ui.colors.grey600;
|
|
62
|
+
} // show icon if configured in maintenance app
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if (showIcon) {
|
|
66
|
+
// embed icon to allow changing color
|
|
67
|
+
// (elements with fill need to use "currentColor" for this to work)
|
|
68
|
+
const iconSvgNode = document.createElementNS(svgNS, 'svg');
|
|
69
|
+
iconSvgNode.setAttribute('width', iconSize);
|
|
70
|
+
iconSvgNode.setAttribute('height', iconSize);
|
|
71
|
+
iconSvgNode.setAttribute('viewBox', '0 0 48 48');
|
|
72
|
+
iconSvgNode.setAttribute('y', "-".concat(iconSize / 2));
|
|
73
|
+
iconSvgNode.setAttribute('x', "-".concat((iconSize + iconPadding + textWidth) / 2));
|
|
74
|
+
iconSvgNode.setAttribute('style', "color: ".concat(fillColor));
|
|
75
|
+
const parser = new DOMParser();
|
|
76
|
+
const svgIconDocument = parser.parseFromString(icon, 'image/svg+xml');
|
|
77
|
+
Array.from(svgIconDocument.documentElement.children).forEach(node => iconSvgNode.appendChild(node));
|
|
78
|
+
svgValue.appendChild(iconSvgNode);
|
|
39
79
|
}
|
|
40
80
|
|
|
41
81
|
const textNode = document.createElementNS(svgNS, 'text');
|
|
42
|
-
textNode.setAttribute('text-anchor', 'middle');
|
|
43
82
|
textNode.setAttribute('font-size', textSize);
|
|
44
83
|
textNode.setAttribute('font-weight', '300');
|
|
45
84
|
textNode.setAttribute('letter-spacing', '-5');
|
|
46
|
-
textNode.setAttribute('
|
|
85
|
+
textNode.setAttribute('text-anchor', 'middle');
|
|
86
|
+
textNode.setAttribute('x', showIcon ? "".concat((iconSize + iconPadding) / 2) : 0); // vertical align, "alignment-baseline: central" is not supported by Batik
|
|
87
|
+
|
|
88
|
+
textNode.setAttribute('y', '.35em');
|
|
47
89
|
textNode.setAttribute('fill', fillColor);
|
|
48
90
|
textNode.setAttribute('data-test', 'visualization-primary-value');
|
|
49
91
|
textNode.appendChild(document.createTextNode(formattedValue));
|
|
50
92
|
svgValue.appendChild(textNode);
|
|
51
93
|
|
|
52
94
|
if (subText) {
|
|
53
|
-
const svgSubText = document.createElementNS(svgNS, 'svg');
|
|
54
|
-
const subTextSize = 40;
|
|
55
|
-
svgSubText.setAttribute('viewBox', "0 -50 ".concat(textSize * 0.75 * formattedValue.length, " ").concat(textSize + 200));
|
|
56
|
-
|
|
57
|
-
if (y) {
|
|
58
|
-
svgSubText.setAttribute('y', y);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
95
|
const subTextNode = document.createElementNS(svgNS, 'text');
|
|
62
96
|
subTextNode.setAttribute('text-anchor', 'middle');
|
|
63
97
|
subTextNode.setAttribute('font-size', subTextSize);
|
|
64
|
-
subTextNode.setAttribute('
|
|
65
|
-
subTextNode.setAttribute('
|
|
66
|
-
subTextNode.setAttribute('fill',
|
|
98
|
+
subTextNode.setAttribute('y', iconSize / 2);
|
|
99
|
+
subTextNode.setAttribute('dy', subTextSize);
|
|
100
|
+
subTextNode.setAttribute('fill', textColor);
|
|
67
101
|
subTextNode.appendChild(document.createTextNode(subText));
|
|
68
|
-
|
|
69
|
-
svgValue.appendChild(svgSubText);
|
|
102
|
+
svgValue.appendChild(subTextNode);
|
|
70
103
|
}
|
|
71
104
|
|
|
72
105
|
return svgValue;
|
|
@@ -74,14 +107,28 @@ const generateValueSVG = _ref => {
|
|
|
74
107
|
|
|
75
108
|
const generateDashboardItem = (config, _ref2) => {
|
|
76
109
|
let {
|
|
110
|
+
svgContainer,
|
|
111
|
+
width,
|
|
112
|
+
height,
|
|
77
113
|
valueColor,
|
|
78
114
|
titleColor,
|
|
79
115
|
backgroundColor,
|
|
80
|
-
noData
|
|
116
|
+
noData,
|
|
117
|
+
icon
|
|
81
118
|
} = _ref2;
|
|
119
|
+
svgContainer.appendChild(generateValueSVG({
|
|
120
|
+
formattedValue: config.formattedValue,
|
|
121
|
+
subText: config.subText,
|
|
122
|
+
valueColor,
|
|
123
|
+
textColor: titleColor,
|
|
124
|
+
noData,
|
|
125
|
+
icon,
|
|
126
|
+
containerWidth: width,
|
|
127
|
+
containerHeight: height
|
|
128
|
+
}));
|
|
82
129
|
const container = document.createElement('div');
|
|
83
|
-
container.setAttribute('style', "display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; background-color:".concat(backgroundColor, ";"));
|
|
84
|
-
const titleStyle = "font-size: 12px; color: ".concat(titleColor || '#666', ";");
|
|
130
|
+
container.setAttribute('style', "display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; padding-top: 8px; ".concat(backgroundColor ? "background-color:".concat(backgroundColor, ";") : ''));
|
|
131
|
+
const titleStyle = "padding: 0 8px; text-align: center; font-size: 12px; color: ".concat(titleColor || '#666', ";");
|
|
85
132
|
const title = document.createElement('span');
|
|
86
133
|
title.setAttribute('style', titleStyle);
|
|
87
134
|
|
|
@@ -92,18 +139,12 @@ const generateDashboardItem = (config, _ref2) => {
|
|
|
92
139
|
|
|
93
140
|
if (config.subtitle) {
|
|
94
141
|
const subtitle = document.createElement('span');
|
|
95
|
-
subtitle.setAttribute('style', titleStyle + ' margin-top: 4px;
|
|
142
|
+
subtitle.setAttribute('style', titleStyle + ' margin-top: 4px;');
|
|
96
143
|
subtitle.appendChild(document.createTextNode(config.subtitle));
|
|
97
144
|
container.appendChild(subtitle);
|
|
98
145
|
}
|
|
99
146
|
|
|
100
|
-
container.appendChild(
|
|
101
|
-
formattedValue: config.formattedValue,
|
|
102
|
-
subText: config.subText,
|
|
103
|
-
valueColor,
|
|
104
|
-
noData,
|
|
105
|
-
y: 40
|
|
106
|
-
}));
|
|
147
|
+
container.appendChild(svgContainer);
|
|
107
148
|
return container;
|
|
108
149
|
};
|
|
109
150
|
|
|
@@ -137,37 +178,32 @@ const getXFromTextAlign = textAlign => {
|
|
|
137
178
|
|
|
138
179
|
const generateDVItem = (config, _ref3) => {
|
|
139
180
|
let {
|
|
181
|
+
svgContainer,
|
|
182
|
+
width,
|
|
183
|
+
height,
|
|
140
184
|
valueColor,
|
|
185
|
+
noData,
|
|
141
186
|
backgroundColor,
|
|
142
187
|
titleColor,
|
|
143
|
-
parentEl,
|
|
144
188
|
fontStyle,
|
|
145
|
-
|
|
189
|
+
icon
|
|
146
190
|
} = _ref3;
|
|
147
|
-
const parentElBBox = parentEl.getBoundingClientRect();
|
|
148
|
-
const width = parentElBBox.width;
|
|
149
|
-
const height = parentElBBox.height;
|
|
150
|
-
const svgNS = 'http://www.w3.org/2000/svg';
|
|
151
|
-
const svg = document.createElementNS(svgNS, 'svg');
|
|
152
|
-
svg.setAttribute('xmlns', svgNS);
|
|
153
|
-
svg.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
|
154
|
-
svg.setAttribute('width', width);
|
|
155
|
-
svg.setAttribute('height', height);
|
|
156
|
-
svg.setAttribute('data-test', 'visualization-container');
|
|
157
191
|
|
|
158
192
|
if (backgroundColor) {
|
|
159
|
-
|
|
193
|
+
svgContainer.setAttribute('style', "background-color: ".concat(backgroundColor, ";"));
|
|
160
194
|
const background = document.createElementNS(svgNS, 'rect');
|
|
161
195
|
background.setAttribute('width', '100%');
|
|
162
196
|
background.setAttribute('height', '100%');
|
|
163
197
|
background.setAttribute('fill', backgroundColor);
|
|
164
|
-
|
|
198
|
+
svgContainer.appendChild(background);
|
|
165
199
|
}
|
|
166
200
|
|
|
201
|
+
const svgWrapper = document.createElementNS(svgNS, 'svg');
|
|
167
202
|
const title = document.createElementNS(svgNS, 'text');
|
|
168
203
|
const titleFontStyle = (0, _fontStyle.mergeFontStyleWithDefault)(fontStyle && fontStyle[_fontStyle.FONT_STYLE_VISUALIZATION_TITLE], _fontStyle.FONT_STYLE_VISUALIZATION_TITLE);
|
|
204
|
+
const titleYPosition = titleFontStyle[_fontStyle.FONT_STYLE_OPTION_FONT_SIZE];
|
|
169
205
|
title.setAttribute('x', getXFromTextAlign(titleFontStyle[_fontStyle.FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
170
|
-
title.setAttribute('y',
|
|
206
|
+
title.setAttribute('y', titleYPosition);
|
|
171
207
|
title.setAttribute('text-anchor', getTextAnchorFromTextAlign(titleFontStyle[_fontStyle.FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
172
208
|
title.setAttribute('font-size', "".concat(titleFontStyle[_fontStyle.FONT_STYLE_OPTION_FONT_SIZE], "px"));
|
|
173
209
|
title.setAttribute('font-weight', titleFontStyle[_fontStyle.FONT_STYLE_OPTION_BOLD] ? _fontStyle.FONT_STYLE_OPTION_BOLD : 'normal');
|
|
@@ -183,14 +219,14 @@ const generateDVItem = (config, _ref3) => {
|
|
|
183
219
|
|
|
184
220
|
if (config.title) {
|
|
185
221
|
title.appendChild(document.createTextNode(config.title));
|
|
186
|
-
|
|
222
|
+
svgWrapper.appendChild(title);
|
|
187
223
|
}
|
|
188
224
|
|
|
189
225
|
const subtitleFontStyle = (0, _fontStyle.mergeFontStyleWithDefault)(fontStyle && fontStyle[_fontStyle.FONT_STYLE_VISUALIZATION_SUBTITLE], _fontStyle.FONT_STYLE_VISUALIZATION_SUBTITLE);
|
|
190
226
|
const subtitle = document.createElementNS(svgNS, 'text');
|
|
191
227
|
subtitle.setAttribute('x', getXFromTextAlign(subtitleFontStyle[_fontStyle.FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
192
|
-
subtitle.setAttribute('y',
|
|
193
|
-
subtitle.setAttribute('dy',
|
|
228
|
+
subtitle.setAttribute('y', titleYPosition);
|
|
229
|
+
subtitle.setAttribute('dy', "".concat(subtitleFontStyle[_fontStyle.FONT_STYLE_OPTION_FONT_SIZE] + 4));
|
|
194
230
|
subtitle.setAttribute('text-anchor', getTextAnchorFromTextAlign(subtitleFontStyle[_fontStyle.FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
195
231
|
subtitle.setAttribute('font-size', "".concat(subtitleFontStyle[_fontStyle.FONT_STYLE_OPTION_FONT_SIZE], "px"));
|
|
196
232
|
subtitle.setAttribute('font-weight', subtitleFontStyle[_fontStyle.FONT_STYLE_OPTION_BOLD] ? _fontStyle.FONT_STYLE_OPTION_BOLD : 'normal');
|
|
@@ -206,20 +242,25 @@ const generateDVItem = (config, _ref3) => {
|
|
|
206
242
|
|
|
207
243
|
if (config.subtitle) {
|
|
208
244
|
subtitle.appendChild(document.createTextNode(config.subtitle));
|
|
209
|
-
|
|
245
|
+
svgWrapper.appendChild(subtitle);
|
|
210
246
|
}
|
|
211
247
|
|
|
212
|
-
|
|
248
|
+
svgContainer.appendChild(svgWrapper);
|
|
249
|
+
svgContainer.appendChild(generateValueSVG({
|
|
213
250
|
formattedValue: config.formattedValue,
|
|
214
251
|
subText: config.subText,
|
|
215
252
|
valueColor,
|
|
253
|
+
textColor: titleColor,
|
|
216
254
|
noData,
|
|
217
|
-
|
|
255
|
+
icon,
|
|
256
|
+
containerWidth: width,
|
|
257
|
+
containerHeight: height
|
|
218
258
|
}));
|
|
219
|
-
return
|
|
259
|
+
return svgContainer;
|
|
220
260
|
};
|
|
221
261
|
|
|
222
|
-
const shouldUseContrastColor =
|
|
262
|
+
const shouldUseContrastColor = function () {
|
|
263
|
+
let inputColor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
223
264
|
// based on https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
|
|
224
265
|
var color = inputColor.charAt(0) === '#' ? inputColor.substring(1, 7) : inputColor;
|
|
225
266
|
var r = parseInt(color.substring(0, 2), 16); // hexToR
|
|
@@ -246,7 +287,8 @@ function _default(config, parentEl, _ref4) {
|
|
|
246
287
|
legendSets,
|
|
247
288
|
fontStyle,
|
|
248
289
|
noData,
|
|
249
|
-
legendOptions
|
|
290
|
+
legendOptions,
|
|
291
|
+
icon
|
|
250
292
|
} = _ref4;
|
|
251
293
|
const legendSet = legendOptions && legendSets[0];
|
|
252
294
|
const legendColor = legendSet && (0, _legends.getColorByValueFromLegendSet)(legendSet, config.value);
|
|
@@ -264,26 +306,42 @@ function _default(config, parentEl, _ref4) {
|
|
|
264
306
|
parentEl.style.overflow = 'hidden';
|
|
265
307
|
parentEl.style.display = 'flex';
|
|
266
308
|
parentEl.style.justifyContent = 'center';
|
|
309
|
+
const parentElBBox = parentEl.getBoundingClientRect();
|
|
310
|
+
const width = parentElBBox.width;
|
|
311
|
+
const height = parentElBBox.height;
|
|
312
|
+
const svgContainer = document.createElementNS(svgNS, 'svg');
|
|
313
|
+
svgContainer.setAttribute('xmlns', svgNS);
|
|
314
|
+
svgContainer.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
|
315
|
+
svgContainer.setAttribute('width', dashboard ? '100%' : width);
|
|
316
|
+
svgContainer.setAttribute('height', dashboard ? '100%' : height);
|
|
317
|
+
svgContainer.setAttribute('data-test', 'visualization-container');
|
|
267
318
|
|
|
268
319
|
if (dashboard) {
|
|
269
320
|
parentEl.style.borderRadius = _ui.spacers.dp8;
|
|
270
321
|
return generateDashboardItem(config, {
|
|
322
|
+
svgContainer,
|
|
323
|
+
width,
|
|
324
|
+
height,
|
|
271
325
|
valueColor,
|
|
272
326
|
backgroundColor,
|
|
273
327
|
noData,
|
|
274
|
-
|
|
328
|
+
icon,
|
|
329
|
+
...(legendOptions.style === _legends.LEGEND_DISPLAY_STYLE_FILL && legendColor && shouldUseContrastColor(legendColor) ? {
|
|
275
330
|
titleColor: _ui.colors.white
|
|
276
331
|
} : {})
|
|
277
332
|
});
|
|
278
333
|
} else {
|
|
279
334
|
parentEl.style.height = "100%";
|
|
280
335
|
return generateDVItem(config, {
|
|
336
|
+
svgContainer,
|
|
337
|
+
width,
|
|
338
|
+
height,
|
|
281
339
|
valueColor,
|
|
282
340
|
backgroundColor,
|
|
283
341
|
titleColor,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
342
|
+
noData,
|
|
343
|
+
icon,
|
|
344
|
+
fontStyle
|
|
287
345
|
});
|
|
288
346
|
}
|
|
289
347
|
}
|
|
@@ -387,7 +387,8 @@ const CalculationModal = _ref => {
|
|
|
387
387
|
loading: isCreatingCalculation || isUpdatingCalculation || isSavingCalculation,
|
|
388
388
|
dataTest: "save-button"
|
|
389
389
|
}, i18n.t('Save calculation')))))), showDeletePrompt && /*#__PURE__*/React.createElement(Modal, {
|
|
390
|
-
small: true
|
|
390
|
+
small: true,
|
|
391
|
+
dataTest: "calculation-delete-modal"
|
|
391
392
|
}, /*#__PURE__*/React.createElement(ModalTitle, null, i18n.t('Delete calculation')), /*#__PURE__*/React.createElement(ModalContent, null, i18n.t('Are you sure you want to delete this calculation? It may be used by other visualizations.')), /*#__PURE__*/React.createElement(ModalActions, null, /*#__PURE__*/React.createElement(ButtonStrip, {
|
|
392
393
|
end: true
|
|
393
394
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
@@ -42,6 +42,7 @@ const DataElementOption = _ref => {
|
|
|
42
42
|
className: "jsx-".concat(styles.__hash) + " " + (listeners && listeners.className != null && listeners.className || attributes && attributes.className != null && attributes.className || "draggable-item")
|
|
43
43
|
}), /*#__PURE__*/React.createElement("div", {
|
|
44
44
|
onDoubleClick: () => onDoubleClick(data),
|
|
45
|
+
"data-test": "data-element-option",
|
|
45
46
|
className: "jsx-".concat(styles.__hash) + " " + "chip"
|
|
46
47
|
}, /*#__PURE__*/React.createElement("span", {
|
|
47
48
|
className: "jsx-".concat(styles.__hash) + " " + "icon"
|
|
@@ -51,11 +51,13 @@ const GroupSelector = _ref => {
|
|
|
51
51
|
}, defaultGroup ? /*#__PURE__*/React.createElement(SingleSelectOption, {
|
|
52
52
|
value: defaultGroup.id,
|
|
53
53
|
key: defaultGroup.id,
|
|
54
|
-
label: defaultGroup.getName()
|
|
54
|
+
label: defaultGroup.getName(),
|
|
55
|
+
dataTest: "data-element-group-select-option-".concat(defaultGroup.id)
|
|
55
56
|
}) : null, !loading ? groups.map(group => /*#__PURE__*/React.createElement(SingleSelectOption, {
|
|
56
57
|
value: group.id,
|
|
57
58
|
key: group.id,
|
|
58
|
-
label: group.name
|
|
59
|
+
label: group.name,
|
|
60
|
+
dataTest: "data-element-group-select-option-".concat(group.id)
|
|
59
61
|
})) : null), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
60
62
|
id: styles.__hash
|
|
61
63
|
}, styles));
|
|
@@ -83,7 +85,8 @@ const DisaggregationSelector = _ref2 => {
|
|
|
83
85
|
}, Object.entries(options).map(option => /*#__PURE__*/React.createElement(SingleSelectOption, {
|
|
84
86
|
value: option[0],
|
|
85
87
|
key: option[0],
|
|
86
|
-
label: option[1]
|
|
88
|
+
label: option[1],
|
|
89
|
+
dataTest: "data-element-disaggregation-select-option-".concat(option[0])
|
|
87
90
|
}))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
88
91
|
id: styles.__hash
|
|
89
92
|
}, styles));
|
|
@@ -13,7 +13,7 @@ export const LAST_DROPZONE_ID = 'lastdropzone';
|
|
|
13
13
|
export const FORMULA_BOX_ID = 'formulabox';
|
|
14
14
|
|
|
15
15
|
const Placeholder = () => /*#__PURE__*/React.createElement("div", {
|
|
16
|
-
"data-test":
|
|
16
|
+
"data-test": "placeholder",
|
|
17
17
|
className: "jsx-".concat(styles.__hash) + " " + "placeholder"
|
|
18
18
|
}, /*#__PURE__*/React.createElement(FormulaIcon, null), /*#__PURE__*/React.createElement("span", {
|
|
19
19
|
className: "jsx-".concat(styles.__hash) + " " + "help-text"
|
|
@@ -45,7 +45,7 @@ const FormulaField = _ref => {
|
|
|
45
45
|
className: "jsx-".concat(styles.__hash) + " " + "border"
|
|
46
46
|
}), /*#__PURE__*/React.createElement("div", {
|
|
47
47
|
ref: setLastDropzoneRef,
|
|
48
|
-
"data-test":
|
|
48
|
+
"data-test": "formula-field",
|
|
49
49
|
className: "jsx-".concat(styles.__hash) + " " + "formula-field"
|
|
50
50
|
}, loading && /*#__PURE__*/React.createElement(Center, null, /*#__PURE__*/React.createElement(CircularLoader, {
|
|
51
51
|
small: true
|
|
@@ -15,6 +15,7 @@ const MathOperatorSelector = _ref => {
|
|
|
15
15
|
}, /*#__PURE__*/React.createElement("h4", {
|
|
16
16
|
className: "jsx-".concat(styles.__hash) + " " + "sub-header"
|
|
17
17
|
}, i18n.t('Math operators')), /*#__PURE__*/React.createElement("div", {
|
|
18
|
+
"data-test": "operators-list",
|
|
18
19
|
className: "jsx-".concat(styles.__hash) + " " + "operators"
|
|
19
20
|
}, getOperators().map((_ref2, index) => {
|
|
20
21
|
let {
|
|
@@ -9,7 +9,7 @@ const LegendKey = _ref => {
|
|
|
9
9
|
legendSets
|
|
10
10
|
} = _ref;
|
|
11
11
|
return legendSets.length ? /*#__PURE__*/React.createElement("div", {
|
|
12
|
-
"data-test":
|
|
12
|
+
"data-test": "legend-key-container",
|
|
13
13
|
className: "jsx-".concat(styles.__hash) + " " + "container"
|
|
14
14
|
}, legendSets.map((legendSet, index) => /*#__PURE__*/React.createElement("div", {
|
|
15
15
|
key: legendSet.id,
|
|
@@ -1,62 +1,95 @@
|
|
|
1
1
|
import { colors, spacers } from '@dhis2/ui';
|
|
2
2
|
import { FONT_STYLE_VISUALIZATION_TITLE, FONT_STYLE_VISUALIZATION_SUBTITLE, FONT_STYLE_OPTION_FONT_SIZE, FONT_STYLE_OPTION_TEXT_COLOR, FONT_STYLE_OPTION_TEXT_ALIGN, FONT_STYLE_OPTION_ITALIC, FONT_STYLE_OPTION_BOLD, TEXT_ALIGN_LEFT, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER, mergeFontStyleWithDefault, defaultFontStyle } from '../../../../modules/fontStyle.js';
|
|
3
3
|
import { getColorByValueFromLegendSet, LEGEND_DISPLAY_STYLE_FILL } from '../../../../modules/legends.js';
|
|
4
|
-
const svgNS = 'http://www.w3.org/2000/svg';
|
|
4
|
+
const svgNS = 'http://www.w3.org/2000/svg'; // Compute text width before rendering
|
|
5
|
+
// Not exactly precise but close enough
|
|
6
|
+
|
|
7
|
+
const getTextWidth = (text, font) => {
|
|
8
|
+
const canvas = document.createElement('canvas');
|
|
9
|
+
const context = canvas.getContext('2d');
|
|
10
|
+
context.font = font;
|
|
11
|
+
return context.measureText(text).width;
|
|
12
|
+
};
|
|
5
13
|
|
|
6
14
|
const generateValueSVG = _ref => {
|
|
7
15
|
let {
|
|
8
16
|
formattedValue,
|
|
9
17
|
subText,
|
|
10
18
|
valueColor,
|
|
19
|
+
textColor,
|
|
20
|
+
icon,
|
|
11
21
|
noData,
|
|
12
|
-
|
|
22
|
+
containerWidth,
|
|
23
|
+
containerHeight
|
|
13
24
|
} = _ref;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
const ratio = containerHeight / containerWidth;
|
|
26
|
+
const iconSize = 300;
|
|
27
|
+
const iconPadding = 50;
|
|
28
|
+
const textSize = iconSize * 0.85;
|
|
29
|
+
const textWidth = getTextWidth(formattedValue, "".concat(textSize, "px Roboto"));
|
|
30
|
+
const subTextSize = 40;
|
|
31
|
+
const showIcon = icon && formattedValue !== noData.text;
|
|
32
|
+
let viewBoxWidth = textWidth;
|
|
33
|
+
|
|
34
|
+
if (showIcon) {
|
|
35
|
+
viewBoxWidth += iconSize + iconPadding;
|
|
21
36
|
}
|
|
22
37
|
|
|
38
|
+
const viewBoxHeight = viewBoxWidth * ratio;
|
|
39
|
+
const svgValue = document.createElementNS(svgNS, 'svg');
|
|
40
|
+
svgValue.setAttribute('viewBox', "0 0 ".concat(viewBoxWidth, " ").concat(viewBoxHeight));
|
|
41
|
+
svgValue.setAttribute('width', '95%');
|
|
42
|
+
svgValue.setAttribute('height', '95%');
|
|
43
|
+
svgValue.setAttribute('x', '50%');
|
|
44
|
+
svgValue.setAttribute('y', '50%');
|
|
45
|
+
svgValue.setAttribute('style', 'overflow: visible');
|
|
23
46
|
let fillColor = colors.grey900;
|
|
24
47
|
|
|
25
48
|
if (valueColor) {
|
|
26
49
|
fillColor = valueColor;
|
|
27
50
|
} else if (formattedValue === noData.text) {
|
|
28
51
|
fillColor = colors.grey600;
|
|
52
|
+
} // show icon if configured in maintenance app
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if (showIcon) {
|
|
56
|
+
// embed icon to allow changing color
|
|
57
|
+
// (elements with fill need to use "currentColor" for this to work)
|
|
58
|
+
const iconSvgNode = document.createElementNS(svgNS, 'svg');
|
|
59
|
+
iconSvgNode.setAttribute('width', iconSize);
|
|
60
|
+
iconSvgNode.setAttribute('height', iconSize);
|
|
61
|
+
iconSvgNode.setAttribute('viewBox', '0 0 48 48');
|
|
62
|
+
iconSvgNode.setAttribute('y', "-".concat(iconSize / 2));
|
|
63
|
+
iconSvgNode.setAttribute('x', "-".concat((iconSize + iconPadding + textWidth) / 2));
|
|
64
|
+
iconSvgNode.setAttribute('style', "color: ".concat(fillColor));
|
|
65
|
+
const parser = new DOMParser();
|
|
66
|
+
const svgIconDocument = parser.parseFromString(icon, 'image/svg+xml');
|
|
67
|
+
Array.from(svgIconDocument.documentElement.children).forEach(node => iconSvgNode.appendChild(node));
|
|
68
|
+
svgValue.appendChild(iconSvgNode);
|
|
29
69
|
}
|
|
30
70
|
|
|
31
71
|
const textNode = document.createElementNS(svgNS, 'text');
|
|
32
|
-
textNode.setAttribute('text-anchor', 'middle');
|
|
33
72
|
textNode.setAttribute('font-size', textSize);
|
|
34
73
|
textNode.setAttribute('font-weight', '300');
|
|
35
74
|
textNode.setAttribute('letter-spacing', '-5');
|
|
36
|
-
textNode.setAttribute('
|
|
75
|
+
textNode.setAttribute('text-anchor', 'middle');
|
|
76
|
+
textNode.setAttribute('x', showIcon ? "".concat((iconSize + iconPadding) / 2) : 0); // vertical align, "alignment-baseline: central" is not supported by Batik
|
|
77
|
+
|
|
78
|
+
textNode.setAttribute('y', '.35em');
|
|
37
79
|
textNode.setAttribute('fill', fillColor);
|
|
38
80
|
textNode.setAttribute('data-test', 'visualization-primary-value');
|
|
39
81
|
textNode.appendChild(document.createTextNode(formattedValue));
|
|
40
82
|
svgValue.appendChild(textNode);
|
|
41
83
|
|
|
42
84
|
if (subText) {
|
|
43
|
-
const svgSubText = document.createElementNS(svgNS, 'svg');
|
|
44
|
-
const subTextSize = 40;
|
|
45
|
-
svgSubText.setAttribute('viewBox', "0 -50 ".concat(textSize * 0.75 * formattedValue.length, " ").concat(textSize + 200));
|
|
46
|
-
|
|
47
|
-
if (y) {
|
|
48
|
-
svgSubText.setAttribute('y', y);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
85
|
const subTextNode = document.createElementNS(svgNS, 'text');
|
|
52
86
|
subTextNode.setAttribute('text-anchor', 'middle');
|
|
53
87
|
subTextNode.setAttribute('font-size', subTextSize);
|
|
54
|
-
subTextNode.setAttribute('
|
|
55
|
-
subTextNode.setAttribute('
|
|
56
|
-
subTextNode.setAttribute('fill',
|
|
88
|
+
subTextNode.setAttribute('y', iconSize / 2);
|
|
89
|
+
subTextNode.setAttribute('dy', subTextSize);
|
|
90
|
+
subTextNode.setAttribute('fill', textColor);
|
|
57
91
|
subTextNode.appendChild(document.createTextNode(subText));
|
|
58
|
-
|
|
59
|
-
svgValue.appendChild(svgSubText);
|
|
92
|
+
svgValue.appendChild(subTextNode);
|
|
60
93
|
}
|
|
61
94
|
|
|
62
95
|
return svgValue;
|
|
@@ -64,14 +97,28 @@ const generateValueSVG = _ref => {
|
|
|
64
97
|
|
|
65
98
|
const generateDashboardItem = (config, _ref2) => {
|
|
66
99
|
let {
|
|
100
|
+
svgContainer,
|
|
101
|
+
width,
|
|
102
|
+
height,
|
|
67
103
|
valueColor,
|
|
68
104
|
titleColor,
|
|
69
105
|
backgroundColor,
|
|
70
|
-
noData
|
|
106
|
+
noData,
|
|
107
|
+
icon
|
|
71
108
|
} = _ref2;
|
|
109
|
+
svgContainer.appendChild(generateValueSVG({
|
|
110
|
+
formattedValue: config.formattedValue,
|
|
111
|
+
subText: config.subText,
|
|
112
|
+
valueColor,
|
|
113
|
+
textColor: titleColor,
|
|
114
|
+
noData,
|
|
115
|
+
icon,
|
|
116
|
+
containerWidth: width,
|
|
117
|
+
containerHeight: height
|
|
118
|
+
}));
|
|
72
119
|
const container = document.createElement('div');
|
|
73
|
-
container.setAttribute('style', "display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; background-color:".concat(backgroundColor, ";"));
|
|
74
|
-
const titleStyle = "font-size: 12px; color: ".concat(titleColor || '#666', ";");
|
|
120
|
+
container.setAttribute('style', "display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; padding-top: 8px; ".concat(backgroundColor ? "background-color:".concat(backgroundColor, ";") : ''));
|
|
121
|
+
const titleStyle = "padding: 0 8px; text-align: center; font-size: 12px; color: ".concat(titleColor || '#666', ";");
|
|
75
122
|
const title = document.createElement('span');
|
|
76
123
|
title.setAttribute('style', titleStyle);
|
|
77
124
|
|
|
@@ -82,18 +129,12 @@ const generateDashboardItem = (config, _ref2) => {
|
|
|
82
129
|
|
|
83
130
|
if (config.subtitle) {
|
|
84
131
|
const subtitle = document.createElement('span');
|
|
85
|
-
subtitle.setAttribute('style', titleStyle + ' margin-top: 4px;
|
|
132
|
+
subtitle.setAttribute('style', titleStyle + ' margin-top: 4px;');
|
|
86
133
|
subtitle.appendChild(document.createTextNode(config.subtitle));
|
|
87
134
|
container.appendChild(subtitle);
|
|
88
135
|
}
|
|
89
136
|
|
|
90
|
-
container.appendChild(
|
|
91
|
-
formattedValue: config.formattedValue,
|
|
92
|
-
subText: config.subText,
|
|
93
|
-
valueColor,
|
|
94
|
-
noData,
|
|
95
|
-
y: 40
|
|
96
|
-
}));
|
|
137
|
+
container.appendChild(svgContainer);
|
|
97
138
|
return container;
|
|
98
139
|
};
|
|
99
140
|
|
|
@@ -127,37 +168,32 @@ const getXFromTextAlign = textAlign => {
|
|
|
127
168
|
|
|
128
169
|
const generateDVItem = (config, _ref3) => {
|
|
129
170
|
let {
|
|
171
|
+
svgContainer,
|
|
172
|
+
width,
|
|
173
|
+
height,
|
|
130
174
|
valueColor,
|
|
175
|
+
noData,
|
|
131
176
|
backgroundColor,
|
|
132
177
|
titleColor,
|
|
133
|
-
parentEl,
|
|
134
178
|
fontStyle,
|
|
135
|
-
|
|
179
|
+
icon
|
|
136
180
|
} = _ref3;
|
|
137
|
-
const parentElBBox = parentEl.getBoundingClientRect();
|
|
138
|
-
const width = parentElBBox.width;
|
|
139
|
-
const height = parentElBBox.height;
|
|
140
|
-
const svgNS = 'http://www.w3.org/2000/svg';
|
|
141
|
-
const svg = document.createElementNS(svgNS, 'svg');
|
|
142
|
-
svg.setAttribute('xmlns', svgNS);
|
|
143
|
-
svg.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
|
144
|
-
svg.setAttribute('width', width);
|
|
145
|
-
svg.setAttribute('height', height);
|
|
146
|
-
svg.setAttribute('data-test', 'visualization-container');
|
|
147
181
|
|
|
148
182
|
if (backgroundColor) {
|
|
149
|
-
|
|
183
|
+
svgContainer.setAttribute('style', "background-color: ".concat(backgroundColor, ";"));
|
|
150
184
|
const background = document.createElementNS(svgNS, 'rect');
|
|
151
185
|
background.setAttribute('width', '100%');
|
|
152
186
|
background.setAttribute('height', '100%');
|
|
153
187
|
background.setAttribute('fill', backgroundColor);
|
|
154
|
-
|
|
188
|
+
svgContainer.appendChild(background);
|
|
155
189
|
}
|
|
156
190
|
|
|
191
|
+
const svgWrapper = document.createElementNS(svgNS, 'svg');
|
|
157
192
|
const title = document.createElementNS(svgNS, 'text');
|
|
158
193
|
const titleFontStyle = mergeFontStyleWithDefault(fontStyle && fontStyle[FONT_STYLE_VISUALIZATION_TITLE], FONT_STYLE_VISUALIZATION_TITLE);
|
|
194
|
+
const titleYPosition = titleFontStyle[FONT_STYLE_OPTION_FONT_SIZE];
|
|
159
195
|
title.setAttribute('x', getXFromTextAlign(titleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
160
|
-
title.setAttribute('y',
|
|
196
|
+
title.setAttribute('y', titleYPosition);
|
|
161
197
|
title.setAttribute('text-anchor', getTextAnchorFromTextAlign(titleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
162
198
|
title.setAttribute('font-size', "".concat(titleFontStyle[FONT_STYLE_OPTION_FONT_SIZE], "px"));
|
|
163
199
|
title.setAttribute('font-weight', titleFontStyle[FONT_STYLE_OPTION_BOLD] ? FONT_STYLE_OPTION_BOLD : 'normal');
|
|
@@ -173,14 +209,14 @@ const generateDVItem = (config, _ref3) => {
|
|
|
173
209
|
|
|
174
210
|
if (config.title) {
|
|
175
211
|
title.appendChild(document.createTextNode(config.title));
|
|
176
|
-
|
|
212
|
+
svgWrapper.appendChild(title);
|
|
177
213
|
}
|
|
178
214
|
|
|
179
215
|
const subtitleFontStyle = mergeFontStyleWithDefault(fontStyle && fontStyle[FONT_STYLE_VISUALIZATION_SUBTITLE], FONT_STYLE_VISUALIZATION_SUBTITLE);
|
|
180
216
|
const subtitle = document.createElementNS(svgNS, 'text');
|
|
181
217
|
subtitle.setAttribute('x', getXFromTextAlign(subtitleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
182
|
-
subtitle.setAttribute('y',
|
|
183
|
-
subtitle.setAttribute('dy',
|
|
218
|
+
subtitle.setAttribute('y', titleYPosition);
|
|
219
|
+
subtitle.setAttribute('dy', "".concat(subtitleFontStyle[FONT_STYLE_OPTION_FONT_SIZE] + 4));
|
|
184
220
|
subtitle.setAttribute('text-anchor', getTextAnchorFromTextAlign(subtitleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN]));
|
|
185
221
|
subtitle.setAttribute('font-size', "".concat(subtitleFontStyle[FONT_STYLE_OPTION_FONT_SIZE], "px"));
|
|
186
222
|
subtitle.setAttribute('font-weight', subtitleFontStyle[FONT_STYLE_OPTION_BOLD] ? FONT_STYLE_OPTION_BOLD : 'normal');
|
|
@@ -196,20 +232,25 @@ const generateDVItem = (config, _ref3) => {
|
|
|
196
232
|
|
|
197
233
|
if (config.subtitle) {
|
|
198
234
|
subtitle.appendChild(document.createTextNode(config.subtitle));
|
|
199
|
-
|
|
235
|
+
svgWrapper.appendChild(subtitle);
|
|
200
236
|
}
|
|
201
237
|
|
|
202
|
-
|
|
238
|
+
svgContainer.appendChild(svgWrapper);
|
|
239
|
+
svgContainer.appendChild(generateValueSVG({
|
|
203
240
|
formattedValue: config.formattedValue,
|
|
204
241
|
subText: config.subText,
|
|
205
242
|
valueColor,
|
|
243
|
+
textColor: titleColor,
|
|
206
244
|
noData,
|
|
207
|
-
|
|
245
|
+
icon,
|
|
246
|
+
containerWidth: width,
|
|
247
|
+
containerHeight: height
|
|
208
248
|
}));
|
|
209
|
-
return
|
|
249
|
+
return svgContainer;
|
|
210
250
|
};
|
|
211
251
|
|
|
212
|
-
const shouldUseContrastColor =
|
|
252
|
+
const shouldUseContrastColor = function () {
|
|
253
|
+
let inputColor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
|
213
254
|
// based on https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
|
|
214
255
|
var color = inputColor.charAt(0) === '#' ? inputColor.substring(1, 7) : inputColor;
|
|
215
256
|
var r = parseInt(color.substring(0, 2), 16); // hexToR
|
|
@@ -236,7 +277,8 @@ export default function (config, parentEl, _ref4) {
|
|
|
236
277
|
legendSets,
|
|
237
278
|
fontStyle,
|
|
238
279
|
noData,
|
|
239
|
-
legendOptions
|
|
280
|
+
legendOptions,
|
|
281
|
+
icon
|
|
240
282
|
} = _ref4;
|
|
241
283
|
const legendSet = legendOptions && legendSets[0];
|
|
242
284
|
const legendColor = legendSet && getColorByValueFromLegendSet(legendSet, config.value);
|
|
@@ -254,26 +296,42 @@ export default function (config, parentEl, _ref4) {
|
|
|
254
296
|
parentEl.style.overflow = 'hidden';
|
|
255
297
|
parentEl.style.display = 'flex';
|
|
256
298
|
parentEl.style.justifyContent = 'center';
|
|
299
|
+
const parentElBBox = parentEl.getBoundingClientRect();
|
|
300
|
+
const width = parentElBBox.width;
|
|
301
|
+
const height = parentElBBox.height;
|
|
302
|
+
const svgContainer = document.createElementNS(svgNS, 'svg');
|
|
303
|
+
svgContainer.setAttribute('xmlns', svgNS);
|
|
304
|
+
svgContainer.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
|
305
|
+
svgContainer.setAttribute('width', dashboard ? '100%' : width);
|
|
306
|
+
svgContainer.setAttribute('height', dashboard ? '100%' : height);
|
|
307
|
+
svgContainer.setAttribute('data-test', 'visualization-container');
|
|
257
308
|
|
|
258
309
|
if (dashboard) {
|
|
259
310
|
parentEl.style.borderRadius = spacers.dp8;
|
|
260
311
|
return generateDashboardItem(config, {
|
|
312
|
+
svgContainer,
|
|
313
|
+
width,
|
|
314
|
+
height,
|
|
261
315
|
valueColor,
|
|
262
316
|
backgroundColor,
|
|
263
317
|
noData,
|
|
264
|
-
|
|
318
|
+
icon,
|
|
319
|
+
...(legendOptions.style === LEGEND_DISPLAY_STYLE_FILL && legendColor && shouldUseContrastColor(legendColor) ? {
|
|
265
320
|
titleColor: colors.white
|
|
266
321
|
} : {})
|
|
267
322
|
});
|
|
268
323
|
} else {
|
|
269
324
|
parentEl.style.height = "100%";
|
|
270
325
|
return generateDVItem(config, {
|
|
326
|
+
svgContainer,
|
|
327
|
+
width,
|
|
328
|
+
height,
|
|
271
329
|
valueColor,
|
|
272
330
|
backgroundColor,
|
|
273
331
|
titleColor,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
332
|
+
noData,
|
|
333
|
+
icon,
|
|
334
|
+
fontStyle
|
|
277
335
|
});
|
|
278
336
|
}
|
|
279
337
|
}
|