]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Align the tooltip callbacks parameters with scriptable options (#7603)
authorBen McCann <322311+benmccann@users.noreply.github.com>
Sun, 12 Jul 2020 22:26:13 +0000 (15:26 -0700)
committerGitHub <noreply@github.com>
Sun, 12 Jul 2020 22:26:13 +0000 (18:26 -0400)
Align the tooltip callbacks parameters with scriptable options

docs/docs/configuration/tooltip.md
docs/docs/getting-started/v3-migration.md
samples/charts/multi-series-pie.html
samples/scales/financial.html
samples/tooltips/callbacks.html
src/controllers/controller.doughnut.js
src/controllers/controller.polarArea.js
src/plugins/plugin.tooltip.js
test/specs/plugin.tooltip.tests.js

index 2cb65f11b74610d34627dab46df191af473923ca..e4b6dfb94a4398c6aec582bc307671f5aff8b9dc 100644 (file)
@@ -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
 }
 ```
 
index d6de6a1f2dc9a985781ad7ac5077805a384884f0..8db298d4399f4f67bdd1def1e4a03a7a69647eee 100644 (file)
@@ -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
 
index 0148c11594bf838b60b3f26a2b382bc9e6a122e4..15347ebb70318985de5a332c1a4691b27fbc8059 100644 (file)
@@ -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];
                                                }
                                        }
                                }
index e22603521cf59417cf450dda0600ee0c0a4fbd29..922ced60cee331ecd58da2c1ec7f7c719530d6e1 100644 (file)
                                        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;
                                                }
                                        }
index 3d2a0790e2e3bed29fdedb61f5fc0569b01b0b81..453250aba36cdbe7142467366e7cd4c8761adf49 100644 (file)
                                        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;
                                                },
index 892c9ae3e70b59247e1b03bd215ccd70bc2cc0ef..a5a107f4f9116233c5b8d1ee52dec31bf0aed642 100644 (file)
@@ -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
index 5006eaef3939795526b3be18b8ead624d7a62d8e..760428bb19a3e1c3c9836e9713e7d67930447ba2 100644 (file)
@@ -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;
                        }
                }
        }
index c4363a1006adc5e56106bffd296cb62103de1eff..95d4000a3ffa0318e95f614acf95b113941ee238 100644 (file)
@@ -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
index 75086ba8beb03ec3f75fec576b710b35ff57816c..2fa26724d166219baa82371705427a4b51d1a5f6 100644 (file)
@@ -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]);