]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix title, body and footer alignment inside tooltip (#5925)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Tue, 8 Jan 2019 12:37:36 +0000 (14:37 +0200)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Tue, 8 Jan 2019 12:37:36 +0000 (13:37 +0100)
src/core/core.tooltip.js
test/specs/core.tooltip.tests.js

index fb081e60b3175e992d7f5b27952befce23f5aa1a..127720a415ef028ea60e5e3e92e8eae3deefb4ae 100644 (file)
@@ -463,6 +463,14 @@ function getBackgroundPoint(vm, size, alignment, chart) {
        };
 }
 
+function getAlignedX(vm, align) {
+       return align === 'center'
+               ? vm.x + vm.width / 2
+               : align === 'right'
+                       ? vm.x + vm.width - vm.xPadding
+                       : vm.x + vm.xPadding;
+}
+
 /**
  * Helper to build before and after body lines
  */
@@ -730,6 +738,8 @@ var exports = Element.extend({
                var title = vm.title;
 
                if (title.length) {
+                       pt.x = getAlignedX(vm, vm._titleAlign);
+
                        ctx.textAlign = vm._titleAlign;
                        ctx.textBaseline = 'top';
 
@@ -754,14 +764,21 @@ var exports = Element.extend({
        drawBody: function(pt, vm, ctx) {
                var bodyFontSize = vm.bodyFontSize;
                var bodySpacing = vm.bodySpacing;
+               var bodyAlign = vm._bodyAlign;
                var body = vm.body;
+               var drawColorBoxes = vm.displayColors;
+               var labelColors = vm.labelColors;
+               var xLinePadding = 0;
+               var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
+               var textColor;
 
-               ctx.textAlign = vm._bodyAlign;
+               ctx.textAlign = bodyAlign;
                ctx.textBaseline = 'top';
                ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
 
+               pt.x = getAlignedX(vm, bodyAlign);
+
                // Before Body
-               var xLinePadding = 0;
                var fillLineOfText = function(line) {
                        ctx.fillText(line, pt.x + xLinePadding, pt.y);
                        pt.y += bodyFontSize + bodySpacing;
@@ -771,12 +788,13 @@ var exports = Element.extend({
                ctx.fillStyle = vm.bodyFontColor;
                helpers.each(vm.beforeBody, fillLineOfText);
 
-               var drawColorBoxes = vm.displayColors;
-               xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
+               xLinePadding = drawColorBoxes && bodyAlign !== 'right'
+                       ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
+                       : 0;
 
                // Draw body lines now
                helpers.each(body, function(bodyItem, i) {
-                       var textColor = vm.labelTextColors[i];
+                       textColor = vm.labelTextColors[i];
                        ctx.fillStyle = textColor;
                        helpers.each(bodyItem.before, fillLineOfText);
 
@@ -785,16 +803,16 @@ var exports = Element.extend({
                                if (drawColorBoxes) {
                                        // Fill a white rect so that colours merge nicely if the opacity is < 1
                                        ctx.fillStyle = vm.legendColorBackground;
-                                       ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
+                                       ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize);
 
                                        // Border
                                        ctx.lineWidth = 1;
-                                       ctx.strokeStyle = vm.labelColors[i].borderColor;
-                                       ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
+                                       ctx.strokeStyle = labelColors[i].borderColor;
+                                       ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize);
 
                                        // Inner square
-                                       ctx.fillStyle = vm.labelColors[i].backgroundColor;
-                                       ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
+                                       ctx.fillStyle = labelColors[i].backgroundColor;
+                                       ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
                                        ctx.fillStyle = textColor;
                                }
 
@@ -816,6 +834,7 @@ var exports = Element.extend({
                var footer = vm.footer;
 
                if (footer.length) {
+                       pt.x = getAlignedX(vm, vm._footerAlign);
                        pt.y += vm.footerMarginTop;
 
                        ctx.textAlign = vm._footerAlign;
@@ -905,7 +924,6 @@ var exports = Element.extend({
                        this.drawBackground(pt, vm, ctx, tooltipSize);
 
                        // Draw Title, Body, and Footer
-                       pt.x += vm.xPadding;
                        pt.y += vm.yPadding;
 
                        // Titles
index b9b93d0116a6cb2c283518dc4527e09ec1e5780f..a994b4ac2143f4085c40f5c21f589742993e6244 100755 (executable)
@@ -1099,4 +1099,174 @@ describe('Core.Tooltip', function() {
                        }]
                }));
        });
+
+       describe('text align', function() {
+               var globalDefaults = Chart.defaults.global;
+               var makeView = function(title, body, footer) {
+                       return {
+                               // Positioning
+                               x: 100,
+                               y: 100,
+                               width: 100,
+                               height: 100,
+                               xPadding: 5,
+                               yPadding: 5,
+                               xAlign: 'left',
+                               yAlign: 'top',
+
+                               // Body
+                               bodyFontColor: '#fff',
+                               _bodyFontFamily: globalDefaults.defaultFontFamily,
+                               _bodyFontStyle: globalDefaults.defaultFontStyle,
+                               _bodyAlign: body,
+                               bodyFontSize: globalDefaults.defaultFontSize,
+                               bodySpacing: 2,
+
+                               // Title
+                               titleFontColor: '#fff',
+                               _titleFontFamily: globalDefaults.defaultFontFamily,
+                               _titleFontStyle: 'bold',
+                               titleFontSize: globalDefaults.defaultFontSize,
+                               _titleAlign: title,
+                               titleSpacing: 2,
+                               titleMarginBottom: 6,
+
+                               // Footer
+                               footerFontColor: '#fff',
+                               _footerFontFamily: globalDefaults.defaultFontFamily,
+                               _footerFontStyle: 'bold',
+                               footerFontSize: globalDefaults.defaultFontSize,
+                               _footerAlign: footer,
+                               footerSpacing: 2,
+                               footerMarginTop: 6,
+
+                               // Appearance
+                               caretSize: 5,
+                               cornerRadius: 6,
+                               borderColor: '#aaa',
+                               borderWidth: 1,
+                               backgroundColor: 'rgba(0,0,0,0.8)',
+                               opacity: 1,
+                               legendColorBackground: '#fff',
+
+                               // Text
+                               title: ['title'],
+                               beforeBody: [],
+                               body: [{
+                                       before: [],
+                                       lines: ['label'],
+                                       after: []
+                               }],
+                               afterBody: [],
+                               footer: ['footer'],
+                               caretPadding: 2,
+                               labelTextColors: ['#fff'],
+                               labelColors: [{
+                                       borderColor: 'rgb(255, 0, 0)',
+                                       backgroundColor: 'rgb(0, 255, 0)'
+                               }, {
+                                       borderColor: 'rgb(0, 0, 255)',
+                                       backgroundColor: 'rgb(0, 255, 255)'
+                               }]
+                       };
+               };
+               var drawBody = [
+                       {name: 'save', args: []},
+                       {name: 'setFillStyle', args: ['rgba(0,0,0,0.8)']},
+                       {name: 'setStrokeStyle', args: ['#aaa']},
+                       {name: 'setLineWidth', args: [1]},
+                       {name: 'beginPath', args: []},
+                       {name: 'moveTo', args: [106, 100]},
+                       {name: 'lineTo', args: [106, 100]},
+                       {name: 'lineTo', args: [111, 95]},
+                       {name: 'lineTo', args: [116, 100]},
+                       {name: 'lineTo', args: [194, 100]},
+                       {name: 'quadraticCurveTo', args: [200, 100, 200, 106]},
+                       {name: 'lineTo', args: [200, 194]},
+                       {name: 'quadraticCurveTo', args: [200, 200, 194, 200]},
+                       {name: 'lineTo', args: [106, 200]},
+                       {name: 'quadraticCurveTo', args: [100, 200, 100, 194]},
+                       {name: 'lineTo', args: [100, 106]},
+                       {name: 'quadraticCurveTo', args: [100, 100, 106, 100]},
+                       {name: 'closePath', args: []},
+                       {name: 'fill', args: []},
+                       {name: 'stroke', args: []}
+               ];
+
+               var mockContext = window.createMockContext();
+               var tooltip = new Chart.Tooltip({
+                       _options: globalDefaults.tooltips,
+                       _chart: {
+                               ctx: mockContext,
+                       }
+               });
+
+               it('Should go left', function() {
+                       mockContext.resetCalls();
+                       tooltip._view = makeView('left', 'left', 'left');
+                       tooltip.draw();
+
+                       expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['title', 105, 105]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['label', 105, 123]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['footer', 105, 141]},
+                               {name: 'restore', args: []}
+                       ]));
+               });
+
+               it('Should go right', function() {
+                       mockContext.resetCalls();
+                       tooltip._view = makeView('right', 'right', 'right');
+                       tooltip.draw();
+
+                       expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['title', 195, 105]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['label', 195, 123]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['footer', 195, 141]},
+                               {name: 'restore', args: []}
+                       ]));
+               });
+
+               it('Should center', function() {
+                       mockContext.resetCalls();
+                       tooltip._view = makeView('center', 'center', 'center');
+                       tooltip.draw();
+
+                       expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['title', 150, 105]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['label', 150, 123]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['footer', 150, 141]},
+                               {name: 'restore', args: []}
+                       ]));
+               });
+
+               it('Should allow mixed', function() {
+                       mockContext.resetCalls();
+                       tooltip._view = makeView('right', 'center', 'left');
+                       tooltip.draw();
+
+                       expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['title', 195, 105]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['label', 150, 123]},
+                               {name: 'setFillStyle', args: ['#fff']},
+                               {name: 'fillText', args: ['footer', 105, 141]},
+                               {name: 'restore', args: []}
+                       ]));
+               });
+       });
 });