Skip to content

Commit

Permalink
Added format and formatter options for series.label
Browse files Browse the repository at this point in the history
  • Loading branch information
TorsteinHonsi committed Mar 26, 2020
1 parent a03e27a commit d7910a3
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 142 deletions.
154 changes: 95 additions & 59 deletions js/modules/series-label.src.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import H from '../parts/Globals.js';
* https://jsfiddle.net/highcharts/y5A37/
*/
import U from '../parts/Utilities.js';
var addEvent = U.addEvent, animObject = U.animObject, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, pick = U.pick, syncTimeout = U.syncTimeout;
var addEvent = U.addEvent, animObject = U.animObject, extend = U.extend, fireEvent = U.fireEvent, format = U.format, isNumber = U.isNumber, pick = U.pick, syncTimeout = U.syncTimeout;
import '../parts/Chart.js';
import '../parts/Series.js';
var labelDistance = 3, Series = H.Series, SVGRenderer = H.SVGRenderer, Chart = H.Chart;
Expand Down Expand Up @@ -92,6 +92,26 @@ H.setOptions({
* connector.
*/
connectorNeighbourDistance: 24,
/**
* A format string for the label, with support for a subset of
* HTML. Variables are enclosed by curly brackets. Available
* variables are `name`, `options.xxx`, `color` and other
* members from the `series` object. Use this option also to set
* a static text for the label.
*
* @type string
* @since next
*/
format: void 0,
/**
* Callback function to format each of the series' labels. The
* `this` keyword refers to the series object. By default the
* `formatter` is undefined and the `series.name` is rendered.
*
* @type {Highcharts.FormatterCallbackFunction<Series>}
* @since next
*/
formatter: void 0,
/**
* For area-like series, allow the font size to vary so that
* small areas get a smaller font size. The default applies this
Expand Down Expand Up @@ -464,10 +484,11 @@ Chart.prototype.drawSeriesLabels = function () {
});
});
chart.series.forEach(function (series) {
if (!series.xAxis && !series.yAxis) {
var labelOptions = series.options.label;
if (!labelOptions || (!series.xAxis && !series.yAxis)) {
return;
}
var bBox, x, y, results = [], clearPoint, i, best, labelOptions = series.options.label, inverted = chart.inverted, paneLeft = (inverted ? series.yAxis.pos : series.xAxis.pos), paneTop = (inverted ? series.xAxis.pos : series.yAxis.pos), paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len, paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len, points = series.interpolatedPoints, onArea = pick(labelOptions.onArea, !!series.area), label = series.labelBySeries, isNew = !label, minFontSize = labelOptions.minFontSize, maxFontSize = labelOptions.maxFontSize, dataExtremes, areaMin, areaMax, colorClass = 'highcharts-color-' + pick(series.colorIndex, 'none');
var bBox, x, y, results = [], clearPoint, i, best, inverted = chart.inverted, paneLeft = (inverted ? series.yAxis.pos : series.xAxis.pos), paneTop = (inverted ? series.xAxis.pos : series.yAxis.pos), paneWidth = chart.inverted ? series.yAxis.len : series.xAxis.len, paneHeight = chart.inverted ? series.xAxis.len : series.yAxis.len, points = series.interpolatedPoints, onArea = pick(labelOptions.onArea, !!series.area), label = series.labelBySeries, isNew = !label, minFontSize = labelOptions.minFontSize, maxFontSize = labelOptions.maxFontSize, dataExtremes, areaMin, areaMax, colorClass = 'highcharts-color-' + pick(series.colorIndex, 'none');
// Stay within the area data bounds (#10038)
if (onArea && !inverted) {
dataExtremes = [
Expand Down Expand Up @@ -497,8 +518,15 @@ Chart.prototype.drawSeriesLabels = function () {
}
if (series.visible && !series.isSeriesBoosting && points) {
if (!label) {
var labelText = series.name;
if (typeof labelOptions.format === 'string') {
labelText = format(labelOptions.format, series, chart);
}
else if (labelOptions.formatter) {
labelText = labelOptions.formatter.call(series);
}
series.labelBySeries = label = chart.renderer
.label(series.name, 0, -9999, 'connector')
.label(labelText, 0, -9999, 'connector')
.addClass('highcharts-series-label ' +
'highcharts-series-label-' + series.index + ' ' +
(series.options.className || '') +
Expand All @@ -508,7 +536,12 @@ Chart.prototype.drawSeriesLabels = function () {
color: onArea ?
chart.renderer.getContrast(series.color) :
series.color
}, series.options.label.style));
}, labelOptions.style || {}));
label.attr({
opacity: chart.renderer.forExport ? 1 : 0,
stroke: series.color,
'stroke-width': 1
});
}
// Adapt label sizes to the sum of the data
if (minFontSize && maxFontSize) {
Expand All @@ -519,9 +552,6 @@ Chart.prototype.drawSeriesLabels = function () {
label
.attr({
padding: 0,
opacity: chart.renderer.forExport ? 1 : 0,
stroke: series.color,
'stroke-width': 1,
zIndex: 3
})
.add();
Expand Down Expand Up @@ -605,7 +635,7 @@ Chart.prototype.drawSeriesLabels = function () {
});
// Move it if needed
var dist = Math.sqrt(Math.pow(Math.abs(best.x - label.x), 2), Math.pow(Math.abs(best.y - label.y), 2));
if (dist) {
if (dist && series.labelBySeries) {
// Move fast and fade in - pure animation movement is
// distractive...
var attr = {
Expand All @@ -623,32 +653,35 @@ Chart.prototype.drawSeriesLabels = function () {
};
attr = {};
}
// Default initial animation to a fraction of the series
// animation (#9396)
var animationOptions = void 0;
if (isNew) {
animationOptions = animObject(series.options.animation);
animationOptions.duration *= 0.2;
}
series.labelBySeries
.attr(extend(attr, {
anchorX: best.connectorPoint &&
best.connectorPoint.plotX + paneLeft,
anchorY: best.connectorPoint &&
best.connectorPoint.plotY + paneTop
}))
.animate(anim, isNew ?
// Default initial animation to a fraction of
// the series animation (#9396)
animObject(series.options.animation).duration * 0.2 :
// On updating, default to the general chart
// animation
chart.renderer.globalAnimation);
.animate(anim, animationOptions);
// Record closest point to stick to for sync redraw
series.options.kdNow = true;
series.buildKDTree();
var closest = series.searchPoint({
chartX: best.x,
chartY: best.y
}, true);
label.closest = [
closest,
best.x - closest.plotX,
best.y - closest.plotY
];
if (closest) {
label.closest = [
closest,
best.x - (closest.plotX || 0),
best.y - (closest.plotY || 0)
];
}
}
}
else {
Expand All @@ -670,47 +703,50 @@ Chart.prototype.drawSeriesLabels = function () {
* @function drawLabels
*/
function drawLabels(e) {
var chart = this, delay = animObject(chart.renderer.globalAnimation).duration;
chart.labelSeries = [];
chart.labelSeriesMaxSum = 0;
U.clearTimeout(chart.seriesLabelTimer);
// Which series should have labels
chart.series.forEach(function (series) {
var options = series.options.label, label = series.labelBySeries, closest = label && label.closest;
if (options.enabled &&
series.visible &&
(series.graph || series.area) &&
!series.isSeriesBoosting) {
chart.labelSeries.push(series);
if (options.minFontSize && options.maxFontSize) {
series.sum = series.yData.reduce(function (pv, cv) {
return (pv || 0) + (cv || 0);
}, 0);
chart.labelSeriesMaxSum = Math.max(chart.labelSeriesMaxSum, series.sum);
}
// The labels are processing heavy, wait until the animation is done
if (e.type === 'load') {
delay = Math.max(delay, animObject(series.options.animation).duration);
}
// Keep the position updated to the axis while redrawing
if (closest) {
if (typeof closest[0].plotX !== 'undefined') {
label.animate({
x: closest[0].plotX + closest[1],
y: closest[0].plotY + closest[2]
});
if (this.renderer) {
var chart = this, delay = animObject(chart.renderer.globalAnimation).duration;
chart.labelSeries = [];
chart.labelSeriesMaxSum = 0;
U.clearTimeout(chart.seriesLabelTimer);
// Which series should have labels
chart.series.forEach(function (series) {
var options = series.options.label, label = series.labelBySeries, closest = label && label.closest;
if (options.enabled &&
series.visible &&
(series.graph || series.area) &&
!series.isSeriesBoosting) {
chart.labelSeries.push(series);
if (options.minFontSize && options.maxFontSize) {
series.sum = series.yData.reduce(function (pv, cv) {
return (pv || 0) + (cv || 0);
}, 0);
chart.labelSeriesMaxSum = Math.max(chart.labelSeriesMaxSum, series.sum);
}
else {
label.attr({ opacity: 0 });
// The labels are processing heavy, wait until the animation is
// done
if (e.type === 'load') {
delay = Math.max(delay, animObject(series.options.animation).duration);
}
// Keep the position updated to the axis while redrawing
if (closest) {
if (typeof closest[0].plotX !== 'undefined') {
label.animate({
x: closest[0].plotX + closest[1],
y: closest[0].plotY + closest[2]
});
}
else {
label.attr({ opacity: 0 });
}
}
}
}
});
chart.seriesLabelTimer = syncTimeout(function () {
if (chart.series && chart.labelSeries) { // #7931, chart destroyed
chart.drawSeriesLabels();
}
}, chart.renderer.forExport || !delay ? 0 : delay);
});
chart.seriesLabelTimer = syncTimeout(function () {
if (chart.series && chart.labelSeries) { // #7931, chart destroyed
chart.drawSeriesLabels();
}
}, chart.renderer.forExport || !delay ? 0 : delay);
}
}
// Leave both events, we handle animation differently (#9815)
addEvent(Chart, 'load', drawLabels);
Expand Down
3 changes: 3 additions & 0 deletions samples/unit-tests/datalabels/members/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,7 @@ QUnit.test('Series.drawDataLabels', function (assert) {
false,
'Should destroy dataLabel when series.formatter returns a value not of type string'
);

// Revert to prototype
delete renderer.label;
});
5 changes: 5 additions & 0 deletions samples/unit-tests/series-label/series-label/demo.details
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
resources:
- https://code.jquery.com/qunit/qunit-2.0.1.js
- https://code.jquery.com/qunit/qunit-2.0.1.css
...
8 changes: 8 additions & 0 deletions samples/unit-tests/series-label/series-label/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>


<div id="qunit"></div>
<div id="qunit-fixture"></div>

<div id="container"></div>
46 changes: 46 additions & 0 deletions samples/unit-tests/series-label/series-label/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
QUnit.test('Series label', function (assert) {
const chart = Highcharts.chart('container', {
chart: {
width: 400
},
series: [{
data: [1, 3, 2, 4],
label: {
enabled: true
}
}, {
data: [1, 3, 2, 4],
label: {
format: 'Format {name}',
formatter: function () {
return 'Formatter ' + this.name;
}
}
}, {
data: [1, 3, 2, 4],
label: {
formatter: function () {
return 'Formatter ' + this.name;
}
}
}]
});

assert.strictEqual(
chart.series[0].labelBySeries.text.textStr,
'Series 1',
'Default series label should be series name'
);

assert.strictEqual(
chart.series[1].labelBySeries.text.textStr,
'Format Series 2',
'Series label with format should take precedence'
);

assert.strictEqual(
chart.series[2].labelBySeries.text.textStr,
'Formatter Series 3',
'Series label with formatter'
);
});
1 change: 1 addition & 0 deletions test/karma-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"code/modules/price-indicator.src.js",
"code/modules/sankey.src.js",
"code/modules/dependency-wheel.src.js",
"code/modules/series-label.src.js",
"code/modules/solid-gauge.src.js",
"code/modules/sonification.src.js",
"code/modules/stock-tools.src.js",
Expand Down
Loading

0 comments on commit d7910a3

Please sign in to comment.