@datarailsshared/dr_renderer 1.3.30 → 1.3.32
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/package.json
CHANGED
@@ -0,0 +1,323 @@
|
|
1
|
+
const _ = require('lodash');
|
2
|
+
const helpers = require("../dr-renderer-helpers");
|
3
|
+
const {DrGaugeChart} = require("./dr_gauge_chart");
|
4
|
+
|
5
|
+
const GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT = {
|
6
|
+
gauge: {
|
7
|
+
background: "#fff",
|
8
|
+
startAngle: -90,
|
9
|
+
endAngle: 90,
|
10
|
+
thickness: 17,
|
11
|
+
tickLength: 0,
|
12
|
+
tickWidth: 0,
|
13
|
+
valueOffset: [0, 0, 0, 0],
|
14
|
+
offset: [12, 8, 12, 8],
|
15
|
+
goalIcon: '',
|
16
|
+
goalIconSize: [0, 0],
|
17
|
+
pivot: {
|
18
|
+
radius: 0,
|
19
|
+
color: "#808080",
|
20
|
+
},
|
21
|
+
colors: {
|
22
|
+
meta: "#333",
|
23
|
+
goal: "#4646CE",
|
24
|
+
},
|
25
|
+
},
|
26
|
+
goal: {
|
27
|
+
title: "Goal",
|
28
|
+
value: 1000000,
|
29
|
+
},
|
30
|
+
isAbsoluteValue: false,
|
31
|
+
segments: [
|
32
|
+
{
|
33
|
+
title: 'Completed',
|
34
|
+
count: 0,
|
35
|
+
isCompletionParameter: true,
|
36
|
+
color: '#037C5A',
|
37
|
+
},
|
38
|
+
{
|
39
|
+
title: 'Overdue',
|
40
|
+
count: 0,
|
41
|
+
color: '#BF1D30',
|
42
|
+
},
|
43
|
+
{
|
44
|
+
title: 'Pending',
|
45
|
+
count: 0,
|
46
|
+
color: '#FFA310',
|
47
|
+
},
|
48
|
+
{
|
49
|
+
title: 'Remaining',
|
50
|
+
count: 0,
|
51
|
+
color: '#F0F1F4',
|
52
|
+
}],
|
53
|
+
};
|
54
|
+
|
55
|
+
function DrGaugeCategoriesSummaryChart(pivotData, opts) {
|
56
|
+
this.completionPercentage = 0;
|
57
|
+
|
58
|
+
this.render = function () {
|
59
|
+
return DrGaugeChart.highchartsRenderer.ptCreateElementAndDraw(this.configChart(), opts);
|
60
|
+
};
|
61
|
+
|
62
|
+
this.createPlotBands = function (options) {
|
63
|
+
const {
|
64
|
+
segments,
|
65
|
+
goal: { value: goalValue },
|
66
|
+
gauge: { thickness },
|
67
|
+
} = options;
|
68
|
+
|
69
|
+
let bands = segments.map((item, index) => {
|
70
|
+
return {
|
71
|
+
from: item.from,
|
72
|
+
to: item.to,
|
73
|
+
color: item.color,
|
74
|
+
thickness: thickness,
|
75
|
+
title: item.title,
|
76
|
+
};
|
77
|
+
});
|
78
|
+
|
79
|
+
if (bands.length === 0) {
|
80
|
+
bands = [
|
81
|
+
{
|
82
|
+
from: 0,
|
83
|
+
to: 100,
|
84
|
+
color: '#F0F1F4',
|
85
|
+
thickness: thickness,
|
86
|
+
title: 'None',
|
87
|
+
}
|
88
|
+
]
|
89
|
+
}
|
90
|
+
|
91
|
+
// clamp last segment
|
92
|
+
bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
|
93
|
+
|
94
|
+
return bands;
|
95
|
+
};
|
96
|
+
|
97
|
+
this.getDefaultValueForChart = function (type, existing_options) {
|
98
|
+
return DrGaugeChart.highchartsRenderer.getDefaultValueForChart(type, existing_options);
|
99
|
+
};
|
100
|
+
|
101
|
+
this.mergeOptions = function (options) {
|
102
|
+
return helpers.mergeDeep(
|
103
|
+
JSON.parse(JSON.stringify(GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT)),
|
104
|
+
this.getDefaultValueForChart(DrGaugeChart.highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY),
|
105
|
+
options
|
106
|
+
);
|
107
|
+
};
|
108
|
+
|
109
|
+
this.prepareSegments = function (options) {
|
110
|
+
let segmentsSumAggregator = 0;
|
111
|
+
let segmentsOutput = [];
|
112
|
+
for (let i = 0; i < options.segments.length; i++) {
|
113
|
+
let currentSegment = options.segments[i];
|
114
|
+
segmentsOutput.push(
|
115
|
+
{
|
116
|
+
from: segmentsSumAggregator,
|
117
|
+
to: segmentsSumAggregator += currentSegment.count,
|
118
|
+
color: currentSegment.color,
|
119
|
+
name: currentSegment.title,
|
120
|
+
}
|
121
|
+
);
|
122
|
+
}
|
123
|
+
segmentsOutput = segmentsOutput.filter(
|
124
|
+
(segment) => {
|
125
|
+
return segment.from !== segment.to;
|
126
|
+
}
|
127
|
+
);
|
128
|
+
options.segments = segmentsOutput;
|
129
|
+
options.goal = {
|
130
|
+
title: '',
|
131
|
+
value: segmentsSumAggregator,
|
132
|
+
};
|
133
|
+
}
|
134
|
+
|
135
|
+
this.getCompletionPercentage = function (options) {
|
136
|
+
const countOfCompletedItems = options.segments.find(segment => !!segment.isCompletionParameter)?.count || 0;
|
137
|
+
const totalCount = options.segments.reduce((acc, segment) => acc + segment.count, 0);
|
138
|
+
if (totalCount === 0) {
|
139
|
+
return 0;
|
140
|
+
}
|
141
|
+
return Math.round(countOfCompletedItems / totalCount * 100);
|
142
|
+
}
|
143
|
+
|
144
|
+
this.clampValueToPane = function (value, max = this.max, min = this.min) {
|
145
|
+
const correction = Math.abs(max - min) * 0.02;
|
146
|
+
return helpers.clamp(min - correction, value, max + correction);
|
147
|
+
};
|
148
|
+
|
149
|
+
|
150
|
+
this.formatValueLabel = function (value, options) {
|
151
|
+
return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${
|
152
|
+
options.label.font_size
|
153
|
+
}px;">
|
154
|
+
<span style="font-weight: 600; font-size: 1.5em; line-height: 1; color: ${options.label.font_color}">
|
155
|
+
${this.completionPercentage}%
|
156
|
+
</span>
|
157
|
+
<span style="font-weight: 600; font-size: 0.75em; line-height: 1; color: ${
|
158
|
+
options.gauge.colors.meta
|
159
|
+
};">Completed</span>
|
160
|
+
</span>`;
|
161
|
+
};
|
162
|
+
|
163
|
+
this.clampValueToPane = function (value, max = this.max, min = this.min) {
|
164
|
+
const correction = Math.abs(max - min) * 0.02;
|
165
|
+
return helpers.clamp(min - correction, value, max + correction);
|
166
|
+
};
|
167
|
+
|
168
|
+
this.createValueLabel = function (chart, options) {
|
169
|
+
const label = chart.renderer.text(this.formatValueLabel(this.value, options), 0, 0, true).add().toFront();
|
170
|
+
|
171
|
+
helpers.removeSVGTextCorrection(label, "yCorr");
|
172
|
+
|
173
|
+
label.attr(DrGaugeChart.getValueLabelPosition(chart, options)).css({
|
174
|
+
transform: "translateX(-50%) translateY(-100%)",
|
175
|
+
});
|
176
|
+
|
177
|
+
return label;
|
178
|
+
};
|
179
|
+
|
180
|
+
|
181
|
+
this.getPaneDimensions = function (chart, options) {
|
182
|
+
const {renderer} = chart;
|
183
|
+
const {offset} = options.gauge;
|
184
|
+
|
185
|
+
const offsetBottom = options.gauge.valueOffset[0] + options.gauge.valueOffset[2] + offset[2];
|
186
|
+
|
187
|
+
const radiuses = [chart.chartWidth / 2 - Math.max(offset[1], offset[3]), chart.chartHeight - offsetBottom - offset[0]];
|
188
|
+
|
189
|
+
radiuses.push((chart.chartWidth / 2 - offset[1]) / Math.sin(Math.abs(Math.PI / 2)));
|
190
|
+
radiuses.push((chart.chartHeight - offsetBottom - offset[0]) / Math.cos(Math.abs(Math.PI / 2)));
|
191
|
+
|
192
|
+
return {
|
193
|
+
radius: Math.min(...radiuses),
|
194
|
+
center: [chart.chartWidth / 2, chart.chartHeight - offsetBottom],
|
195
|
+
};
|
196
|
+
};
|
197
|
+
|
198
|
+
this.setPane = function (chart, options) {
|
199
|
+
const {radius, center} = this.getPaneDimensions(chart, options);
|
200
|
+
chart.pane[0].options.size = 2 * radius;
|
201
|
+
chart.pane[0].options.center = center;
|
202
|
+
chart.yAxis[0].options.plotBands.forEach((band) => {
|
203
|
+
band.outerRadius = radius - (options.gauge.tickLength - options.gauge.thickness) / 2;
|
204
|
+
});
|
205
|
+
};
|
206
|
+
|
207
|
+
this.setCustomElements = function (chart, options) {
|
208
|
+
chart.label = this.createValueLabel(chart, options);
|
209
|
+
chart.startBorder = DrGaugeChart.createBorder(chart, options, "start");
|
210
|
+
chart.endBorder = DrGaugeChart.createBorder(chart, options, "end");
|
211
|
+
};
|
212
|
+
|
213
|
+
this.updateCustomElements = function (chart, options) {
|
214
|
+
chart.startBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "start"));
|
215
|
+
chart.endBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "end"));
|
216
|
+
chart.label.attr(DrGaugeChart.getValueLabelPosition(chart, options));
|
217
|
+
};
|
218
|
+
|
219
|
+
this.moveCustomElementsToFront = function (chart) {
|
220
|
+
chart.startBorder.toFront();
|
221
|
+
chart.endBorder.toFront();
|
222
|
+
};
|
223
|
+
|
224
|
+
this.configChart = function () {
|
225
|
+
return {
|
226
|
+
title: {
|
227
|
+
text: null,
|
228
|
+
},
|
229
|
+
subtitle: null,
|
230
|
+
exporting: {
|
231
|
+
allowHTML: true,
|
232
|
+
},
|
233
|
+
tooltip: {
|
234
|
+
enabled: false,
|
235
|
+
},
|
236
|
+
chart: {
|
237
|
+
type: "gauge",
|
238
|
+
backgroundColor: this.options.gauge.background,
|
239
|
+
plotBackgroundColor: null,
|
240
|
+
plotBackgroundImage: null,
|
241
|
+
plotBorderWidth: 0,
|
242
|
+
plotShadow: false,
|
243
|
+
events: {
|
244
|
+
render: ({target: chart}) => {
|
245
|
+
this.moveCustomElementsToFront(chart);
|
246
|
+
},
|
247
|
+
beforeRedraw: ({target: chart}) => {
|
248
|
+
this.setPane(chart, this.options);
|
249
|
+
this.updateCustomElements(chart, this.options);
|
250
|
+
},
|
251
|
+
beforeRender: ({target: chart}) => {
|
252
|
+
this.setPane(chart, this.options);
|
253
|
+
this.setCustomElements(chart, this.options);
|
254
|
+
}
|
255
|
+
},
|
256
|
+
margin: [0, 0, 0, 0],
|
257
|
+
spacing: [0, 0, 0, 0],
|
258
|
+
},
|
259
|
+
|
260
|
+
pane: {
|
261
|
+
startAngle: -90,
|
262
|
+
endAngle: 90,
|
263
|
+
background: null,
|
264
|
+
center: [0, 0],
|
265
|
+
},
|
266
|
+
|
267
|
+
// the value axis
|
268
|
+
yAxis: {
|
269
|
+
min: this.min,
|
270
|
+
max: this.max,
|
271
|
+
tickPositions: this.ticks,
|
272
|
+
tickPosition: "inside",
|
273
|
+
tickColor: this.options.gauge.background,
|
274
|
+
tickLength: this.options.gauge.tickLength,
|
275
|
+
tickWidth: this.options.gauge.tickWidth,
|
276
|
+
minorTickInterval: null,
|
277
|
+
labels: {
|
278
|
+
enabled: false
|
279
|
+
},
|
280
|
+
lineWidth: 0,
|
281
|
+
plotBands: this.plotBands,
|
282
|
+
},
|
283
|
+
|
284
|
+
series: [
|
285
|
+
{
|
286
|
+
name: null,
|
287
|
+
data: [this.clampValueToPane(this.value)],
|
288
|
+
dataLabels: [
|
289
|
+
{
|
290
|
+
enabled: false,
|
291
|
+
},
|
292
|
+
],
|
293
|
+
dial: {
|
294
|
+
radius: "0",
|
295
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
296
|
+
baseWidth: 2 * this.options.gauge.pivot.radius,
|
297
|
+
baseLength: "0%",
|
298
|
+
rearLength: "0%",
|
299
|
+
},
|
300
|
+
pivot: {
|
301
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
302
|
+
radius: this.options.gauge.pivot.radius,
|
303
|
+
},
|
304
|
+
},
|
305
|
+
],
|
306
|
+
};
|
307
|
+
};
|
308
|
+
|
309
|
+
|
310
|
+
this.options = this.mergeOptions(opts.chartOptions);
|
311
|
+
this.completionPercentage = this.getCompletionPercentage(this.options);
|
312
|
+
this.prepareSegments(this.options);
|
313
|
+
this.aggregation = pivotData.getAggregator([], []);
|
314
|
+
this.format = this.aggregation.widget_values_format;
|
315
|
+
this.goal = this.options.goal;
|
316
|
+
this.plotBands = this.createPlotBands(this.options);
|
317
|
+
this.ticks = DrGaugeChart.createTicks(this.plotBands, this.options);
|
318
|
+
this.value = DrGaugeChart.getValue(pivotData, opts);
|
319
|
+
this.max = this.ticks[this.ticks.length - 1];
|
320
|
+
this.min = this.ticks[0];
|
321
|
+
}
|
322
|
+
|
323
|
+
module.exports = {DrGaugeCategoriesSummaryChart, GAUGE_CATEGORIES_SUMMARY_OPTIONS_DEFAULT};
|
@@ -84,34 +84,7 @@ function DrGaugeChart(pivotData, opts) {
|
|
84
84
|
return (value - min) < (max - min) / 2;
|
85
85
|
};
|
86
86
|
|
87
|
-
this.
|
88
|
-
const {
|
89
|
-
segments,
|
90
|
-
isAbsoluteValue,
|
91
|
-
goal: { value: goalValue },
|
92
|
-
gauge: { thickness },
|
93
|
-
} = options;
|
94
|
-
|
95
|
-
const bands = segments.map((item, index) => {
|
96
|
-
const itemFrom = !!index ? item.from - 1 : item.from;
|
97
|
-
return {
|
98
|
-
from: isAbsoluteValue ? itemFrom : (itemFrom * goalValue) / 100,
|
99
|
-
to: isAbsoluteValue ? item.to : (item.to * goalValue) / 100,
|
100
|
-
color: item.color,
|
101
|
-
thickness: thickness,
|
102
|
-
title: item.title,
|
103
|
-
};
|
104
|
-
});
|
105
|
-
|
106
|
-
// clamp last segment
|
107
|
-
bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
|
108
|
-
|
109
|
-
return bands;
|
110
|
-
};
|
111
|
-
|
112
|
-
this.createTicks = function (plotBands, options) {
|
113
|
-
return _.uniq([plotBands[0].from || 0, ...plotBands.map((b) => b.to), options.goal.value]).sort((a, b) => a - b);
|
114
|
-
};
|
87
|
+
this.createTicks = DrGaugeChart.createTicks;
|
115
88
|
|
116
89
|
this.mergeOptions = function (options) {
|
117
90
|
return helpers.mergeDeep(
|
@@ -142,21 +115,17 @@ function DrGaugeChart(pivotData, opts) {
|
|
142
115
|
};
|
143
116
|
|
144
117
|
this.formatValueLabel = function (value, options) {
|
145
|
-
return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${
|
146
|
-
|
147
|
-
}px;">
|
118
|
+
return `<span style="display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: ${options.label.font_size
|
119
|
+
}px;">
|
148
120
|
<span style="font-weight: 600; font-size: 1.3em; line-height: 1; color: ${options.label.font_color}">
|
149
121
|
${this.formatValue(value)}
|
150
|
-
${
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
: ""
|
156
|
-
}
|
122
|
+
${options.label.show_percentage_in_value
|
123
|
+
? `<span style="font-size: 0.5833em; font-weight: 600; color: ${options.gauge.colors.meta
|
124
|
+
};">(${this.toPercent(value)})</span>`
|
125
|
+
: ""
|
126
|
+
}
|
157
127
|
</span>
|
158
|
-
<span style="font-weight: 500; font-size: 0.875em; line-height: 1; color: ${
|
159
|
-
options.gauge.colors.meta
|
128
|
+
<span style="font-weight: 500; font-size: 0.875em; line-height: 1; color: ${options.gauge.colors.meta
|
160
129
|
};">Current status</span>
|
161
130
|
</span>`;
|
162
131
|
};
|
@@ -176,8 +145,8 @@ function DrGaugeChart(pivotData, opts) {
|
|
176
145
|
: "";
|
177
146
|
const percentage = options.label.show_percentage_in_segments
|
178
147
|
? `<span style="font-size: 0.75em; color: ${options.gauge.colors.meta}; font-weight: 400;">(${this.toPercent(
|
179
|
-
|
180
|
-
|
148
|
+
value
|
149
|
+
)})</span>`
|
181
150
|
: "";
|
182
151
|
|
183
152
|
return `<span style="
|
@@ -194,67 +163,6 @@ function DrGaugeChart(pivotData, opts) {
|
|
194
163
|
</span>`;
|
195
164
|
};
|
196
165
|
|
197
|
-
this.getValue = function (pivotData, opts) {
|
198
|
-
const lineSeries = this.ptCreateBasicLineSeries(pivotData, null, true, null, null, opts, {});
|
199
|
-
|
200
|
-
let total = _.flatten(lineSeries
|
201
|
-
.map((s) => s.data.map((v) => v))
|
202
|
-
.filter((v) => !_.isNaN(v))
|
203
|
-
);
|
204
|
-
|
205
|
-
let aggfunc = (a, b) => a + b;
|
206
|
-
let base = 0;
|
207
|
-
let singleValueAgg = this.getSingleValueAgg(
|
208
|
-
{
|
209
|
-
value: {
|
210
|
-
value: "Sum",
|
211
|
-
},
|
212
|
-
},
|
213
|
-
aggfunc,
|
214
|
-
base
|
215
|
-
);
|
216
|
-
|
217
|
-
aggfunc = singleValueAgg.aggfunc;
|
218
|
-
base = singleValueAgg.base;
|
219
|
-
|
220
|
-
const aggregator = pivotData.getAggregator([], []);
|
221
|
-
const value = total.length > 0 ? total.reduce(aggfunc, base) : aggregator.value();
|
222
|
-
|
223
|
-
return value;
|
224
|
-
};
|
225
|
-
|
226
|
-
this.getBorderPosition = function (chart, options, position) {
|
227
|
-
const { center, size } = chart.pane[0].options;
|
228
|
-
const {
|
229
|
-
gauge: { tickLength, tickWidth },
|
230
|
-
} = options;
|
231
|
-
|
232
|
-
return {
|
233
|
-
x: position === "start" ? center[0] - size / 2 + tickLength / 2 : center[0] + size / 2 - tickLength / 2,
|
234
|
-
y: center[1] + tickWidth / 2,
|
235
|
-
};
|
236
|
-
};
|
237
|
-
|
238
|
-
this.createBorder = function (chart, options, position) {
|
239
|
-
return chart.renderer
|
240
|
-
.arc(
|
241
|
-
Object.assign(this.getBorderPosition(chart, options, position), {
|
242
|
-
r: options.gauge.thickness / 2,
|
243
|
-
innerR: 0,
|
244
|
-
start: 0,
|
245
|
-
end: Math.PI,
|
246
|
-
})
|
247
|
-
)
|
248
|
-
.attr({
|
249
|
-
fill:
|
250
|
-
position === "start"
|
251
|
-
? chart.yAxis[0].options.plotBands[0].color
|
252
|
-
: chart.yAxis[0].options.plotBands.slice(-1)[0].color,
|
253
|
-
})
|
254
|
-
.add()
|
255
|
-
.toFront();
|
256
|
-
};
|
257
|
-
|
258
166
|
this.getGoalIconPosition = function (chart, options) {
|
259
167
|
const { center, size } = chart.pane[0].options;
|
260
168
|
const radius = size / 2;
|
@@ -277,20 +185,12 @@ function DrGaugeChart(pivotData, opts) {
|
|
277
185
|
.toFront();
|
278
186
|
};
|
279
187
|
|
280
|
-
this.getValueLabelPosition = function (chart, options) {
|
281
|
-
const { center } = chart.pane[0].options;
|
282
|
-
return {
|
283
|
-
x: center[0],
|
284
|
-
y: center[1] + options.gauge.valueOffset[0],
|
285
|
-
};
|
286
|
-
};
|
287
|
-
|
288
188
|
this.createValueLabel = function (chart, options) {
|
289
189
|
const label = chart.renderer.text(this.formatValueLabel(this.value, options), 0, 0, true).add().toFront();
|
290
190
|
|
291
191
|
helpers.removeSVGTextCorrection(label, "yCorr");
|
292
192
|
|
293
|
-
label.attr(
|
193
|
+
label.attr(DrGaugeChart.getValueLabelPosition(chart, options)).css({
|
294
194
|
transform: "translateX(-50%)",
|
295
195
|
});
|
296
196
|
|
@@ -314,12 +214,12 @@ function DrGaugeChart(pivotData, opts) {
|
|
314
214
|
// depends on label width
|
315
215
|
radiuses.push(
|
316
216
|
(chart.chartWidth / 2 - label.bBox.width - Math.max(offset[1], offset[3])) /
|
317
|
-
|
217
|
+
Math.sin(Math.abs(Math.PI / 2 - angle))
|
318
218
|
);
|
319
219
|
// depends on label height
|
320
220
|
radiuses.push(
|
321
221
|
(chart.chartHeight - offsetBottom - label.bBox.height / 2 - offset[0]) /
|
322
|
-
|
222
|
+
Math.cos(Math.abs(Math.PI / 2 - angle))
|
323
223
|
);
|
324
224
|
label.destroy();
|
325
225
|
});
|
@@ -361,7 +261,7 @@ function DrGaugeChart(pivotData, opts) {
|
|
361
261
|
|
362
262
|
// If goal is hidden, we need to always show goal and hide the closest visible label before it
|
363
263
|
this.updateLabelsWhenGoalIsHidden(ticks, options);
|
364
|
-
|
264
|
+
|
365
265
|
};
|
366
266
|
|
367
267
|
this.updateLabelsWhenGoalIsHidden = function (ticks, options) {
|
@@ -430,8 +330,7 @@ function DrGaugeChart(pivotData, opts) {
|
|
430
330
|
// goal tooltip if lebels are hidden
|
431
331
|
if (tick.pos === options.goal.value && !options.label.show) {
|
432
332
|
drTooltip.add(
|
433
|
-
`${
|
434
|
-
options.label.show_goal_name ? options.goal.title || "" : ""
|
333
|
+
`${options.label.show_goal_name ? options.goal.title || "" : ""
|
435
334
|
}<span style="font-weight: 600">${this.formatValue(options.goal.value)}</span>`,
|
436
335
|
chart.goalIcon.element,
|
437
336
|
{
|
@@ -461,16 +360,41 @@ function DrGaugeChart(pivotData, opts) {
|
|
461
360
|
|
462
361
|
this.setCustomElements = function (chart, options) {
|
463
362
|
chart.label = this.createValueLabel(chart, options);
|
464
|
-
chart.startBorder =
|
465
|
-
chart.endBorder =
|
363
|
+
chart.startBorder = DrGaugeChart.createBorder(chart, options, "start");
|
364
|
+
chart.endBorder = DrGaugeChart.createBorder(chart, options, "end");
|
466
365
|
chart.goalIcon = this.createGoalIcon(chart, options);
|
467
366
|
};
|
468
367
|
|
469
368
|
this.updateCustomElements = function (chart, options) {
|
470
|
-
chart.startBorder.attr(
|
471
|
-
chart.endBorder.attr(
|
369
|
+
chart.startBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "start"));
|
370
|
+
chart.endBorder.attr(DrGaugeChart.getBorderPosition(chart, options, "end"));
|
472
371
|
chart.goalIcon.attr(this.getGoalIconPosition(chart, options));
|
473
|
-
chart.label.attr(
|
372
|
+
chart.label.attr(DrGaugeChart.getValueLabelPosition(chart, options));
|
373
|
+
};
|
374
|
+
|
375
|
+
this.createPlotBands = function (options) {
|
376
|
+
const {
|
377
|
+
segments,
|
378
|
+
isAbsoluteValue,
|
379
|
+
goal: { value: goalValue },
|
380
|
+
gauge: { thickness },
|
381
|
+
} = options;
|
382
|
+
|
383
|
+
const bands = segments.map((item, index) => {
|
384
|
+
const itemFrom = !!index ? item.from - 1 : item.from;
|
385
|
+
return {
|
386
|
+
from: isAbsoluteValue ? itemFrom : (itemFrom * goalValue) / 100,
|
387
|
+
to: isAbsoluteValue ? item.to : (item.to * goalValue) / 100,
|
388
|
+
color: item.color,
|
389
|
+
thickness: thickness,
|
390
|
+
title: item.title,
|
391
|
+
};
|
392
|
+
});
|
393
|
+
|
394
|
+
// clamp last segment
|
395
|
+
bands[bands.length - 1].to = Math.max(bands[bands.length - 1].to, goalValue);
|
396
|
+
|
397
|
+
return bands;
|
474
398
|
};
|
475
399
|
|
476
400
|
this.moveCustomElementsToFront = function (chart) {
|
@@ -522,6 +446,7 @@ function DrGaugeChart(pivotData, opts) {
|
|
522
446
|
},
|
523
447
|
margin: [0, 0, 0, 0],
|
524
448
|
spacing: [0, 0, 0, 0],
|
449
|
+
animation: !DrGaugeChart.highchartsRenderer.chartAnimationsDisabled(),
|
525
450
|
},
|
526
451
|
|
527
452
|
pane: {
|
@@ -581,6 +506,7 @@ function DrGaugeChart(pivotData, opts) {
|
|
581
506
|
backgroundColor: this.options.gauge.pivot.color,
|
582
507
|
radius: this.options.gauge.pivot.radius,
|
583
508
|
},
|
509
|
+
animation: !DrGaugeChart.highchartsRenderer.chartAnimationsDisabled(),
|
584
510
|
},
|
585
511
|
],
|
586
512
|
};
|
@@ -593,9 +519,98 @@ function DrGaugeChart(pivotData, opts) {
|
|
593
519
|
this.goal = this.options.goal;
|
594
520
|
this.plotBands = this.createPlotBands(this.options);
|
595
521
|
this.ticks = this.createTicks(this.plotBands, this.options);
|
596
|
-
this.value =
|
522
|
+
this.value = DrGaugeChart.getValue(pivotData, opts);
|
597
523
|
this.max = this.ticks[this.ticks.length - 1];
|
598
524
|
this.min = this.ticks[0];
|
599
525
|
}
|
600
526
|
|
527
|
+
DrGaugeChart.createTicks = function (plotBands, options) {
|
528
|
+
return _.uniq([plotBands[0].from || 0, ...plotBands.map((b) => b.to), options.goal.value]).sort((a, b) => a - b);
|
529
|
+
};
|
530
|
+
|
531
|
+
DrGaugeChart.getBorderPosition = function (chart, options, position) {
|
532
|
+
const { center, size } = chart.pane[0].options;
|
533
|
+
const {
|
534
|
+
gauge: { tickLength, tickWidth },
|
535
|
+
} = options;
|
536
|
+
|
537
|
+
return {
|
538
|
+
x: position === "start" ? center[0] - size / 2 + tickLength / 2 : center[0] + size / 2 - tickLength / 2,
|
539
|
+
y: center[1] + tickWidth / 2,
|
540
|
+
};
|
541
|
+
};
|
542
|
+
|
543
|
+
DrGaugeChart.createBorder = function (chart, options, position) {
|
544
|
+
return chart.renderer
|
545
|
+
.arc(
|
546
|
+
Object.assign(DrGaugeChart.getBorderPosition(chart, options, position), {
|
547
|
+
r: options.gauge.thickness / 2,
|
548
|
+
innerR: 0,
|
549
|
+
start: 0,
|
550
|
+
end: Math.PI,
|
551
|
+
})
|
552
|
+
)
|
553
|
+
.attr({
|
554
|
+
fill:
|
555
|
+
position === "start"
|
556
|
+
? chart.yAxis[0].options.plotBands[0].color
|
557
|
+
: chart.yAxis[0].options.plotBands.slice(-1)[0].color,
|
558
|
+
})
|
559
|
+
.add()
|
560
|
+
.toFront();
|
561
|
+
};
|
562
|
+
|
563
|
+
DrGaugeChart.getValueLabelPosition = function (chart, options) {
|
564
|
+
const { center } = chart.pane[0].options;
|
565
|
+
return {
|
566
|
+
x: center[0],
|
567
|
+
y: center[1] + options.gauge.valueOffset[0],
|
568
|
+
};
|
569
|
+
};
|
570
|
+
|
571
|
+
DrGaugeChart.ptCreateBasicLineSeries = function (pivotData, colors, onlyNumbers, isUniqueVals, additionOptions, opts, chartOptions) {
|
572
|
+
return DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries(
|
573
|
+
pivotData,
|
574
|
+
colors,
|
575
|
+
onlyNumbers,
|
576
|
+
isUniqueVals,
|
577
|
+
additionOptions,
|
578
|
+
opts,
|
579
|
+
chartOptions
|
580
|
+
);
|
581
|
+
};
|
582
|
+
|
583
|
+
DrGaugeChart.getSingleValueAgg = function (opts, aggfunc, base) {
|
584
|
+
return DrGaugeChart.highchartsRenderer.getSingleValueAgg(opts, aggfunc, base);
|
585
|
+
};
|
586
|
+
|
587
|
+
DrGaugeChart.getValue = function (pivotData, opts) {
|
588
|
+
const lineSeries = DrGaugeChart.ptCreateBasicLineSeries(pivotData, null, true, null, null, opts, {});
|
589
|
+
|
590
|
+
let total = _.flatten(lineSeries
|
591
|
+
.map((s) => s.data.map((v) => v))
|
592
|
+
.filter((v) => !_.isNaN(v))
|
593
|
+
);
|
594
|
+
|
595
|
+
let aggfunc = (a, b) => a + b;
|
596
|
+
let base = 0;
|
597
|
+
let singleValueAgg = DrGaugeChart.getSingleValueAgg(
|
598
|
+
{
|
599
|
+
value: {
|
600
|
+
value: "Sum",
|
601
|
+
},
|
602
|
+
},
|
603
|
+
aggfunc,
|
604
|
+
base
|
605
|
+
);
|
606
|
+
|
607
|
+
aggfunc = singleValueAgg.aggfunc;
|
608
|
+
base = singleValueAgg.base;
|
609
|
+
|
610
|
+
const aggregator = pivotData.getAggregator([], []);
|
611
|
+
const value = total.length > 0 ? total.reduce(aggfunc, base) : aggregator.value();
|
612
|
+
|
613
|
+
return value;
|
614
|
+
};
|
615
|
+
|
601
616
|
module.exports = { DrGaugeChart, GAUGE_OPTIONS_DEFAULT };
|
@@ -2,6 +2,7 @@ const helpers = require('./dr-renderer-helpers');
|
|
2
2
|
const { DrGaugeChart, GAUGE_OPTIONS_DEFAULT } = require('./charts/dr_gauge_chart');
|
3
3
|
const { DrDonutChart } = require('./charts/dr_donut_chart');
|
4
4
|
const seriesPointStylesHelper= require('./seriesPointStyles-helper');
|
5
|
+
const {DrGaugeCategoriesSummaryChart} = require("./charts/dr_gauge_categories_summary_chart");
|
5
6
|
|
6
7
|
const mobileBrowserRegex = new RegExp([
|
7
8
|
'(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)',
|
@@ -59,6 +60,7 @@ const CHART_TYPES = {
|
|
59
60
|
GAUGE_SOLID_CHART: 'gauge-solid-chart',
|
60
61
|
GAUGE_CHART: 'gauge-chart',
|
61
62
|
GAUGE_CHART_ENHANCED: 'gauge-chart-enhanced',
|
63
|
+
GAUGE_CHART_CATEGORIES_SUMMARY: 'gauge-chart-categories-summary',
|
62
64
|
KPI_WIDGET: 'kpi-widget',
|
63
65
|
TEXT_WIDGET: 'text-widget',
|
64
66
|
WATERFALL_BREAKDOWN: 'waterfall-chart-breakdown',
|
@@ -2274,6 +2276,10 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
|
|
2274
2276
|
return new DrGaugeChart(pivotData, opts).render();
|
2275
2277
|
};
|
2276
2278
|
|
2279
|
+
highchartsRenderer.ptRenderGaugeCategoriesSummary = (pivotData, opts) => {
|
2280
|
+
return new DrGaugeCategoriesSummaryChart(pivotData, opts).render();
|
2281
|
+
};
|
2282
|
+
|
2277
2283
|
highchartsRenderer.ptRenderKpi = function (pivotData, opts) {
|
2278
2284
|
var chartOptions = {};
|
2279
2285
|
|
@@ -7692,6 +7698,14 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
|
|
7692
7698
|
legendName: 'Data Series',
|
7693
7699
|
startedMessage: 'To get started, drag one field to the value section. Best practice: Drag one field to the filter section, and filter as required.',
|
7694
7700
|
},
|
7701
|
+
[highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY]: {
|
7702
|
+
name: 'Gauge chart categories summary',
|
7703
|
+
label: 'Gauge',
|
7704
|
+
title: 'Gauge chart showing summary sections for different categories.',
|
7705
|
+
axisName: 'X-Axis',
|
7706
|
+
legendName: 'Data Series',
|
7707
|
+
startedMessage: '',
|
7708
|
+
},
|
7695
7709
|
'kpi-widget': {
|
7696
7710
|
name: 'Kpi ',
|
7697
7711
|
label: 'KPI',
|
@@ -8085,6 +8099,24 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
|
|
8085
8099
|
},
|
8086
8100
|
]
|
8087
8101
|
},
|
8102
|
+
{
|
8103
|
+
type: 'gauge-categories-summary',
|
8104
|
+
name: 'Gauge Categories Summary',
|
8105
|
+
class: 'google-visualization-charteditor-mini-gauge',
|
8106
|
+
subtypes: [
|
8107
|
+
{
|
8108
|
+
type: highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY,
|
8109
|
+
name: highchartsRenderer.chartsTypesInfo[highchartsRenderer.CHART_TYPES.GAUGE_CHART_CATEGORIES_SUMMARY].name,
|
8110
|
+
class: 'google-visualization-charteditor-thumbs-gauge-solid',
|
8111
|
+
render: highchartsRenderer.ptRenderGaugeCategoriesSummary,
|
8112
|
+
suboptions: [
|
8113
|
+
highchartsRenderer.suboptions["label_gauge"],
|
8114
|
+
highchartsRenderer.suboptions["tooltips_gauge"],
|
8115
|
+
highchartsRenderer.suboptions["subtitle"]
|
8116
|
+
]
|
8117
|
+
},
|
8118
|
+
]
|
8119
|
+
},
|
8088
8120
|
{
|
8089
8121
|
type: 'kpi',
|
8090
8122
|
name: 'KPI',
|
@@ -9900,6 +9932,10 @@ let getHighchartsRenderer = function ($, document, Highcharts, default_colors, h
|
|
9900
9932
|
disableAnimation = disabled;
|
9901
9933
|
};
|
9902
9934
|
|
9935
|
+
highchartsRenderer.chartAnimationsDisabled = function () {
|
9936
|
+
return disableAnimation;
|
9937
|
+
};
|
9938
|
+
|
9903
9939
|
highchartsRenderer.adjustFormatToSupportedByMoment = function(format) {
|
9904
9940
|
return typeof format === 'string'
|
9905
9941
|
? format.replace(/y/g, 'Y').replace(/d/g, 'D').replace(/h/g, 'H')
|
@@ -5,6 +5,7 @@ const { DrChartTooltip } = require("../src/dr_chart_tooltip");
|
|
5
5
|
jest.mock("../src/dr_chart_tooltip"); // Mock the tooltip class
|
6
6
|
|
7
7
|
const mockFormattedValue = "16,549";
|
8
|
+
let disableChartAnimation = false;
|
8
9
|
|
9
10
|
DrGaugeChart.highchartsRenderer = {
|
10
11
|
CHART_TYPES: {
|
@@ -20,6 +21,8 @@ DrGaugeChart.highchartsRenderer = {
|
|
20
21
|
formatValue: jest.fn().mockReturnValue({
|
21
22
|
value: mockFormattedValue,
|
22
23
|
}),
|
24
|
+
disableChartAnimations: jest.fn((value) => disableChartAnimation = value),
|
25
|
+
chartAnimationsDisabled: jest.fn(() => disableChartAnimation),
|
23
26
|
};
|
24
27
|
|
25
28
|
const mockAggregationValue = 1000;
|
@@ -111,6 +114,11 @@ describe("DrGaugeChart", () => {
|
|
111
114
|
chart.render();
|
112
115
|
expect(chart.configChart).toHaveBeenCalled();
|
113
116
|
});
|
117
|
+
|
118
|
+
it("disables chart animations by demand", () => {
|
119
|
+
chart.render();
|
120
|
+
expect(DrGaugeChart.highchartsRenderer.chartAnimationsDisabled).toHaveBeenCalledTimes(2);
|
121
|
+
});
|
114
122
|
});
|
115
123
|
|
116
124
|
describe("isLeftQuarter", () => {
|
@@ -306,7 +314,7 @@ describe("DrGaugeChart", () => {
|
|
306
314
|
describe("createTicks", () => {
|
307
315
|
it("should create a ticks array", () => {
|
308
316
|
expect(
|
309
|
-
|
317
|
+
DrGaugeChart.createTicks(
|
310
318
|
[
|
311
319
|
{
|
312
320
|
to: 100,
|
@@ -329,7 +337,7 @@ describe("DrGaugeChart", () => {
|
|
329
337
|
|
330
338
|
it("should sort and remove duplicates", () => {
|
331
339
|
expect(
|
332
|
-
|
340
|
+
DrGaugeChart.createTicks(
|
333
341
|
[
|
334
342
|
{
|
335
343
|
to: 100,
|
@@ -707,12 +715,12 @@ describe("DrGaugeChart", () => {
|
|
707
715
|
describe("getValue", () => {
|
708
716
|
it("should return aggregator value unledss the total is empty", () => {
|
709
717
|
DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries.mockReturnValue([]);
|
710
|
-
expect(
|
718
|
+
expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(mockAggregationValue);
|
711
719
|
});
|
712
720
|
|
713
721
|
it("should return value to display", () => {
|
714
722
|
DrGaugeChart.highchartsRenderer.ptCreateBasicLineSeries.mockReturnValue([{ data: [2000] }]);
|
715
|
-
expect(
|
723
|
+
expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(2000);
|
716
724
|
});
|
717
725
|
|
718
726
|
it("should calculate total", () => {
|
@@ -721,7 +729,7 @@ describe("DrGaugeChart", () => {
|
|
721
729
|
{ data: [3000] },
|
722
730
|
{ data: [5000] },
|
723
731
|
]);
|
724
|
-
expect(
|
732
|
+
expect(DrGaugeChart.getValue(mockPivotData, {})).toBe(10000);
|
725
733
|
});
|
726
734
|
});
|
727
735
|
|
@@ -745,14 +753,14 @@ describe("DrGaugeChart", () => {
|
|
745
753
|
};
|
746
754
|
|
747
755
|
it("return a start border position", () => {
|
748
|
-
expect(
|
756
|
+
expect(DrGaugeChart.getBorderPosition(mockChart, mockOptions, "start")).toEqual({
|
749
757
|
x: 170,
|
750
758
|
y: 201,
|
751
759
|
});
|
752
760
|
});
|
753
761
|
|
754
762
|
it("return an end border position", () => {
|
755
|
-
expect(
|
763
|
+
expect(DrGaugeChart.getBorderPosition(mockChart, mockOptions, "end")).toEqual({
|
756
764
|
x: 230,
|
757
765
|
y: 201,
|
758
766
|
});
|
@@ -761,7 +769,7 @@ describe("DrGaugeChart", () => {
|
|
761
769
|
|
762
770
|
describe("createBorder", () => {
|
763
771
|
beforeEach(() => {
|
764
|
-
|
772
|
+
DrGaugeChart.getBorderPosition = jest.fn().mockReturnValue({
|
765
773
|
x: 100,
|
766
774
|
y: 200,
|
767
775
|
});
|
@@ -805,7 +813,7 @@ describe("DrGaugeChart", () => {
|
|
805
813
|
};
|
806
814
|
|
807
815
|
it("should create a start border", () => {
|
808
|
-
|
816
|
+
DrGaugeChart.createBorder(
|
809
817
|
mockChart,
|
810
818
|
{
|
811
819
|
gauge: {
|
@@ -834,7 +842,7 @@ describe("DrGaugeChart", () => {
|
|
834
842
|
});
|
835
843
|
|
836
844
|
it("should create an end border", () => {
|
837
|
-
|
845
|
+
DrGaugeChart.createBorder(
|
838
846
|
mockChart,
|
839
847
|
{
|
840
848
|
gauge: {
|
@@ -990,7 +998,7 @@ describe("DrGaugeChart", () => {
|
|
990
998
|
describe("getValueLabelPosition", () => {
|
991
999
|
it("should return a value label position depends on the pane center and offsets", () => {
|
992
1000
|
expect(
|
993
|
-
|
1001
|
+
DrGaugeChart.getValueLabelPosition(
|
994
1002
|
{
|
995
1003
|
pane: [
|
996
1004
|
{
|
@@ -1012,7 +1020,7 @@ describe("DrGaugeChart", () => {
|
|
1012
1020
|
});
|
1013
1021
|
|
1014
1022
|
expect(
|
1015
|
-
|
1023
|
+
DrGaugeChart.getValueLabelPosition(
|
1016
1024
|
{
|
1017
1025
|
pane: [
|
1018
1026
|
{
|
@@ -1044,7 +1052,7 @@ describe("DrGaugeChart", () => {
|
|
1044
1052
|
|
1045
1053
|
beforeEach(() => {
|
1046
1054
|
chart.formatValueLabel = jest.fn().mockReturnValue("<div>Mock label</div>");
|
1047
|
-
|
1055
|
+
DrGaugeChart.getValueLabelPosition = jest.fn().mockReturnValue({
|
1048
1056
|
x: 100,
|
1049
1057
|
y: 200,
|
1050
1058
|
});
|
@@ -1540,7 +1548,7 @@ describe("DrGaugeChart", () => {
|
|
1540
1548
|
|
1541
1549
|
// Spy on class methods
|
1542
1550
|
jest.spyOn(chart, "createValueLabel").mockReturnValue(mockValueLabel);
|
1543
|
-
jest.spyOn(
|
1551
|
+
jest.spyOn(DrGaugeChart, "createBorder").mockImplementation((chart, options, position) => {
|
1544
1552
|
if (position === "start") return mockStartBorder;
|
1545
1553
|
if (position === "end") return mockEndBorder;
|
1546
1554
|
});
|
@@ -1561,14 +1569,14 @@ describe("DrGaugeChart", () => {
|
|
1561
1569
|
it("assigns the start border to chart.startBorder", () => {
|
1562
1570
|
chart.setCustomElements(mockChart, mockOptions);
|
1563
1571
|
|
1564
|
-
expect(
|
1572
|
+
expect(DrGaugeChart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "start");
|
1565
1573
|
expect(mockChart.startBorder).toBe(mockStartBorder);
|
1566
1574
|
});
|
1567
1575
|
|
1568
1576
|
it("assigns the end border to chart.endBorder", () => {
|
1569
1577
|
chart.setCustomElements(mockChart, mockOptions);
|
1570
1578
|
|
1571
|
-
expect(
|
1579
|
+
expect(DrGaugeChart.createBorder).toHaveBeenCalledWith(mockChart, mockOptions, "end");
|
1572
1580
|
expect(mockChart.endBorder).toBe(mockEndBorder);
|
1573
1581
|
});
|
1574
1582
|
|
@@ -1609,12 +1617,12 @@ describe("DrGaugeChart", () => {
|
|
1609
1617
|
mockValueLabelPosition = { x: 70, y: 80 };
|
1610
1618
|
|
1611
1619
|
// Spy on helper methods
|
1612
|
-
jest.spyOn(
|
1620
|
+
jest.spyOn(DrGaugeChart, "getBorderPosition").mockImplementation((chart, options, position) => {
|
1613
1621
|
if (position === "start") return mockStartPosition;
|
1614
1622
|
if (position === "end") return mockEndPosition;
|
1615
1623
|
});
|
1616
1624
|
jest.spyOn(chart, "getGoalIconPosition").mockReturnValue(mockGoalIconPosition);
|
1617
|
-
jest.spyOn(
|
1625
|
+
jest.spyOn(DrGaugeChart, "getValueLabelPosition").mockReturnValue(mockValueLabelPosition);
|
1618
1626
|
});
|
1619
1627
|
|
1620
1628
|
afterEach(() => {
|
@@ -1624,14 +1632,14 @@ describe("DrGaugeChart", () => {
|
|
1624
1632
|
it("updates startBorder attributes correctly", () => {
|
1625
1633
|
chart.updateCustomElements(mockChart, mockOptions);
|
1626
1634
|
|
1627
|
-
expect(
|
1635
|
+
expect(DrGaugeChart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "start");
|
1628
1636
|
expect(mockChart.startBorder.attr).toHaveBeenCalledWith(mockStartPosition);
|
1629
1637
|
});
|
1630
1638
|
|
1631
1639
|
it("updates endBorder attributes correctly", () => {
|
1632
1640
|
chart.updateCustomElements(mockChart, mockOptions);
|
1633
1641
|
|
1634
|
-
expect(
|
1642
|
+
expect(DrGaugeChart.getBorderPosition).toHaveBeenCalledWith(mockChart, mockOptions, "end");
|
1635
1643
|
expect(mockChart.endBorder.attr).toHaveBeenCalledWith(mockEndPosition);
|
1636
1644
|
});
|
1637
1645
|
|
@@ -1645,7 +1653,7 @@ describe("DrGaugeChart", () => {
|
|
1645
1653
|
it("updates label attributes correctly", () => {
|
1646
1654
|
chart.updateCustomElements(mockChart, mockOptions);
|
1647
1655
|
|
1648
|
-
expect(
|
1656
|
+
expect(DrGaugeChart.getValueLabelPosition).toHaveBeenCalledWith(mockChart, mockOptions);
|
1649
1657
|
expect(mockChart.label.attr).toHaveBeenCalledWith(mockValueLabelPosition);
|
1650
1658
|
});
|
1651
1659
|
});
|