Fix title, body and footer alignment inside tooltip (#5925)

This commit is contained in:
Jukka Kurkela
2019-01-08 14:37:36 +02:00
committed by Simon Brunel
parent 934ce241ae
commit dd3564aee5
2 changed files with 199 additions and 11 deletions

View 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

View File

@@ -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: []}
]));
});
});
});