]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Handle '\n' as new line in tooltips (#5521)
authorMatt Haff <matt.haff@gmail.com>
Tue, 5 Jun 2018 07:14:37 +0000 (03:14 -0400)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Tue, 5 Jun 2018 07:14:37 +0000 (09:14 +0200)
src/core/core.tooltip.js
test/specs/core.tooltip.tests.js

index 3f9490f854626e8fed2f4acff3a69f5928814ea5..c529812cc617055786cad3fdcd7c955ce7749a3b 100644 (file)
@@ -190,6 +190,20 @@ function pushOrConcat(base, toPush) {
        return base;
 }
 
+/**
+ * Returns array of strings split by newline
+ * @param {String} value - The value to split by newline.
+ * @returns {Array} value if newline present - Returned from String split() method
+ * @function
+ */
+function splitNewlines(str) {
+       if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
+               return str.split('\n');
+       }
+       return str;
+}
+
+
 // Private helper to create a tooltip item model
 // @param element : the chart element (point, arc, bar) to create the tooltip item for
 // @return : new tooltip item
@@ -404,7 +418,7 @@ function determineAlignment(tooltip, size) {
 }
 
 /**
- * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
+ * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
  */
 function getBackgroundPoint(vm, size, alignment, chart) {
        // Background Position
@@ -457,6 +471,13 @@ function getBackgroundPoint(vm, size, alignment, chart) {
        };
 }
 
+/**
+ * Helper to build before and after body lines
+ */
+function getBeforeAfterBodyLines(callback) {
+       return pushOrConcat([], splitNewlines(callback));
+}
+
 var exports = module.exports = Element.extend({
        initialize: function() {
                this._model = getBaseModel(this._options);
@@ -475,17 +496,16 @@ var exports = module.exports = Element.extend({
                var afterTitle = callbacks.afterTitle.apply(me, arguments);
 
                var lines = [];
-               lines = pushOrConcat(lines, beforeTitle);
-               lines = pushOrConcat(lines, title);
-               lines = pushOrConcat(lines, afterTitle);
+               lines = pushOrConcat(lines, splitNewlines(beforeTitle));
+               lines = pushOrConcat(lines, splitNewlines(title));
+               lines = pushOrConcat(lines, splitNewlines(afterTitle));
 
                return lines;
        },
 
        // Args are: (tooltipItem, data)
        getBeforeBody: function() {
-               var lines = this._options.callbacks.beforeBody.apply(this, arguments);
-               return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
+               return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
        },
 
        // Args are: (tooltipItem, data)
@@ -500,9 +520,9 @@ var exports = module.exports = Element.extend({
                                lines: [],
                                after: []
                        };
-                       pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
+                       pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
                        pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
-                       pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
+                       pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
 
                        bodyItems.push(bodyItem);
                });
@@ -512,8 +532,7 @@ var exports = module.exports = Element.extend({
 
        // Args are: (tooltipItem, data)
        getAfterBody: function() {
-               var lines = this._options.callbacks.afterBody.apply(this, arguments);
-               return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
+               return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
        },
 
        // Get the footer and beforeFooter and afterFooter lines
@@ -527,9 +546,9 @@ var exports = module.exports = Element.extend({
                var afterFooter = callbacks.afterFooter.apply(me, arguments);
 
                var lines = [];
-               lines = pushOrConcat(lines, beforeFooter);
-               lines = pushOrConcat(lines, footer);
-               lines = pushOrConcat(lines, afterFooter);
+               lines = pushOrConcat(lines, splitNewlines(beforeFooter));
+               lines = pushOrConcat(lines, splitNewlines(footer));
+               lines = pushOrConcat(lines, splitNewlines(afterFooter));
 
                return lines;
        },
index 8878d9d9dbcc2ceaa6fa666f68cf775d2753cbc5..9d858a49e10e7444764eee2d280fed6aaefcba84 100755 (executable)
@@ -949,4 +949,152 @@ describe('Core.Tooltip', function() {
                        }
                }
        });
+
+       it('Should split newlines into separate lines in user callbacks', function() {
+               var chart = window.acquireChart({
+                       type: 'line',
+                       data: {
+                               datasets: [{
+                                       label: 'Dataset 1',
+                                       data: [10, 20, 30],
+                                       pointHoverBorderColor: 'rgb(255, 0, 0)',
+                                       pointHoverBackgroundColor: 'rgb(0, 255, 0)'
+                               }, {
+                                       label: 'Dataset 2',
+                                       data: [40, 40, 40],
+                                       pointHoverBorderColor: 'rgb(0, 0, 255)',
+                                       pointHoverBackgroundColor: 'rgb(0, 255, 255)'
+                               }],
+                               labels: ['Point 1', 'Point 2', 'Point 3']
+                       },
+                       options: {
+                               tooltips: {
+                                       mode: 'label',
+                                       callbacks: {
+                                               beforeTitle: function() {
+                                                       return 'beforeTitle\nnewline';
+                                               },
+                                               title: function() {
+                                                       return 'title\nnewline';
+                                               },
+                                               afterTitle: function() {
+                                                       return 'afterTitle\nnewline';
+                                               },
+                                               beforeBody: function() {
+                                                       return 'beforeBody\nnewline';
+                                               },
+                                               beforeLabel: function() {
+                                                       return 'beforeLabel\nnewline';
+                                               },
+                                               label: function() {
+                                                       return 'label';
+                                               },
+                                               afterLabel: function() {
+                                                       return 'afterLabel\nnewline';
+                                               },
+                                               afterBody: function() {
+                                                       return 'afterBody\nnewline';
+                                               },
+                                               beforeFooter: function() {
+                                                       return 'beforeFooter\nnewline';
+                                               },
+                                               footer: function() {
+                                                       return 'footer\nnewline';
+                                               },
+                                               afterFooter: function() {
+                                                       return 'afterFooter\nnewline';
+                                               },
+                                               labelTextColor: function() {
+                                                       return 'labelTextColor';
+                                               }
+                                       }
+                               }
+                       }
+               });
+
+               // Trigger an event over top of the
+               var meta = chart.getDatasetMeta(0);
+               var point = meta.data[1];
+               var node = chart.canvas;
+               var rect = node.getBoundingClientRect();
+               var evt = new MouseEvent('mousemove', {
+                       view: window,
+                       bubbles: true,
+                       cancelable: true,
+                       clientX: rect.left + point._model.x,
+                       clientY: rect.top + point._model.y
+               });
+
+               // Manually trigger rather than having an async test
+               node.dispatchEvent(evt);
+
+               // Check and see if tooltip was displayed
+               var tooltip = chart.tooltip;
+               var globalDefaults = Chart.defaults.global;
+
+               expect(tooltip._view).toEqual(jasmine.objectContaining({
+                       // Positioning
+                       xPadding: 6,
+                       yPadding: 6,
+                       xAlign: 'center',
+                       yAlign: 'top',
+
+                       // Body
+                       bodyFontColor: '#fff',
+                       _bodyFontFamily: globalDefaults.defaultFontFamily,
+                       _bodyFontStyle: globalDefaults.defaultFontStyle,
+                       _bodyAlign: 'left',
+                       bodyFontSize: globalDefaults.defaultFontSize,
+                       bodySpacing: 2,
+
+                       // Title
+                       titleFontColor: '#fff',
+                       _titleFontFamily: globalDefaults.defaultFontFamily,
+                       _titleFontStyle: 'bold',
+                       titleFontSize: globalDefaults.defaultFontSize,
+                       _titleAlign: 'left',
+                       titleSpacing: 2,
+                       titleMarginBottom: 6,
+
+                       // Footer
+                       footerFontColor: '#fff',
+                       _footerFontFamily: globalDefaults.defaultFontFamily,
+                       _footerFontStyle: 'bold',
+                       footerFontSize: globalDefaults.defaultFontSize,
+                       _footerAlign: 'left',
+                       footerSpacing: 2,
+                       footerMarginTop: 6,
+
+                       // Appearance
+                       caretSize: 5,
+                       cornerRadius: 6,
+                       backgroundColor: 'rgba(0,0,0,0.8)',
+                       opacity: 1,
+                       legendColorBackground: '#fff',
+
+                       // Text
+                       title: ['beforeTitle', 'newline', 'title', 'newline', 'afterTitle', 'newline'],
+                       beforeBody: ['beforeBody', 'newline'],
+                       body: [{
+                               before: ['beforeLabel', 'newline'],
+                               lines: ['label'],
+                               after: ['afterLabel', 'newline']
+                       }, {
+                               before: ['beforeLabel', 'newline'],
+                               lines: ['label'],
+                               after: ['afterLabel', 'newline']
+                       }],
+                       afterBody: ['afterBody', 'newline'],
+                       footer: ['beforeFooter', 'newline', 'footer', 'newline', 'afterFooter', 'newline'],
+                       caretPadding: 2,
+                       labelTextColors: ['labelTextColor', 'labelTextColor'],
+                       labelColors: [{
+                               borderColor: 'rgb(255, 0, 0)',
+                               backgroundColor: 'rgb(0, 255, 0)'
+                       }, {
+                               borderColor: 'rgb(0, 0, 255)',
+                               backgroundColor: 'rgb(0, 255, 255)'
+                       }]
+               }));
+       });
 });