Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFritz Schinkel2020-10-27 15:48:04 +0000
committerFritz Schinkel2020-10-28 11:50:43 +0000
commitb978c2c4ea71317d00b672af3055fa41d491a771 (patch)
tree1fbd4dc0e55079584c63cd7e6975c8ab3578d1df
parent7fcbfaf779030e7e43c57c3e39ffa36d776032bb (diff)
downloadorg.eclipse.scout.rt-features/cs_2020.tar.gz
org.eclipse.scout.rt-features/cs_2020.tar.xz
org.eclipse.scout.rt-features/cs_2020.zip
Charts: Several Improvementsfeatures/cs_2020
Add tooltip delay. Complete border for checkable bars. Prevent too small bubbles. Enlarge margin top for chart on ChartTableControl. Enlarge hitRadius for line charts. Darker grid for not inverted polar area charts. Change-Id: I1d657de4d1b44673ef74210abe7d465e626011b5 Reviewed-on: https://git.eclipse.org/r/c/scout/org.eclipse.scout.rt/+/171369 Tested-by: Scout Bot <scout-bot@eclipse.org> Reviewed-by: Fritz Schinkel <fritz.schinkel@bsi-software.com>
-rw-r--r--eclipse-scout-chart/src/chart/Chart.less5
-rw-r--r--eclipse-scout-chart/src/chart/ChartJsRenderer.js128
-rw-r--r--eclipse-scout-chart/src/chart/ChartJsTooltipDelay.js124
-rw-r--r--eclipse-scout-chart/src/table/controls/ChartTableControl.js16
-rw-r--r--eclipse-scout-chart/src/table/controls/ChartTableControl.less2
-rw-r--r--eclipse-scout-chart/test/chart/ChartJsRendererSpec.js148
-rw-r--r--org.eclipse.scout.rt.chart.shared/src/main/java/org/eclipse/scout/rt/chart/shared/data/basic/chart/IChartConfig.java1
7 files changed, 393 insertions, 31 deletions
diff --git a/eclipse-scout-chart/src/chart/Chart.less b/eclipse-scout-chart/src/chart/Chart.less
index 8278c17f40..be67df3e3c 100644
--- a/eclipse-scout-chart/src/chart/Chart.less
+++ b/eclipse-scout-chart/src/chart/Chart.less
@@ -516,6 +516,11 @@
& > .elements {
#scout.chart-label-grid-colors();
+
+ :not(.inverted) > & > .grid {
+ fill: darken(@chart-axis-line-color, 15);
+ }
+
#scout.chart-auto-colors-schemes(@opacity: 70);
#scout.chart-stroke-colors-schemes(
@tile-default-background-color,
diff --git a/eclipse-scout-chart/src/chart/ChartJsRenderer.js b/eclipse-scout-chart/src/chart/ChartJsRenderer.js
index aa3cdb469b..c5ce2ea143 100644
--- a/eclipse-scout-chart/src/chart/ChartJsRenderer.js
+++ b/eclipse-scout-chart/src/chart/ChartJsRenderer.js
@@ -13,6 +13,8 @@ import ChartJs from 'chart.js';
import {Event, styles, arrays, strings} from '@eclipse-scout/core';
// noinspection ES6UnusedImports
import chartjs_plugin_datalabels from 'chartjs-plugin-datalabels';
+// noinspection ES6UnusedImports
+import ChartJsTooltipDelay from './ChartJsTooltipDelay';
/**
* @typedef ChartJs
@@ -34,11 +36,13 @@ ChartJs.defaults.global.elements.line.tension = 0;
ChartJs.defaults.global.elements.line.fill = false;
ChartJs.defaults.global.elements.line.borderWidth = 2;
ChartJs.defaults.global.elements.point.radius = 0;
-ChartJs.defaults.global.elements.point.hitRadius = 5;
+ChartJs.defaults.global.elements.point.hitRadius = 10;
ChartJs.defaults.global.elements.point.hoverRadius = 5;
ChartJs.defaults.global.elements.point.hoverBorderWidth = 2;
ChartJs.defaults.global.elements.arc.borderWidth = 1;
ChartJs.defaults.global.elements.rectangle.borderWidth = 1;
+ChartJs.defaults.global.elements.rectangle.borderSkipped = '';
+ChartJs.defaults.horizontalBar.elements.rectangle.borderSkipped = '';
ChartJs.defaults.global.tooltips.borderWidth = 1;
ChartJs.defaults.global.tooltips.cornerRadius = 4;
ChartJs.defaults.global.tooltips.xPadding = 8;
@@ -86,6 +90,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
this.resetDatasetAfterHover = false;
+ this._resizeHandler = this._onResize.bind(this);
+
this._labelFormatter = this._formatLabel.bind(this);
this._xLabelFormatter = this._formatXLabel.bind(this);
this._yLabelFormatter = this._formatYLabel.bind(this);
@@ -220,7 +226,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
* @property {object} chartArea
*/
this.chartJs = new ChartJs(this.$canvas[0].getContext('2d'), config);
- this._adjustGrid(this.chartJs.config, this.chartJs.chartArea);
+ this._adjustSize(this.chartJs.config, this.chartJs.chartArea);
this.chartJs.update();
}
@@ -377,24 +383,18 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
config.data.labels = newLabels;
config.data.maxSegmentsExceeded = true;
}
- if (config.type === Chart.Type.BUBBLE) {
- config.data.datasets.forEach(dataset => dataset.data.forEach(data => {
- if (isNaN(data.r) && !isNaN(data.z)) {
- data.r = Math.sqrt(data.z);
- }
- }));
- if (!(config.bubble || {}).bubbleScalingFactor) {
- let maxR = this._computeMaxMinValue(config.data.datasets, 'r', true).maxValue,
- bubbleScalingFactor = 1;
- if (maxR > 0 && (config.bubble || {}).sizeOfLargestBubble) {
- bubbleScalingFactor = Math.min(config.bubble.sizeOfLargestBubble, Math.floor(this.$canvas.cssWidth() / 8), Math.floor(this.$canvas.cssHeight() / 6)) / maxR;
- config.data.datasets.forEach(dataset => dataset.data.forEach(data => {
- data.r = data.r * bubbleScalingFactor;
- }));
- }
- config.bubble = $.extend(true, {}, config.bubble, {bubbleScalingFactor: bubbleScalingFactor});
- }
+
+ if (config.type !== Chart.Type.BUBBLE) {
+ return;
}
+
+ config.data.datasets.forEach(dataset => dataset.data.forEach(data => {
+ if (!isNaN(data.r)) {
+ data.z = Math.pow(data.r, 2);
+ } else if (!isNaN(data.z)) {
+ data.r = Math.sqrt(data.z);
+ }
+ }));
}
_adjustLayout(config) {
@@ -417,6 +417,9 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
}
}
});
+ if (config.options.handleResize) {
+ config.options.onResize = this._resizeHandler;
+ }
if (scout.isOneOf(type, Chart.Type.POLAR_AREA, Chart.Type.RADAR)) {
config.options = $.extend(true, {}, config.options, {
scale: {}
@@ -522,6 +525,11 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
}
}
+ _onResize(chart, size) {
+ chart.update();
+ this._adjustSize(chart.config, chart.chartArea);
+ }
+
_formatLabel(label) {
return this._formatLabelMap(label, null, ((this.chartJs || {config: {}}).config.options || {}).numberFormatter);
}
@@ -617,7 +625,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
_formatDatalabels(value, context) {
if (context.chart.config.type === Chart.Type.BUBBLE) {
- return this._formatLabel(Math.pow(value.r / (context.chart.config.bubble.bubbleScalingFactor || 1), 2));
+ return this._formatLabel(value.z);
}
return this._formatLabel(value);
}
@@ -970,7 +978,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
value = this._formatLabel(dataset.data[tooltipItem.index]);
} else if (config.type === Chart.Type.BUBBLE) {
label = dataset.label;
- value = this._formatLabel(Math.pow(dataset.data[tooltipItem.index].r / (config.bubble.bubbleScalingFactor || 1), 2));
+ value = this._formatLabel(dataset.data[tooltipItem.index].z);
} else {
let defaultLabel = (((ChartJs.defaults[config.type] || {}).tooltips || {}).callbacks || {}).label || ChartJs.defaults.global.tooltips.callbacks.label;
label = defaultLabel.call(this.chartJs, tooltipItem, data);
@@ -1013,6 +1021,72 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
return defaultLabelColor.call(chart, tooltipItem, chart);
}
+ _adjustSize(config, chartArea) {
+ this._adjustBubbleSizes(config, chartArea);
+ this._adjustGrid(config, chartArea);
+ }
+
+ _adjustBubbleSizes(config, chartArea) {
+ if (config.type !== Chart.Type.BUBBLE) {
+ return;
+ }
+ // Scale all bubbles so that the largest radius is equal to sizeOfLargestBubble and the smallest greater than or equal to minBubbleSize.
+ // First reset all radii.
+ config.data.datasets.forEach(dataset => dataset.data.forEach(data => {
+ if (!isNaN(data.z)) {
+ data.r = Math.sqrt(data.z);
+ }
+ }));
+ let maxMinR = this._computeMaxMinValue(config.data.datasets, 'r', true),
+ maxR = maxMinR.maxValue,
+ minR = maxMinR.minValue,
+ // Compute a scalingFactor and an offset to get the new radius newR = r * scalingFactor + offset.
+ bubbleScalingFactor = 1,
+ bubbleRadiusOffset = 0;
+ if ((config.bubble || {}).sizeOfLargestBubble) {
+ let width = Math.abs(chartArea.right - chartArea.left),
+ height = Math.abs(chartArea.top - chartArea.bottom),
+ sizeOfLargestBubble = Math.min(config.bubble.sizeOfLargestBubble, Math.floor(Math.min(width, height) / 6));
+ if (maxR === 0) {
+ // If maxR is equal to 0, all radii are equal to 0, therefore set bubbleRadiusOffset to sizeOfLargestBubble.
+ bubbleRadiusOffset = sizeOfLargestBubble;
+ } else if ((config.bubble || {}).minBubbleSize && config.bubble.sizeOfLargestBubble > config.bubble.minBubbleSize && (minR / maxR) < (config.bubble.minBubbleSize / sizeOfLargestBubble)) {
+ // If minR/maxR is smaller than minBubbleSize/sizeOfLargestBubble, then it is not sufficient to scale all radii.
+
+ // The scalingFactor and the result from the following two conditions:
+ // (1) minBubbleSize = offset + scalingFactor * minR
+ // (2) sizeOfLargestBubble = offset + scalingFactor * maxR
+
+ // Therefore
+ // (1*) offset = minBubbleSize - scalingFactor * minR
+ // (2*) offset = sizeOfLargestBubble - scalingFactor * maxR
+
+ // (1*) = (2*):
+ // minBubbleSize - scalingFactor * minR = sizeOfLargestBubble - scalingFactor * maxR
+ // <=> scalingFactor * maxR - scalingFactor * minR = sizeOfLargestBubble - minBubbleSize
+ // <=> scalingFactor * (maxR - minR) = sizeOfLargestBubble - minBubbleSize
+ // <=> scalingFactor = (sizeOfLargestBubble - minBubbleSize) / (maxR - minR)
+ bubbleScalingFactor = (sizeOfLargestBubble - config.bubble.minBubbleSize) / (maxR - minR);
+ bubbleRadiusOffset = config.bubble.minBubbleSize - bubbleScalingFactor * minR;
+ } else {
+ // Scaling is sufficient.
+ bubbleScalingFactor = sizeOfLargestBubble / maxR;
+ }
+ } else if ((config.bubble || {}).minBubbleSize && config.bubble.minBubbleSize > minR) {
+ // sizeOfLargestBubble is not set
+ if (minR === 0) {
+ // If the smallest radius equals 0 scaling will have no effect.
+ bubbleRadiusOffset = config.bubble.minBubbleSize;
+ } else {
+ // Scaling is sufficient.
+ bubbleScalingFactor = config.bubble.minBubbleSize / minR;
+ }
+ }
+ config.data.datasets.forEach(dataset => dataset.data.forEach(data => {
+ data.r = data.r * bubbleScalingFactor + bubbleRadiusOffset;
+ }));
+ }
+
_adjustGrid(config, chartArea) {
if (!config || !config.type || !config.options || (!config.options.scale && !config.options.scales) || !chartArea) {
return;
@@ -1037,6 +1111,10 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
} else {
yBoundaries = this._computeMaxMinValue(config.data.datasets, 'y', config.options.scales.yLabelMap, true, padding, height);
}
+ if (config.options.scales.yLabelMap) {
+ yBoundaries.maxValue = Math.ceil(yBoundaries.maxValue);
+ yBoundaries.minValue = Math.floor(yBoundaries.minValue);
+ }
} else {
let datasets = [],
datasetsDiffType = [];
@@ -1126,6 +1204,10 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
} else {
xBoundaries = this._computeMaxMinValue(config.data.datasets, 'x', config.options.scales.xLabelMap, true, padding, width);
}
+ if (config.options.scales.xLabelMap) {
+ xBoundaries.maxValue = Math.ceil(xBoundaries.maxValue);
+ xBoundaries.minValue = Math.floor(xBoundaries.minValue);
+ }
this._adjustAxes(config.options.scales.xAxes, maxXTicks, xBoundaries);
}
@@ -1204,8 +1286,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
}
return {
- maxValue: Math.ceil(maxBoundary),
- minValue: Math.floor(minBoundary)
+ maxValue: maxBoundary,
+ minValue: minBoundary
};
}
diff --git a/eclipse-scout-chart/src/chart/ChartJsTooltipDelay.js b/eclipse-scout-chart/src/chart/ChartJsTooltipDelay.js
new file mode 100644
index 0000000000..71e783ef8c
--- /dev/null
+++ b/eclipse-scout-chart/src/chart/ChartJsTooltipDelay.js
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * BSI Business Systems Integration AG - initial API and implementation
+ */
+import {App, Device} from '@eclipse-scout/core';
+import ChartJs from 'chart.js';
+
+ChartJs.defaults.global.plugins.tooltipDelay = {
+ showTooltipDelay: 700,
+ resetTooltipDelay: 200
+};
+
+let pluginId = 'tooltipDelay';
+
+/**
+ * copied from chart.js core_plugins.notify
+ */
+let _notifyOthers = (chart, hook, args) => {
+ // <customized>
+ let descriptors = chart.$plugins.descriptors;
+ // </customized>
+ let ilen = descriptors.length;
+ let i, descriptor, plugin, params, method;
+
+ for (i = 0; i < ilen; ++i) {
+ descriptor = descriptors[i];
+ plugin = descriptor.plugin;
+ // <customized>
+ if (plugin.id === pluginId) {
+ continue;
+ }
+ // </customized>
+ method = plugin[hook];
+ if (typeof method === 'function') {
+ params = [chart].concat(args || []);
+ params.push(descriptor.options);
+ if (method.apply(plugin, params) === false) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+};
+
+/**
+ * copied from chart.js _drawTooltip
+ */
+let _drawTooltip = (chart, args) => {
+ // <customized>
+ let tooltip = chart.tooltip;
+
+ if (_notifyOthers(chart, 'beforeTooltipDraw', [args]) === false) {
+ // </customized>
+ return;
+ }
+
+ tooltip.draw();
+
+ // <customized>
+ _notifyOthers(chart, 'afterTooltipDraw', [args]);
+ // </customized>
+};
+
+/**
+ * @typedef IPlugin
+ */
+
+/**
+ * @type IPlugin
+ */
+export default class ChartJsTooltipDelay {
+ constructor() {
+ this.id = pluginId;
+ this._chartTooltipVisible = false;
+ this._chartTooltipShowTimeoutIds = []; // timeoutIds
+ this._chartTooltipHideTimeoutIds = []; // timeoutIds
+ }
+
+ beforeTooltipDraw(chart, args, options) {
+ let tooltip = chart.tooltip,
+ showTooltipDelay = options.showTooltipDelay,
+ resetTooltipDelay = options.resetTooltipDelay;
+
+ if ((tooltip._active || []).length) {
+ if (!this._chartTooltipVisible) {
+ this._chartTooltipShowTimeoutIds.push(setTimeout(() => {
+ this._chartTooltipShowTimeoutIds = [];
+ this._chartTooltipVisible = true;
+ _drawTooltip(chart, args);
+ }, showTooltipDelay));
+ } else {
+ this._chartTooltipHideTimeoutIds.forEach(tid => clearTimeout(tid));
+ this._chartTooltipHideTimeoutIds = [];
+ _drawTooltip(chart, args);
+ }
+ } else {
+ if (this._chartTooltipVisible) {
+ this._chartTooltipHideTimeoutIds.push(setTimeout(() => {
+ this._chartTooltipHideTimeoutIds = [];
+ this._chartTooltipVisible = false;
+ }, resetTooltipDelay));
+ _drawTooltip(chart, args);
+ } else {
+ this._chartTooltipShowTimeoutIds.forEach(tid => clearTimeout(tid));
+ this._chartTooltipShowTimeoutIds = [];
+ }
+ }
+
+ return false;
+ }
+}
+
+App.addListener('init', () => {
+ if (!Device.get().supportsOnlyTouch()) {
+ ChartJs.plugins.register(new ChartJsTooltipDelay());
+ }
+});
diff --git a/eclipse-scout-chart/src/table/controls/ChartTableControl.js b/eclipse-scout-chart/src/table/controls/ChartTableControl.js
index 42735e7a85..20423f57c6 100644
--- a/eclipse-scout-chart/src/table/controls/ChartTableControl.js
+++ b/eclipse-scout-chart/src/table/controls/ChartTableControl.js
@@ -697,6 +697,7 @@ export default class ChartTableControl extends TableControl {
}]
},
options: {
+ handleResize: true,
maxSegments: 5,
legend: {
display: false
@@ -839,7 +840,8 @@ export default class ChartTableControl extends TableControl {
if (this.chartType === Chart.Type.BUBBLE) {
config.bubble = $.extend(true, {}, config.bubble, {
- sizeOfLargestBubble: 25
+ sizeOfLargestBubble: 25,
+ minBubbleSize: 5
});
if (!(xAxis.column instanceof NumberColumn)) {
@@ -991,6 +993,18 @@ export default class ChartTableControl extends TableControl {
otherSegmentClickable: true
});
}
+ if (this.chartType === Chart.Type.PIE) {
+ // Compensate the margin of the container so that the chart is always centered vertically
+ let margin = this.chart.$container.cssMarginTop() - this.chart.$container.cssMarginBottom();
+ config.options = $.extend(true, {}, config.options, {
+ layout: {
+ padding: {
+ top: Math.sign(margin) < 0 ? Math.abs(margin) : 0,
+ bottom: Math.sign(margin) > 0 ? margin : 0
+ }
+ }
+ });
+ }
}
_isChartClickable() {
diff --git a/eclipse-scout-chart/src/table/controls/ChartTableControl.less b/eclipse-scout-chart/src/table/controls/ChartTableControl.less
index 7a60d1476d..254c376903 100644
--- a/eclipse-scout-chart/src/table/controls/ChartTableControl.less
+++ b/eclipse-scout-chart/src/table/controls/ChartTableControl.less
@@ -204,7 +204,7 @@ svg.select-chart.selected:not(.disabled) {
.chart-container > .chart {
flex-grow: 1;
align-self: stretch;
- margin: 13px 15px 10px 0;
+ margin: 20px 15px 10px 0;
min-width: 350px;
min-height: 300px;
max-height: 600px;
diff --git a/eclipse-scout-chart/test/chart/ChartJsRendererSpec.js b/eclipse-scout-chart/test/chart/ChartJsRendererSpec.js
index cf36684e9a..adbc20e3ef 100644
--- a/eclipse-scout-chart/test/chart/ChartJsRendererSpec.js
+++ b/eclipse-scout-chart/test/chart/ChartJsRendererSpec.js
@@ -91,9 +91,6 @@ describe('ChartJsRendererSpec', () => {
let config = $.extend(true, {}, defaultScalesConfig, {
type: Chart.Type.BUBBLE,
options: {
- bubble: {
- sizeOfLargestBubble: 50
- },
scales: {
xAxes: [
{
@@ -154,9 +151,6 @@ describe('ChartJsRendererSpec', () => {
config = $.extend(true, {}, defaultScalesConfig, {
type: Chart.Type.BUBBLE,
options: {
- bubble: {
- sizeOfLargestBubble: 50
- },
scales: {
xLabelMap: labelMap,
yLabelMap: labelMap,
@@ -198,4 +192,146 @@ describe('ChartJsRendererSpec', () => {
});
});
});
+
+ describe('_adjustBubbleSizes', () => {
+ let renderer = new ChartJsRenderer({}),
+ chartArea = {
+ top: 0,
+ bottom: 300,
+ left: 0,
+ right: 750
+ },
+ defaultConfig = {
+ type: Chart.Type.BUBBLE,
+ data: {
+ datasets: [{
+ data: [
+ {z: 100},
+ {z: 81},
+ {z: 49},
+ {z: 25},
+ {z: 16}
+ ],
+ label: 'Dataset 1'
+ }]
+ },
+ bubble: {}
+ };
+
+ it('neither sizeOfLargestBubble nor minBubbleSize is set', () => {
+ let config = $.extend(true, {}, defaultConfig);
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 10, z: 100},
+ {r: 9, z: 81},
+ {r: 7, z: 49},
+ {r: 5, z: 25},
+ {r: 4, z: 16}
+ ]);
+ });
+
+ it('only sizeOfLargestBubble is set', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.sizeOfLargestBubble = 20;
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 20, z: 100},
+ {r: 18, z: 81},
+ {r: 14, z: 49},
+ {r: 10, z: 25},
+ {r: 8, z: 16}
+ ]);
+ });
+
+ it('only sizeOfLargestBubble is set, maxR equals 0', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.sizeOfLargestBubble = 20;
+
+ config.data.datasets[0].data = [
+ {z: 0},
+ {z: 0},
+ {z: 0}
+ ];
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 20, z: 0},
+ {r: 20, z: 0},
+ {r: 20, z: 0}
+ ]);
+ });
+
+ it('only minBubbleSize is set', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.minBubbleSize = 5;
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 12.5, z: 100},
+ {r: 11.25, z: 81},
+ {r: 8.75, z: 49},
+ {r: 6.25, z: 25},
+ {r: 5, z: 16}
+ ]);
+ });
+
+ it('only minBubbleSize is set, minR equals 0', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.minBubbleSize = 5;
+
+ config.data.datasets[0].data.push({z: 0});
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 15, z: 100},
+ {r: 14, z: 81},
+ {r: 12, z: 49},
+ {r: 10, z: 25},
+ {r: 9, z: 16},
+ {r: 5, z: 0}
+ ]);
+ });
+
+ it('sizeOfLargestBubble and minBubbleSize are set, scaling is sufficient', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.sizeOfLargestBubble = 20;
+ config.bubble.minBubbleSize = 5;
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 20, z: 100},
+ {r: 18, z: 81},
+ {r: 14, z: 49},
+ {r: 10, z: 25},
+ {r: 8, z: 16}
+ ]);
+ });
+
+ it('sizeOfLargestBubble and minBubbleSize are set, scaling is not sufficient', () => {
+ let config = $.extend(true, {}, defaultConfig);
+ config.bubble.sizeOfLargestBubble = 20;
+ config.bubble.minBubbleSize = 5;
+
+ config.data.datasets[0].data.push({z: 1});
+
+ renderer._adjustBubbleSizes(config, chartArea);
+
+ expect(config.data.datasets[0].data).toEqual([
+ {r: 20, z: 100},
+ {r: 9 * (5 / 3) + (10 / 3), z: 81},
+ {r: 15, z: 49},
+ {r: 5 * (5 / 3) + (10 / 3), z: 25},
+ {r: 10, z: 16},
+ {r: 5, z: 1}
+ ]);
+ });
+ });
});
diff --git a/org.eclipse.scout.rt.chart.shared/src/main/java/org/eclipse/scout/rt/chart/shared/data/basic/chart/IChartConfig.java b/org.eclipse.scout.rt.chart.shared/src/main/java/org/eclipse/scout/rt/chart/shared/data/basic/chart/IChartConfig.java
index 8ba8ff2c89..5d464a75b9 100644
--- a/org.eclipse.scout.rt.chart.shared/src/main/java/org/eclipse/scout/rt/chart/shared/data/basic/chart/IChartConfig.java
+++ b/org.eclipse.scout.rt.chart.shared/src/main/java/org/eclipse/scout/rt/chart/shared/data/basic/chart/IChartConfig.java
@@ -57,6 +57,7 @@ public interface IChartConfig extends Serializable {
String FULFILLMENT_START_VALUE = "fulfillment.startValue";
String BUBBLE_SIZE_OF_LARGEST_BUBBLE = "bubble.sizeOfLargestBubble";
+ String BUBBLE_MIN_BUBBLE_SIZE = "bubble.minBubbleSize";
IChartConfig copy();

Back to the top