@eclipse-scout/chart 22.0.26 → 22.0.33

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.
@@ -1,9 +1,9 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
2
+ * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
3
3
  * All rights reserved. This program and the accompanying materials
4
4
  * are made available under the terms of the Eclipse Public License v1.0
5
5
  * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
7
7
  *
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
@@ -99,6 +99,13 @@ $.extend(true, ChartJs.overrides, {
99
99
  borderWidth: 2
100
100
  }
101
101
  }
102
+ },
103
+ scatter: {
104
+ elements: {
105
+ point: {
106
+ radius: 3
107
+ }
108
+ }
102
109
  }
103
110
  });
104
111
 
@@ -178,6 +185,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
178
185
  this._resizeHandler = this._onResize.bind(this);
179
186
 
180
187
  this._tooltipTitleGenerator = this._generateTooltipTitle.bind(this);
188
+ this._tooltipItemsGenerator = this._generateTooltipItems.bind(this);
181
189
  this._tooltipLabelGenerator = this._generateTooltipLabel.bind(this);
182
190
  this._tooltipLabelValueGenerator = this._generateTooltipLabelValue.bind(this);
183
191
  this._tooltipLabelColorGenerator = this._generateTooltipLabelColor.bind(this);
@@ -772,7 +780,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
772
780
  return;
773
781
  }
774
782
 
775
- if (config.type === Chart.Type.BUBBLE) {
783
+ if (scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
776
784
  this.onlyIntegers = config.data.datasets.every(dataset => dataset.data.every(data => numbers.isInteger(data.x) && numbers.isInteger(data.y)));
777
785
  } else {
778
786
  this.onlyIntegers = config.data.datasets.every(dataset => dataset.data.every(data => numbers.isInteger(data)));
@@ -789,6 +797,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
789
797
  tooltip: {
790
798
  callbacks: {
791
799
  title: this._tooltipTitleGenerator,
800
+ items: this._tooltipItemsGenerator,
792
801
  label: this._tooltipLabelGenerator,
793
802
  labelValue: this._tooltipLabelValueGenerator,
794
803
  labelColor: this._tooltipLabelColorGenerator
@@ -816,21 +825,20 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
816
825
  config = chart.config,
817
826
  dataset = tooltipItem.dataset,
818
827
  title = [];
819
- if (config.type === Chart.Type.BUBBLE) {
828
+ if (scout.isOneOf(config.type, Chart.Type.BUBBLE)) {
820
829
  let xAxis = config.options.scales.x,
821
830
  yAxis = config.options.scales.y,
822
- xAxisLabel = xAxis.title.text,
823
- yAxisLabel = yAxis.title.text;
824
- xAxisLabel = xAxisLabel ? strings.encode(xAxisLabel) : ChartJsRenderer.ARROW_LEFT_RIGHT;
825
- yAxisLabel = yAxisLabel ? strings.encode(yAxisLabel) : ' ' + ChartJsRenderer.ARROW_UP_DOWN + ' ';
831
+ axisLabels = this._getAxisLabels(config);
826
832
  let xTickLabel = xAxis.ticks.callback(dataset.data[tooltipItem.dataIndex].x);
827
833
  if (xTickLabel) {
828
- title.push(this._createTooltipAttribute(xAxisLabel, strings.encode(xTickLabel), true));
834
+ title.push(this._createTooltipAttribute(axisLabels.x, strings.encode(xTickLabel), true));
829
835
  }
830
836
  let yTickLabel = yAxis.ticks.callback(dataset.data[tooltipItem.dataIndex].y);
831
837
  if (yTickLabel) {
832
- title.push(this._createTooltipAttribute(yAxisLabel, strings.encode(yTickLabel), true));
838
+ title.push(this._createTooltipAttribute(axisLabels.y, strings.encode(yTickLabel), true));
833
839
  }
840
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
841
+ // nop, scatter has the title in the items table
834
842
  } else {
835
843
  let label = chart.data.labels[tooltipItem.dataIndex];
836
844
  title.push(this._createTooltipAttribute(config.options.reformatLabels ? this._formatLabel(label) : label, '', true));
@@ -838,6 +846,78 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
838
846
  return title;
839
847
  }
840
848
 
849
+ _getAxisLabels(config) {
850
+ let xAxisLabel = config.options.scales.x.title.text,
851
+ yAxisLabel = config.options.scales.y.title.text;
852
+ xAxisLabel = this._resolveAxisLabel(xAxisLabel, ChartJsRenderer.ARROW_LEFT_RIGHT);
853
+ yAxisLabel = this._resolveAxisLabel(yAxisLabel, ' ' + ChartJsRenderer.ARROW_UP_DOWN + ' ');
854
+
855
+ return {x: xAxisLabel, y: yAxisLabel};
856
+ }
857
+
858
+ _resolveAxisLabel(axisLabel, defaultLabel = '') {
859
+ if (objects.isFunction(axisLabel)) {
860
+ axisLabel = axisLabel();
861
+ axisLabel = objects.isString(axisLabel) ? axisLabel : '';
862
+ }
863
+ return axisLabel ? strings.encode(axisLabel) : defaultLabel;
864
+ }
865
+
866
+ _generateTooltipItems(tooltipItems, tooltipLabel, tooltipLabelValue, tooltipColor) {
867
+ if (!tooltipItems || !tooltipItems.length) {
868
+ return '';
869
+ }
870
+ let tooltipItem = tooltipItems[0],
871
+ chart = tooltipItem.chart,
872
+ config = chart.config,
873
+ xAxisValues = false,
874
+ yAxisValues = false,
875
+ itemsText = '';
876
+
877
+ tooltipItems.forEach(tooltipItem => {
878
+ let {label, labelValue, labelColor} = this._getItemDetails(tooltipItem, tooltipLabel, tooltipLabelValue, tooltipColor);
879
+ if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
880
+ xAxisValues |= objects.isString(labelValue.x);
881
+ yAxisValues |= objects.isString(labelValue.y);
882
+ itemsText += this._createTooltipScatterAttribute(label, labelValue.x, labelValue.y, false, labelColor);
883
+ } else {
884
+ itemsText += this._createTooltipAttribute(label, labelValue, false, labelColor);
885
+ }
886
+ });
887
+
888
+ // tabular representation for scatter tooltip needs an additional header and footer
889
+ if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
890
+ let tableHeader = '<table><tbody>';
891
+ let axisLabels = this._getAxisLabels(config);
892
+ tableHeader += this._createTooltipScatterAttribute('',
893
+ xAxisValues ? axisLabels.x : '', // do not show axis label if no values are shown
894
+ yAxisValues ? axisLabels.y : '', // do not show axis label if no values are shown
895
+ true,
896
+ null);
897
+ let tableFooter = '</tbody></table>';
898
+ itemsText = strings.box(tableHeader, itemsText, tableFooter);
899
+ }
900
+
901
+ return itemsText;
902
+ }
903
+
904
+ _getItemDetails(tooltipItem, tooltipLabel, tooltipLabelValue, tooltipColor) {
905
+ let label, labelValue, labelColor;
906
+ if (objects.isFunction(tooltipLabel)) {
907
+ label = tooltipLabel(tooltipItem);
908
+ label = objects.isString(label) ? label : '';
909
+ }
910
+ if (objects.isFunction(tooltipLabelValue)) {
911
+ labelValue = tooltipLabelValue(tooltipItem);
912
+ labelValue = objects.isString(labelValue) || objects.isPlainObject(labelValue) ? labelValue : '';
913
+ }
914
+ if (objects.isFunction(tooltipColor)) {
915
+ labelColor = tooltipColor(tooltipItem);
916
+ labelColor = objects.isPlainObject(labelColor) ? (labelColor.backgroundColor || '') : '';
917
+ }
918
+ return {label, labelValue, labelColor};
919
+ }
920
+
841
921
  _generateTooltipLabel(tooltipItem) {
842
922
  return strings.encode(tooltipItem.dataset.label);
843
923
  }
@@ -847,6 +927,11 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
847
927
  dataset = tooltipItem.dataset;
848
928
  if (config.type === Chart.Type.BUBBLE) {
849
929
  return strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].z));
930
+ } else if (config.type === Chart.Type.SCATTER) {
931
+ return {
932
+ x: strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].x)),
933
+ y: strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].y))
934
+ };
850
935
  }
851
936
  return strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex]));
852
937
  }
@@ -855,7 +940,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
855
940
  let config = tooltipItem.chart.config,
856
941
  dataset = tooltipItem.dataset,
857
942
  legendColor, backgroundColor, borderColor, index;
858
- if (scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.BAR_HORIZONTAL, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
943
+ if (scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.BAR_HORIZONTAL, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
859
944
  borderColor = dataset.borderColor;
860
945
  legendColor = dataset.legendColor;
861
946
  index = tooltipItem.datasetIndex;
@@ -893,6 +978,18 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
893
978
  '</div>';
894
979
  }
895
980
 
981
+ _createTooltipScatterAttribute(label, xValue, yValue, isTitle, color) {
982
+ let cssClass = isTitle ? 'attribute title' : 'attribute';
983
+ return '<tr class="' + cssClass + '">' +
984
+ '<td class="color-cell">' +
985
+ (color ? '<div class="color" style="background-color:' + color + '"></div>' : '') +
986
+ '</td>' +
987
+ '<td class="label">' + label + '</td>' +
988
+ (xValue ? '<td class="value">' + xValue + '</td>' : '') +
989
+ (yValue ? '<td class="value">' + yValue + '</td>' : '') +
990
+ '</tr>';
991
+ }
992
+
896
993
  _renderTooltip(context) {
897
994
  let isHideTooltip = context.tooltip.opacity === 0 || context.tooltip._tooltipItems.length < 1;
898
995
  if (isHideTooltip) {
@@ -920,8 +1017,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
920
1017
  return;
921
1018
  }
922
1019
  let tooltip = context.tooltip,
923
- tooltipItems = tooltip._tooltipItems;
924
- if (tooltipItems.length < 1) {
1020
+ dataPoints = tooltip.dataPoints;
1021
+ if (dataPoints.length < 1) {
925
1022
  return;
926
1023
  }
927
1024
  if (this._tooltip) {
@@ -932,32 +1029,20 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
932
1029
  let tooltipOptions = tooltip.options || {},
933
1030
  tooltipCallbacks = tooltipOptions.callbacks || {},
934
1031
  tooltipTitle = tooltipCallbacks.title,
1032
+ tooltipItems = tooltipCallbacks.items,
935
1033
  tooltipLabel = tooltipCallbacks.label,
936
1034
  tooltipLabelValue = tooltipCallbacks.labelValue,
937
1035
  tooltipColor = tooltipCallbacks.labelColor,
938
1036
  tooltipText = '';
939
1037
 
940
1038
  if (objects.isFunction(tooltipTitle)) {
941
- tooltipText += arrays.ensure(tooltipTitle(tooltipItems)).join('');
1039
+ tooltipText += arrays.ensure(tooltipTitle(dataPoints)).join('');
1040
+ }
1041
+ if (objects.isFunction(tooltipItems)) {
1042
+ tooltipText += arrays.ensure(tooltipItems(dataPoints, tooltipLabel, tooltipLabelValue, tooltipColor)).join('');
942
1043
  }
943
- tooltipItems.forEach(tooltipItem => {
944
- let label, labelValue, labelColor;
945
- if (objects.isFunction(tooltipLabel)) {
946
- label = tooltipLabel(tooltipItem);
947
- label = objects.isString(label) ? label : '';
948
- }
949
- if (objects.isFunction(tooltipLabelValue)) {
950
- labelValue = tooltipLabelValue(tooltipItem);
951
- labelValue = objects.isString(labelValue) ? labelValue : '';
952
- }
953
- if (objects.isFunction(tooltipColor)) {
954
- labelColor = tooltipColor(tooltipItem);
955
- labelColor = objects.isPlainObject(labelColor) ? (labelColor.backgroundColor || '') : '';
956
- }
957
- tooltipText += this._createTooltipAttribute(label, labelValue, false, labelColor);
958
- });
959
1044
 
960
- let positionAndOffset = this._computeTooltipPositionAndOffset(tooltipItems[0]),
1045
+ let positionAndOffset = this._computeTooltipPositionAndOffset(dataPoints[0]),
961
1046
  origin = graphics.offsetBounds(this.$canvas);
962
1047
  origin.x += tooltip.caretX + positionAndOffset.offsetX;
963
1048
  origin.y += tooltip.caretY + positionAndOffset.offsetY;
@@ -1039,7 +1124,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1039
1124
  let angle = element.angle;
1040
1125
  tooltipPosition = (0 <= angle && angle < Math.PI) ? 'bottom' : 'top';
1041
1126
  tooltipDirection = (-Math.PI / 2 <= angle && angle < Math.PI / 2) ? 'right' : 'left';
1042
- } else if (config.type === Chart.Type.BUBBLE) {
1127
+ } else if (scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1043
1128
  let element = chart.getDatasetMeta(datasetIndex).data[dataIndex];
1044
1129
  let chartArea = chart.chartArea,
1045
1130
  mid = chartArea.left + (chartArea.width / 2);
@@ -1061,7 +1146,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1061
1146
 
1062
1147
  offsetY = -height / 2;
1063
1148
  offsetX = tooltipDirection === 'left' ? b : -b;
1064
- } else if (scout.isOneOf(config.type, Chart.Type.LINE, Chart.Type.BUBBLE, Chart.Type.RADAR) || dataset.type === Chart.Type.LINE) {
1149
+ } else if (scout.isOneOf(config.type, Chart.Type.LINE, Chart.Type.BUBBLE, Chart.Type.SCATTER, Chart.Type.RADAR) || dataset.type === Chart.Type.LINE) {
1065
1150
  // noinspection JSValidateTypes
1066
1151
  /**
1067
1152
  * @type PointElement
@@ -1159,6 +1244,23 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1159
1244
  }
1160
1245
  }
1161
1246
  }, config.options);
1247
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
1248
+ config.options = $.extend(true, {}, {
1249
+ scales: {
1250
+ x: {
1251
+ minSpaceBetweenTicks: 35,
1252
+ ticks: {
1253
+ padding: 10
1254
+ }
1255
+ },
1256
+ y: {
1257
+ minSpaceBetweenTicks: 35,
1258
+ ticks: {
1259
+ padding: 10
1260
+ }
1261
+ }
1262
+ }
1263
+ }, config.options);
1162
1264
  }
1163
1265
 
1164
1266
  this._adjustXAxis(config);
@@ -1173,7 +1275,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1173
1275
  let type = config.type,
1174
1276
  scales = config.options.scales;
1175
1277
 
1176
- if (this._isHorizontalBar(config) || type === Chart.Type.BUBBLE) {
1278
+ if (this._isHorizontalBar(config) || scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1177
1279
  scales.x = $.extend(true, {}, {
1178
1280
  beginAtZero: this._isHorizontalBar(config),
1179
1281
  offset: type === Chart.Type.BUBBLE,
@@ -1194,7 +1296,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1194
1296
  }
1195
1297
  }, scales.x);
1196
1298
  }
1197
- if (this._isHorizontalBar(config) || type === Chart.Type.BUBBLE || config.options.reformatLabels) {
1299
+ if (this._isHorizontalBar(config) || scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER) || config.options.reformatLabels) {
1198
1300
  scales.x = $.extend(true, {}, {
1199
1301
  ticks: {
1200
1302
  callback: this._xLabelFormatter
@@ -1221,7 +1323,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1221
1323
  }, scales.y);
1222
1324
  } else {
1223
1325
  scales.y = $.extend(true, {}, {
1224
- beginAtZero: type !== Chart.Type.BUBBLE,
1326
+ beginAtZero: !scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER),
1225
1327
  grid: {
1226
1328
  drawBorder: false,
1227
1329
  drawTicks: false
@@ -1313,6 +1415,15 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1313
1415
  borderRadius: 3
1314
1416
  }, plugins.datalabels);
1315
1417
  plugins.datalabels.display = 'auto';
1418
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
1419
+ plugins.datalabels = $.extend(true, {}, {
1420
+ backgroundColor: this._datalabelBackgroundColorHandler,
1421
+ borderRadius: 3,
1422
+ anchor: 'end',
1423
+ align: 'top',
1424
+ offset: 3
1425
+ }, plugins.datalabels);
1426
+ plugins.datalabels.display = 'auto';
1316
1427
  }
1317
1428
  if (config.options.reformatLabels) {
1318
1429
  let handleFormatter = formatter => {
@@ -1401,7 +1512,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1401
1512
  this.chart.session.text('ui.Tri'),
1402
1513
  this.chart.session.text('ui.Trd')];
1403
1514
  for (let i = 0; i < abbreviations.length; i++) {
1404
- if (abs >= 1000000) {
1515
+ if (abs >= 1000) {
1405
1516
  abs = abs / 1000;
1406
1517
  abbreviation = ' ' + abbreviations[i];
1407
1518
  } else {
@@ -1533,6 +1644,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1533
1644
  _formatDatalabels(value, context) {
1534
1645
  if (context.chart.config.type === Chart.Type.BUBBLE) {
1535
1646
  return this._formatLabel(value.z);
1647
+ } else if (context.chart.config.type === Chart.Type.SCATTER) {
1648
+ return strings.join(' / ', this._formatLabel(value.x), this._formatLabel(value.y));
1536
1649
  }
1537
1650
  return this._formatLabel(value);
1538
1651
  }
@@ -1608,7 +1721,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1608
1721
  }
1609
1722
  if (checkable) {
1610
1723
  let datasetLength = elem.data.length;
1611
- if (scout.isOneOf(type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.BUBBLE) || (type === Chart.Type.BAR && (elem.type || Chart.Type.BAR) === Chart.Type.BAR)) {
1724
+ if (scout.isOneOf(type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.BUBBLE, Chart.Type.SCATTER) || (type === Chart.Type.BAR && (elem.type || Chart.Type.BAR) === Chart.Type.BAR)) {
1612
1725
  let uncheckedBackgroundColor = (multipleColorsPerDataset ? colors.backgroundColors : arrays.init(datasetLength, colors.backgroundColors[idx])),
1613
1726
  uncheckedHoverBackgroundColor = (multipleColorsPerDataset ? colors.hoverBackgroundColors : arrays.init(datasetLength, colors.hoverBackgroundColors[idx])),
1614
1727
 
@@ -1816,7 +1929,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1816
1929
  checkedBackgroundOpacity = 0.2;
1817
1930
  checkedHoverBackgroundOpacity = 0.35;
1818
1931
  checkedHoverBackgroundDarker = 0;
1819
- } else if (type === Chart.Type.BUBBLE) {
1932
+ } else if (scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1820
1933
  backgroundOpacity = 0.2;
1821
1934
  hoverBackgroundOpacity = 0.35;
1822
1935
  hoverBackgroundDarker = 0;
@@ -1922,7 +2035,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1922
2035
  elem.text = strings.truncateText(elem.text, horizontalSpace, measureText);
1923
2036
  let dataset = data.datasets[idx],
1924
2037
  legendColor, borderColor, backgroundColor;
1925
- if (dataset && scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
2038
+ if (dataset && scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1926
2039
  legendColor = dataset.legendColor;
1927
2040
  borderColor = this._adjustColorOpacity(dataset.borderColor, 1);
1928
2041
  } else if (data.datasets.length && scout.isOneOf(config.type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA)) {
@@ -2086,7 +2199,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2086
2199
  datasetIndex: datasetIndex,
2087
2200
  dataIndex: itemIndex
2088
2201
  };
2089
- if (this.chartJs.config.type === Chart.Type.BUBBLE) {
2202
+ if (scout.isOneOf(this.chartJs.config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2090
2203
  let data = this.chartJs.config.data.datasets[datasetIndex].data[itemIndex];
2091
2204
  clickObject.xIndex = data.x;
2092
2205
  clickObject.yIndex = data.y;
@@ -2285,7 +2398,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2285
2398
  datasets = config.data.datasets,
2286
2399
  dataset = datasets ? datasets[datasetIndex] : null,
2287
2400
  datasetType = dataset ? dataset.type : null;
2288
- if (scout.isOneOf(type, Chart.Type.LINE, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA) || datasetType === Chart.Type.LINE) {
2401
+ if (scout.isOneOf(type, Chart.Type.LINE, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.SCATTER) || datasetType === Chart.Type.LINE) {
2289
2402
  mode = 'point';
2290
2403
  } else {
2291
2404
  mode = 'dataset';
@@ -2342,7 +2455,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2342
2455
  data.r = Math.sqrt(data.z);
2343
2456
  }
2344
2457
  }));
2345
- let maxMinR = this._computeMaxMinValue(datasets, 'r', true),
2458
+ let maxMinR = this._computeMaxMinValue(config, datasets, 'r', true),
2346
2459
  maxR = maxMinR.maxValue,
2347
2460
  minR = maxMinR.minValue,
2348
2461
  // Compute a scalingFactor and an offset to get the new radius newR = r * scalingFactor + offset.
@@ -2396,29 +2509,35 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2396
2509
  }));
2397
2510
  }
2398
2511
 
2399
- _computeMaxMinValue(datasets, identifier, exact, boundRange, padding, space) {
2512
+ _computeMaxMinValue(config, datasets, identifier, exact, boundRange, padding, space) {
2400
2513
  if (!datasets) {
2401
2514
  return;
2402
2515
  }
2403
2516
 
2404
2517
  let maxValue, minValue;
2405
- for (let i = 0; i < datasets.length; i++) {
2406
- for (let j = 0; j < datasets[i].data.length; j++) {
2407
- let value;
2408
- if (identifier) {
2409
- value = datasets[i].data[j][identifier];
2410
- } else {
2411
- value = datasets[i].data[j];
2412
- }
2413
- if (isNaN(maxValue)) {
2414
- maxValue = value;
2415
- } else {
2416
- maxValue = Math.max(value, maxValue);
2417
- }
2418
- if (isNaN(minValue)) {
2419
- minValue = value;
2420
- } else {
2421
- minValue = Math.min(value, minValue);
2518
+ if (config.type === Chart.Type.SCATTER && identifier === 'r') {
2519
+ // do not move the grid boundaries because of the radii of the points (would look weird)
2520
+ maxValue = 0;
2521
+ minValue = 0;
2522
+ } else {
2523
+ for (let i = 0; i < datasets.length; i++) {
2524
+ for (let j = 0; j < datasets[i].data.length; j++) {
2525
+ let value;
2526
+ if (identifier) {
2527
+ value = datasets[i].data[j][identifier];
2528
+ } else {
2529
+ value = datasets[i].data[j];
2530
+ }
2531
+ if (isNaN(maxValue)) {
2532
+ maxValue = value;
2533
+ } else {
2534
+ maxValue = Math.max(value, maxValue);
2535
+ }
2536
+ if (isNaN(minValue)) {
2537
+ minValue = value;
2538
+ } else {
2539
+ minValue = Math.min(value, minValue);
2540
+ }
2422
2541
  }
2423
2542
  }
2424
2543
  }
@@ -2503,7 +2622,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2503
2622
  }
2504
2623
 
2505
2624
  let type = config.type;
2506
- if (!scout.isOneOf(type, Chart.Type.BAR, Chart.Type.LINE, Chart.Type.POLAR_AREA, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
2625
+ if (!scout.isOneOf(type, Chart.Type.BAR, Chart.Type.LINE, Chart.Type.POLAR_AREA, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2507
2626
  return;
2508
2627
  }
2509
2628
 
@@ -2542,16 +2661,16 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2542
2661
  this._adjustAxisMaxMin(yAxis, maxYTicks, yBoundary);
2543
2662
  }
2544
2663
 
2545
- if (type !== Chart.Type.BUBBLE) {
2664
+ if (!scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2546
2665
  return;
2547
2666
  }
2548
2667
 
2549
- let xBoundary = this._computeXBoundaryBubble(config, width);
2668
+ let xBoundary = this._computeXBoundaryPointElement(config, width);
2550
2669
  this._adjustAxisMaxMin(xAxis, maxXTicks, xBoundary);
2551
2670
  }
2552
2671
 
2553
- _computeBoundaryBubble(config, identifier, space) {
2554
- if (!config || !config.type || config.type !== Chart.Type.BUBBLE || !config.data || !config.options || !config.options.scales || !(identifier === 'x' || identifier === 'y') || !space) {
2672
+ _computeBoundaryPointElement(config, identifier, space) {
2673
+ if (!config || !config.type || !scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER) || !config.data || !config.options || !config.options.scales || !(identifier === 'x' || identifier === 'y') || !space) {
2555
2674
  return;
2556
2675
  }
2557
2676
 
@@ -2561,16 +2680,16 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2561
2680
  labelMap = config.options[identifier + 'LabelMap'],
2562
2681
  boundary;
2563
2682
 
2564
- let maxR = this._computeMaxMinValue(datasets, 'r', true).maxValue,
2683
+ let maxR = this._computeMaxMinValue(config, datasets, 'r', true).maxValue,
2565
2684
  padding = maxR;
2566
2685
  if (config.options.elements && config.options.elements.point && config.options.elements.point.hoverRadius) {
2567
2686
  padding = padding + config.options.elements.point.hoverRadius;
2568
2687
  }
2569
2688
 
2570
2689
  if (offset) {
2571
- boundary = this._computeMaxMinValue(datasets, identifier, labelMap, true);
2690
+ boundary = this._computeMaxMinValue(config, datasets, identifier, labelMap, true);
2572
2691
  } else {
2573
- boundary = this._computeMaxMinValue(datasets, identifier, labelMap, true, padding, space);
2692
+ boundary = this._computeMaxMinValue(config, datasets, identifier, labelMap, true, padding, space);
2574
2693
  }
2575
2694
  if (labelMap) {
2576
2695
  boundary.maxValue = Math.ceil(boundary.maxValue);
@@ -2579,12 +2698,12 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2579
2698
  return boundary;
2580
2699
  }
2581
2700
 
2582
- _computeXBoundaryBubble(config, width) {
2583
- return this._computeBoundaryBubble(config, 'x', width);
2701
+ _computeXBoundaryPointElement(config, width) {
2702
+ return this._computeBoundaryPointElement(config, 'x', width);
2584
2703
  }
2585
2704
 
2586
- _computeYBoundaryBubble(config, height) {
2587
- return this._computeBoundaryBubble(config, 'y', height);
2705
+ _computeYBoundaryPointElement(config, height) {
2706
+ return this._computeBoundaryPointElement(config, 'y', height);
2588
2707
  }
2589
2708
 
2590
2709
  _computeYBoundaries(config, height) {
@@ -2596,8 +2715,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2596
2715
  yBoundary,
2597
2716
  yBoundaryDiffType;
2598
2717
 
2599
- if (type === Chart.Type.BUBBLE) {
2600
- yBoundary = this._computeYBoundaryBubble(config, height);
2718
+ if (scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2719
+ yBoundary = this._computeYBoundaryPointElement(config, height);
2601
2720
  } else {
2602
2721
  let datasets = [],
2603
2722
  datasetsDiffType = [];
@@ -2611,10 +2730,10 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2611
2730
  });
2612
2731
  }
2613
2732
 
2614
- yBoundary = this._computeMaxMinValue(datasets);
2733
+ yBoundary = this._computeMaxMinValue(config, datasets);
2615
2734
 
2616
2735
  if (datasets.length && datasetsDiffType.length) {
2617
- yBoundaryDiffType = this._computeMaxMinValue(datasetsDiffType);
2736
+ yBoundaryDiffType = this._computeMaxMinValue(config, datasetsDiffType);
2618
2737
  let yBoundaryRange = yBoundary.maxValue - yBoundary.minValue,
2619
2738
  yBoundaryRangeDiffType = yBoundaryDiffType.maxValue - yBoundaryDiffType.minValue;
2620
2739
  if (yBoundaryRange && yBoundaryRangeDiffType && (yBoundaryRange / yBoundaryRangeDiffType > 10 || yBoundaryRangeDiffType / yBoundaryRange > 10)) {