From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sun, 12 Jul 2020 22:26:13 +0000 (-0700) Subject: Align the tooltip callbacks parameters with scriptable options (#7603) X-Git-Tag: v3.0.0-beta.2~51 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=30e197742d16f808b0f4b75569c64e2faa869d5f;p=thirdparty%2FChart.js.git Align the tooltip callbacks parameters with scriptable options (#7603) Align the tooltip callbacks parameters with scriptable options --- diff --git a/docs/docs/configuration/tooltip.md b/docs/docs/configuration/tooltip.md index 2cb65f11b..e4b6dfb94 100644 --- a/docs/docs/configuration/tooltip.md +++ b/docs/docs/configuration/tooltip.md @@ -99,7 +99,7 @@ Allows filtering of [tooltip items](#tooltip-item-interface). Must implement at The tooltip label configuration is nested below the tooltip configuration using the `callbacks` key. The tooltip has the following callbacks for providing text. For all functions, `this` will be the tooltip object created from the `Tooltip` constructor. -All functions are called with the same arguments: a [tooltip item](#tooltip-item-interface) and the `data` object passed to the chart. All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text. +All functions are called with the same arguments: a [tooltip item context](#tooltip-item-interface). All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text. | Name | Arguments | Description | ---- | --------- | ----------- @@ -128,14 +128,14 @@ var chart = new Chart(ctx, { options: { tooltips: { callbacks: { - label: function(tooltipItem, data) { - var label = data.datasets[tooltipItem.datasetIndex].label || ''; + label: function(context) { + var label = context.dataset.label || ''; if (label) { label += ': '; } - if (!helpers.isNullOrUndef(tooltipItem.value)) { - label += '$' + tooltipItem.value; + if (!helpers.isNullOrUndef(context.value)) { + label += '$' + context.value; } return label; } @@ -156,13 +156,13 @@ var chart = new Chart(ctx, { options: { tooltips: { callbacks: { - labelColor: function(tooltipItem, chart) { + labelColor: function(context) { return { borderColor: 'rgb(255, 0, 0)', backgroundColor: 'rgb(255, 0, 0)' }; }, - labelTextColor: function(tooltipItem, chart) { + labelTextColor: function(context) { return '#543453'; } } @@ -172,23 +172,29 @@ var chart = new Chart(ctx, { ``` -### Tooltip Item Interface +### Tooltip Item Context The tooltip items passed to the tooltip callbacks implement the following interface. ```javascript { + // The chart the tooltip is being shown on + chart: Chart + // Label for the tooltip label: string, // Value for the tooltip value: string, + // The dataset the item comes from + dataset: object + // Index of the dataset the item comes from datasetIndex: number, // Index of this data item in the dataset - index: number + dataIndex: number } ``` diff --git a/docs/docs/getting-started/v3-migration.md b/docs/docs/getting-started/v3-migration.md index d6de6a1f2..8db298d43 100644 --- a/docs/docs/getting-started/v3-migration.md +++ b/docs/docs/getting-started/v3-migration.md @@ -190,6 +190,8 @@ Animation system was completely rewritten in Chart.js v3. Each property can now * The `filter` option will now be passed additional parameters when called and should have the method signature `function(tooltipItem, index, tooltipItems, data)` * The `custom` callback now takes a context object that has `tooltip` and `chart` properties * All properties of tooltip model related to the tooltip options have been moved to reside within the `options` property. +* The callbacks no longer are given a `data` parameter. The tooltip item parameter contains the chart and dataset instead +* The tooltip item's `index` parameter was renamed to `dataIndex` ## Developer migration @@ -339,7 +341,7 @@ The following properties and methods were removed: * Legend `onClick`, `onHover`, and `onLeave` options now receive the legend as the 3rd argument in addition to implicitly via `this` * Legend `onClick`, `onHover`, and `onLeave` options now receive a wrapped `event` as the first parameter. The previous first parameter value is accessible via `event.native`. * `Title.margins` is now private -* The tooltip item's `x` and `y` attributes were removed. Use `datasetIndex` and `index` to get the element and any corresponding data from it +* The tooltip item's `x` and `y` attributes were removed. Use `datasetIndex` and `dataIndex` to get the element and any corresponding data from it #### Removal of private APIs diff --git a/samples/charts/multi-series-pie.html b/samples/charts/multi-series-pie.html index 0148c1159..15347ebb7 100644 --- a/samples/charts/multi-series-pie.html +++ b/samples/charts/multi-series-pie.html @@ -81,9 +81,9 @@ }, tooltips: { callbacks: { - label: function(tooltipItem, data) { - var labelIndex = (tooltipItem.datasetIndex * 2) + tooltipItem.index; - return data.labels[labelIndex] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + label: function(context) { + var labelIndex = (context.datasetIndex * 2) + context.dataIndex; + return context.chart.data.labels[labelIndex] + ': ' + context.dataset.data[context.dataIndex]; } } } diff --git a/samples/scales/financial.html b/samples/scales/financial.html index e22603521..922ced60c 100644 --- a/samples/scales/financial.html +++ b/samples/scales/financial.html @@ -186,12 +186,12 @@ intersect: false, mode: 'index', callbacks: { - label: function(tooltipItem, myData) { - let label = myData.datasets[tooltipItem.datasetIndex].label || ''; + label: function(context) { + let label = context.dataset.label || ''; if (label) { label += ': '; } - label += parseFloat(tooltipItem.value).toFixed(2); + label += parseFloat(context.value).toFixed(2); return label; } } diff --git a/samples/tooltips/callbacks.html b/samples/tooltips/callbacks.html index 3d2a0790e..453250aba 100644 --- a/samples/tooltips/callbacks.html +++ b/samples/tooltips/callbacks.html @@ -63,11 +63,11 @@ mode: 'index', callbacks: { // Use the footer callback to display the sum of the items showing in the tooltip - footer: function(tooltipItems, data) { + footer: function(tooltipItems) { var sum = 0; tooltipItems.forEach(function(tooltipItem) { - sum += data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + sum += tooltipItem.dataset.data[tooltipItem.dataIndex]; }); return 'Sum: ' + sum; }, diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index 892c9ae3e..a5a107f4f 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -325,9 +325,9 @@ DoughnutController.defaults = { title() { return ''; }, - label(tooltipItem, data) { - let dataLabel = data.labels[tooltipItem.index]; - const value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + label(tooltipItem) { + let dataLabel = tooltipItem.chart.data.labels[tooltipItem.dataIndex]; + const value = ': ' + tooltipItem.dataset.data[tooltipItem.dataIndex]; if (isArray(dataLabel)) { // show value on first line of multiline label diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 5006eaef3..760428bb1 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -218,8 +218,8 @@ PolarAreaController.defaults = { title() { return ''; }, - label(item, data) { - return data.labels[item.index] + ': ' + item.value; + label(context) { + return context.chart.data.labels[context.dataIndex] + ': ' + context.value; } } } diff --git a/src/plugins/plugin.tooltip.js b/src/plugins/plugin.tooltip.js index c4363a100..95d4000a3 100644 --- a/src/plugins/plugin.tooltip.js +++ b/src/plugins/plugin.tooltip.js @@ -117,12 +117,16 @@ function splitNewlines(str) { */ function createTooltipItem(chart, item) { const {datasetIndex, index} = item; - const {label, value} = chart.getDatasetMeta(datasetIndex).controller.getLabelAndValue(index); + const controller = chart.getDatasetMeta(datasetIndex).controller; + const dataset = controller.getDataset(); + const {label, value} = controller.getLabelAndValue(index); return { + chart, label, value, - index, + dataset, + dataIndex: index, datasetIndex }; } @@ -405,16 +409,14 @@ export class Tooltip extends Element { return animations; } - // Get the title - // Args are: (tooltipItem, data) - getTitle(tooltipitem, data) { + getTitle(context) { const me = this; const opts = me.options; const callbacks = opts.callbacks; - const beforeTitle = callbacks.beforeTitle.apply(me, [tooltipitem, data]); - const title = callbacks.title.apply(me, [tooltipitem, data]); - const afterTitle = callbacks.afterTitle.apply(me, [tooltipitem, data]); + const beforeTitle = callbacks.beforeTitle.apply(me, [context]); + const title = callbacks.title.apply(me, [context]); + const afterTitle = callbacks.afterTitle.apply(me, [context]); let lines = []; lines = pushOrConcat(lines, splitNewlines(beforeTitle)); @@ -424,26 +426,24 @@ export class Tooltip extends Element { return lines; } - // Args are: (tooltipItem, data) - getBeforeBody(tooltipitem, data) { - return getBeforeAfterBodyLines(this.options.callbacks.beforeBody.apply(this, [tooltipitem, data])); + getBeforeBody(tooltipItems) { + return getBeforeAfterBodyLines(this.options.callbacks.beforeBody.apply(this, [tooltipItems])); } - // Args are: (tooltipItem, data) - getBody(tooltipItems, data) { + getBody(tooltipItems) { const me = this; const callbacks = me.options.callbacks; const bodyItems = []; - each(tooltipItems, (tooltipItem) => { + each(tooltipItems, (context) => { const bodyItem = { before: [], lines: [], after: [] }; - pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); - pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); - pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); + pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, context))); + pushOrConcat(bodyItem.lines, callbacks.label.call(me, context)); + pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, context))); bodyItems.push(bodyItem); }); @@ -451,20 +451,18 @@ export class Tooltip extends Element { return bodyItems; } - // Args are: (tooltipItem, data) - getAfterBody(tooltipitem, data) { - return getBeforeAfterBodyLines(this.options.callbacks.afterBody.apply(this, [tooltipitem, data])); + getAfterBody(tooltipItems) { + return getBeforeAfterBodyLines(this.options.callbacks.afterBody.apply(this, [tooltipItems])); } // Get the footer and beforeFooter and afterFooter lines - // Args are: (tooltipItem, data) - getFooter(tooltipitem, data) { + getFooter(tooltipItems) { const me = this; const callbacks = me.options.callbacks; - const beforeFooter = callbacks.beforeFooter.apply(me, [tooltipitem, data]); - const footer = callbacks.footer.apply(me, [tooltipitem, data]); - const afterFooter = callbacks.afterFooter.apply(me, [tooltipitem, data]); + const beforeFooter = callbacks.beforeFooter.apply(me, [tooltipItems]); + const footer = callbacks.footer.apply(me, [tooltipItems]); + const afterFooter = callbacks.afterFooter.apply(me, [tooltipItems]); let lines = []; lines = pushOrConcat(lines, splitNewlines(beforeFooter)); @@ -502,9 +500,9 @@ export class Tooltip extends Element { } // Determine colors for boxes - each(tooltipItems, (tooltipItem) => { - labelColors.push(options.callbacks.labelColor.call(me, tooltipItem, me._chart)); - labelTextColors.push(options.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); + each(tooltipItems, (context) => { + labelColors.push(options.callbacks.labelColor.call(me, context)); + labelTextColors.push(options.callbacks.labelTextColor.call(me, context)); }); me.labelColors = labelColors; @@ -526,15 +524,14 @@ export class Tooltip extends Element { }; } } else { - const data = me._chart.data; const position = positioners[options.position].call(me, active, me._eventPosition); const tooltipItems = me._createItems(); - me.title = me.getTitle(tooltipItems, data); - me.beforeBody = me.getBeforeBody(tooltipItems, data); - me.body = me.getBody(tooltipItems, data); - me.afterBody = me.getAfterBody(tooltipItems, data); - me.footer = me.getFooter(tooltipItems, data); + me.title = me.getTitle(tooltipItems); + me.beforeBody = me.getBeforeBody(tooltipItems); + me.body = me.getBody(tooltipItems); + me.afterBody = me.getAfterBody(tooltipItems); + me.footer = me.getFooter(tooltipItems); const size = me._size = getTooltipSize(me); const positionAndSize = Object.assign({}, position, size); @@ -1061,21 +1058,19 @@ export default { callbacks: { // Args are: (tooltipItems, data) beforeTitle: noop, - title(tooltipItems, data) { - let title = ''; - const labels = data.labels; - const labelCount = labels ? labels.length : 0; - + title(tooltipItems) { if (tooltipItems.length > 0) { const item = tooltipItems[0]; + const labels = item.chart.data.labels; + const labelCount = labels ? labels.length : 0; if (item.label) { - title = item.label; - } else if (labelCount > 0 && item.index < labelCount) { - title = labels[item.index]; + return item.label; + } else if (labelCount > 0 && item.dataIndex < labelCount) { + return labels[item.dataIndex]; } } - return title; + return ''; }, afterTitle: noop, @@ -1084,8 +1079,8 @@ export default { // Args are: (tooltipItem, data) beforeLabel: noop, - label(tooltipItem, data) { - let label = data.datasets[tooltipItem.datasetIndex].label || ''; + label(tooltipItem) { + let label = tooltipItem.dataset.label || ''; if (label) { label += ': '; @@ -1096,9 +1091,9 @@ export default { } return label; }, - labelColor(tooltipItem, chart) { - const meta = chart.getDatasetMeta(tooltipItem.datasetIndex); - const options = meta.controller.getStyle(tooltipItem.index); + labelColor(tooltipItem) { + const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex); + const options = meta.controller.getStyle(tooltipItem.dataIndex); return { borderColor: options.borderColor, backgroundColor: options.backgroundColor diff --git a/test/specs/plugin.tooltip.tests.js b/test/specs/plugin.tooltip.tests.js index 75086ba8b..2fa26724d 100644 --- a/test/specs/plugin.tooltip.tests.js +++ b/test/specs/plugin.tooltip.tests.js @@ -18,15 +18,16 @@ describe('Plugin.Tooltip', function() { var tooltipItem = { index: 1, datasetIndex: 0, + dataset: data.datasets[0], label: 'Point 2', value: '20' }; - var label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem, data); + var label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem); expect(label).toBe('20'); data.datasets[0].label = 'My dataset'; - label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem, data); + label = Chart.defaults.plugins.tooltip.callbacks.label(tooltipItem); expect(label).toBe('My dataset: 20'); }); }); @@ -835,7 +836,7 @@ describe('Plugin.Tooltip', function() { var tooltipItem = tooltip.dataPoints[0]; - expect(tooltipItem.index).toBe(pointIndex); + expect(tooltipItem.dataIndex).toBe(pointIndex); expect(tooltipItem.datasetIndex).toBe(datasetIndex); expect(typeof tooltipItem.label).toBe('string'); expect(tooltipItem.label).toBe(chart.data.labels[pointIndex]);