@datarailsshared/dr_renderer 1.2.463 → 1.3.1-27.merge
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/.circleci/config.yml +13 -7
- package/.github/workflows/build-deploy.yml +29 -0
- package/CODEOWNERS +1 -0
- package/package.json +1 -1
- package/src/charts/dr_donut_chart.js +163 -0
- package/src/charts/dr_gauge_categories_summary_chart.js +323 -0
- package/src/charts/dr_gauge_chart.js +179 -130
- package/src/dr-renderer-helpers.js +20 -0
- package/src/dr_chart_tooltip.js +16 -9
- package/src/dr_pivottable.js +27 -3
- package/src/highcharts_renderer.js +348 -301
- package/src/pivot.css +32 -18
- package/src/published_items_renderer.js +6 -6
- package/src/smart_queries_helper.js +96 -0
- package/tests/dr-renderer-helpers.test.js +48 -0
- package/tests/dr_chart_tooltip.test.js +50 -0
- package/tests/dr_gauge_chart.test.js +64 -40
- package/tests/highcharts_renderer.test.js +8 -397
package/src/pivot.css
CHANGED
@@ -5,6 +5,17 @@
|
|
5
5
|
flex-direction: column;
|
6
6
|
}
|
7
7
|
|
8
|
+
.pivot-wrapper .noData--table-many-rows {
|
9
|
+
display: flex;
|
10
|
+
flex-direction: column;
|
11
|
+
}
|
12
|
+
.pivot-wrapper .noData--table-many-rows > *:first-child {
|
13
|
+
display: flex;
|
14
|
+
align-items: center;
|
15
|
+
justify-content: center;
|
16
|
+
line-height: 25px;
|
17
|
+
}
|
18
|
+
|
8
19
|
table.pvtTable {
|
9
20
|
font-size: 8pt;
|
10
21
|
text-align: left;
|
@@ -228,16 +239,27 @@ table.pvtTable.newPvtTable thead tr th {
|
|
228
239
|
border-right: 1px solid #fff;
|
229
240
|
}
|
230
241
|
|
231
|
-
table.pvtTable.newPvtTable thead tr th
|
232
|
-
table.pvtTable.newPvtTable
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
242
|
+
table.pvtTable.newPvtTable thead tr th i,
|
243
|
+
table.pvtTable.newPvtTable tbody tr th i {
|
244
|
+
position: relative;
|
245
|
+
bottom: -1px;
|
246
|
+
background-color: #ffffff;
|
247
|
+
border-radius: 2px;
|
248
|
+
margin-right: 2px;
|
249
|
+
margin-left: 1px;
|
250
|
+
font-weight: bold;
|
251
|
+
font-size: 10px;
|
252
|
+
color: #151a41;
|
253
|
+
}
|
254
|
+
|
255
|
+
table.pvtTable.newPvtTable tbody tr th i {
|
256
|
+
bottom: -2px;
|
257
|
+
background-color: #dfe6ec;
|
258
|
+
}
|
259
|
+
|
260
|
+
table.pvtTable.newPvtTable thead tr th .dr-icon-filter-old,
|
261
|
+
table.pvtTable.newPvtTable tbody tr th .dr-icon-filter-old {
|
262
|
+
font-weight: normal;
|
241
263
|
}
|
242
264
|
|
243
265
|
table.pvtTable.newPvtTable.colorized tr th.colTotal,
|
@@ -261,14 +283,6 @@ table.pvtTable.newPvtTable tbody tr td.rowTotal {
|
|
261
283
|
border-color: #ffffff !important;
|
262
284
|
}
|
263
285
|
|
264
|
-
table.pvtTable.newPvtTable tbody tr th .fa {
|
265
|
-
padding: 2px;
|
266
|
-
background-color: #dfe6ec;
|
267
|
-
border-radius: 2px;
|
268
|
-
margin-right: 2px;
|
269
|
-
font-size: 8px;
|
270
|
-
}
|
271
|
-
|
272
286
|
table.pvtTable.newPvtTable tbody tr th,
|
273
287
|
table.pvtTable.newPvtTable tbody tr td {
|
274
288
|
border: 1px solid #dfe6ec;
|
@@ -25,18 +25,18 @@ let getPublishedItemsRenderer = function (publishedItemsRenderer) {
|
|
25
25
|
const body = iframeWindow.document.getElementsByTagName("body")[0];
|
26
26
|
|
27
27
|
if (table && options.responsive) {
|
28
|
-
publishedItemsRenderer.zoomTable(iframeWindow, table
|
28
|
+
publishedItemsRenderer.zoomTable(iframeWindow, table);
|
29
29
|
}
|
30
30
|
|
31
31
|
publishedItemsRenderer.resizePublishedImage(publish_item_image, iframeWindow, body);
|
32
32
|
}
|
33
33
|
|
34
|
-
publishedItemsRenderer.zoomTable = function (iframeWindow, table
|
35
|
-
|
36
|
-
const SCROLL_OFFSET = 16;
|
34
|
+
publishedItemsRenderer.zoomTable = function (iframeWindow, table) {
|
35
|
+
table.style.zoom = 1;
|
37
36
|
|
38
|
-
const
|
39
|
-
const
|
37
|
+
const SCROLL_OFFSET = 16;
|
38
|
+
const tableWidth = table.getBoundingClientRect().width;
|
39
|
+
const tableHeight = table.getBoundingClientRect().height;
|
40
40
|
const root = iframeWindow.document.documentElement;
|
41
41
|
|
42
42
|
let requiredZoom = (root.clientWidth - SCROLL_OFFSET) / tableWidth;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
const lodash = require('lodash');
|
2
|
+
|
3
|
+
const DR_SCENARIO = {
|
4
|
+
SQ_Actuals: 'SQ_Actuals',
|
5
|
+
Forecast: 'Forecast',
|
6
|
+
};
|
7
|
+
|
8
|
+
function createSingleDataSeriesForForecast(chart_series, chartOptions, pivotData) {
|
9
|
+
const { actuals, forecast, smart_query } = chartOptions.chart;
|
10
|
+
const input = pivotData.input;
|
11
|
+
|
12
|
+
const hasSQActuals = input.some(item => item.Scenario && item.Scenario.indexOf(DR_SCENARIO.SQ_Actuals) !== -1);
|
13
|
+
chartOptions.isSmartQueriesEnabled = hasSQActuals;
|
14
|
+
if (!smart_query || !hasSQActuals) return null;
|
15
|
+
|
16
|
+
const midMonthOffset = 0.5
|
17
|
+
return chart_series.length === 1
|
18
|
+
? buildChartSeriesFromPivotInputOnly(input, actuals, forecast, midMonthOffset)
|
19
|
+
: buildChartSeriesFromSeries(chart_series, actuals, forecast, midMonthOffset);
|
20
|
+
}
|
21
|
+
|
22
|
+
function buildChartSeriesFromPivotInputOnly(input, actuals, forecast, midMonthOffset) {
|
23
|
+
const filtered = input.filter(item =>
|
24
|
+
(item.Scenario === DR_SCENARIO.SQ_Actuals || item.Scenario === DR_SCENARIO.Forecast) &&
|
25
|
+
item.Amount !== 0
|
26
|
+
);
|
27
|
+
|
28
|
+
const data = filtered.map(item => ({
|
29
|
+
y: item.Amount,
|
30
|
+
name: item.Reporting_Month,
|
31
|
+
initialName: item.Reporting_Month,
|
32
|
+
type: item.Scenario,
|
33
|
+
})).sort((a, b) => new Date(a.name) - new Date(b.name));
|
34
|
+
|
35
|
+
const sqCount = input.filter(item => item.Scenario === DR_SCENARIO.SQ_Actuals).length;
|
36
|
+
|
37
|
+
return {
|
38
|
+
name: "Forecast Smart Query",
|
39
|
+
data,
|
40
|
+
zoneAxis: "x",
|
41
|
+
zones: [
|
42
|
+
{ value: sqCount - midMonthOffset, dashStyle: actuals },
|
43
|
+
{ dashStyle: forecast },
|
44
|
+
],
|
45
|
+
};
|
46
|
+
}
|
47
|
+
|
48
|
+
function buildChartSeriesFromSeries(chart_series, actuals, forecast, midMonthOffset) {
|
49
|
+
const seriesA = lodash.find(chart_series, s => s.name && lodash.includes(s.name, DR_SCENARIO.SQ_Actuals));
|
50
|
+
const seriesB = lodash.find(chart_series, s => s.name && lodash.includes(s.name, DR_SCENARIO.Forecast));
|
51
|
+
|
52
|
+
if (!seriesA || !seriesB) return null;
|
53
|
+
|
54
|
+
const indexOfForecastFirstZero = lodash.findIndex(seriesB.data, function(value) {
|
55
|
+
return value.y === 0;
|
56
|
+
});
|
57
|
+
const indexOfActualsFirstZero = lodash.findIndex(seriesA.data, function(value) {
|
58
|
+
return value.y === 0;
|
59
|
+
});
|
60
|
+
const cutoffIndex = Math.max(indexOfForecastFirstZero, indexOfActualsFirstZero);
|
61
|
+
|
62
|
+
|
63
|
+
const minLength = Math.min(seriesA.data.length, seriesB.data.length);
|
64
|
+
const data = [];
|
65
|
+
|
66
|
+
for (let i = 0; i < minLength; i++) {
|
67
|
+
const pointA = seriesA.data[i];
|
68
|
+
const pointB = seriesB.data[i];
|
69
|
+
|
70
|
+
const yA = (pointA && pointA.y !== null && typeof pointA.y !== undefined) ? pointA.y : pointA;
|
71
|
+
const yB = (pointB && pointB.y !== null && typeof pointB.y !== undefined) ? pointB.y : pointB;
|
72
|
+
|
73
|
+
data.push({
|
74
|
+
x: i,
|
75
|
+
y: yA + yB,
|
76
|
+
name: pointA && pointA.name ? pointA.name : null,
|
77
|
+
initialName: (pointA && pointA.name) ? pointA.name : 'Point ' + (i + 1),
|
78
|
+
type: yA ? DR_SCENARIO.SQ_Actuals : DR_SCENARIO.Forecast,
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
return {
|
83
|
+
name: "Forecast Smart Query",
|
84
|
+
data,
|
85
|
+
zoneAxis: "x",
|
86
|
+
zones: [
|
87
|
+
{ value: cutoffIndex - midMonthOffset, dashStyle: actuals },
|
88
|
+
{ dashStyle: forecast },
|
89
|
+
],
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
module.exports = {
|
94
|
+
createSingleDataSeriesForForecast,
|
95
|
+
DR_SCENARIO
|
96
|
+
};
|
@@ -147,4 +147,52 @@ describe('dr-renderer-helpers', () => {
|
|
147
147
|
expect(svg.xCorr).toBe(0);
|
148
148
|
});
|
149
149
|
});
|
150
|
+
|
151
|
+
describe('disableLegendInteractionIfRequired', () => {
|
152
|
+
const notPieTypes = ['column', 'line', 'area', 'areaspline', 'scatter', 'spline', 'bar', 'waterfall'];
|
153
|
+
|
154
|
+
it('should not update legend settings', () => {
|
155
|
+
const chartOptionsMock = {
|
156
|
+
chart: {
|
157
|
+
type: 'column',
|
158
|
+
},
|
159
|
+
};
|
160
|
+
const additionOptionsMock = {
|
161
|
+
disable_legend_interaction: false,
|
162
|
+
};
|
163
|
+
drRendererHelpers.disableLegendInteractionIfRequired(chartOptionsMock, additionOptionsMock);
|
164
|
+
expect(chartOptionsMock.plotOptions).toBeFalsy();
|
165
|
+
expect(chartOptionsMock.legend).toBeFalsy();
|
166
|
+
});
|
167
|
+
|
168
|
+
it('should update legend styles and set click event (for pie chart)', () => {
|
169
|
+
const chartOptionsMock = {
|
170
|
+
chart: {
|
171
|
+
type: 'pie',
|
172
|
+
},
|
173
|
+
};
|
174
|
+
const additionOptionsMock = {
|
175
|
+
disable_legend_interaction: true,
|
176
|
+
};
|
177
|
+
drRendererHelpers.disableLegendInteractionIfRequired(chartOptionsMock, additionOptionsMock);
|
178
|
+
expect(chartOptionsMock.plotOptions.pie.point.events.legendItemClick).toEqual(jasmine.any(Function));
|
179
|
+
expect(chartOptionsMock.legend.itemStyle.cursor).toEqual('default');
|
180
|
+
});
|
181
|
+
|
182
|
+
notPieTypes.forEach((chartType) => {
|
183
|
+
it(`should update legend styles and set click event (for ${ chartType } chart)`, () => {
|
184
|
+
const chartOptionsMock = {
|
185
|
+
chart: {
|
186
|
+
type: chartType,
|
187
|
+
},
|
188
|
+
};
|
189
|
+
const additionOptionsMock = {
|
190
|
+
disable_legend_interaction: true,
|
191
|
+
};
|
192
|
+
drRendererHelpers.disableLegendInteractionIfRequired(chartOptionsMock, additionOptionsMock);
|
193
|
+
expect(chartOptionsMock.plotOptions.series.events.legendItemClick).toEqual(jasmine.any(Function));
|
194
|
+
expect(chartOptionsMock.legend.itemStyle.cursor).toEqual('default');
|
195
|
+
});
|
196
|
+
});
|
197
|
+
});
|
150
198
|
});
|
@@ -32,6 +32,7 @@ describe("DrChartTooltip", () => {
|
|
32
32
|
},
|
33
33
|
container: {
|
34
34
|
getBoundingClientRect: jest.fn().mockReturnValue(chartBBoxMock),
|
35
|
+
clientWidth: 800,
|
35
36
|
},
|
36
37
|
chartWidth: 800,
|
37
38
|
chartHeight: 600,
|
@@ -349,6 +350,23 @@ describe("DrChartTooltip", () => {
|
|
349
350
|
expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:15px`);
|
350
351
|
});
|
351
352
|
|
353
|
+
it("sets a top arrow position (tooltip is smaller than anchor) on a zoomed chart", () => {
|
354
|
+
chartMock.container.clientWidth = 1600;
|
355
|
+
tooltipBBoxMock.width = 40;
|
356
|
+
tooltipBBoxMock.height = 20;
|
357
|
+
anchorBBoxMock.width = 50;
|
358
|
+
anchorBBoxMock.height = 50;
|
359
|
+
anchorBBoxMock.x = 200;
|
360
|
+
anchorBBoxMock.y = 200;
|
361
|
+
positionMock = {
|
362
|
+
x: 205,
|
363
|
+
y: 180,
|
364
|
+
direction: "top",
|
365
|
+
};
|
366
|
+
instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
|
367
|
+
expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowTopBorderStyles}left:22px`);
|
368
|
+
});
|
369
|
+
|
352
370
|
it("sets a top arrow position (clamp left)", () => {
|
353
371
|
tooltipBBoxMock.width = 100;
|
354
372
|
tooltipBBoxMock.height = 20;
|
@@ -477,6 +495,23 @@ describe("DrChartTooltip", () => {
|
|
477
495
|
expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:15px`);
|
478
496
|
});
|
479
497
|
|
498
|
+
it("sets a left arrow position (tooltip is smaller than anchor) on a zoomed chart", () => {
|
499
|
+
chartMock.container.clientWidth = 1600;
|
500
|
+
tooltipBBoxMock.width = 20;
|
501
|
+
tooltipBBoxMock.height = 40;
|
502
|
+
anchorBBoxMock.width = 50;
|
503
|
+
anchorBBoxMock.height = 50;
|
504
|
+
anchorBBoxMock.x = 200;
|
505
|
+
anchorBBoxMock.y = 200;
|
506
|
+
positionMock = {
|
507
|
+
x: 180,
|
508
|
+
y: 205,
|
509
|
+
direction: "left",
|
510
|
+
};
|
511
|
+
instance.setArrowPosition(tooltipMock, positionMock, anchorMock, optionsMock);
|
512
|
+
expect(arrowElMock.setAttribute).toHaveBeenCalledWith("style", `${commonStyles}${arrowLeftBorderStyles}top:22px`);
|
513
|
+
});
|
514
|
+
|
480
515
|
it("sets a left arrow position (clamp top)", () => {
|
481
516
|
tooltipBBoxMock.width = 20;
|
482
517
|
tooltipBBoxMock.height = 100;
|
@@ -593,6 +628,11 @@ describe("DrChartTooltip", () => {
|
|
593
628
|
expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 175, y: 170 });
|
594
629
|
});
|
595
630
|
|
631
|
+
it("gets coordinates (top - without correction)", () => {
|
632
|
+
chartMock.container.clientWidth = 1600;
|
633
|
+
expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 400, y: 370 });
|
634
|
+
});
|
635
|
+
|
596
636
|
it("gets coordinates (top - bound to the chart - left)", () => {
|
597
637
|
anchorBBoxMock.x = 0;
|
598
638
|
expect(instance.getCoords("top", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 8 /* offset */, y: 170 });
|
@@ -627,6 +667,11 @@ describe("DrChartTooltip", () => {
|
|
627
667
|
expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 90, y: 215 });
|
628
668
|
});
|
629
669
|
|
670
|
+
it("gets coordinates (left - without correction)", () => {
|
671
|
+
chartMock.container.clientWidth = 1600;
|
672
|
+
expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 290, y: 440 });
|
673
|
+
});
|
674
|
+
|
630
675
|
it("gets coordinates (left - bound to the chart - bottom)", () => {
|
631
676
|
anchorBBoxMock.y = 1000;
|
632
677
|
expect(instance.getCoords("left", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 90, y: 572 });
|
@@ -645,6 +690,11 @@ describe("DrChartTooltip", () => {
|
|
645
690
|
expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 260, y: 215 });
|
646
691
|
});
|
647
692
|
|
693
|
+
it("gets coordinates (right - without correction)", () => {
|
694
|
+
chartMock.container.clientWidth = 1600;
|
695
|
+
expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({ x: 510, y: 440 });
|
696
|
+
});
|
697
|
+
|
648
698
|
it("gets coordinates (right - bound to the chart - bottom)", () => {
|
649
699
|
anchorBBoxMock.y = 1000;
|
650
700
|
expect(instance.getCoords("right", tooltipMock, anchorMock, optionsMock)).toEqual({
|