From 96f6b42c57a57e71f84c73cbaa208afb4949f736 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 13 Mar 2021 15:14:48 -0500 Subject: [PATCH] Use font lineHeight for tooltip alignment (#8631) * Use font lineHeight for tooltip alignment * Remove toFontString usage from tooltip --- src/plugins/plugin.tooltip.js | 57 ++++++++++++++++-------------- test/specs/plugin.tooltip.tests.js | 38 ++++++++++---------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/plugins/plugin.tooltip.js b/src/plugins/plugin.tooltip.js index 16aef128d7e..9660e2cfd2c 100644 --- a/src/plugins/plugin.tooltip.js +++ b/src/plugins/plugin.tooltip.js @@ -1,10 +1,10 @@ import Animations from '../core/core.animations'; import Element from '../core/core.element'; import {each, noop, isNullOrUndef, isArray, _elementsEqual} from '../helpers/helpers.core'; -import {toPadding} from '../helpers/helpers.options'; +import {toFont, toPadding} from '../helpers/helpers.options'; import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl'; import {distanceBetweenPoints} from '../helpers/helpers.math'; -import {drawPoint, toFontString} from '../helpers'; +import {drawPoint} from '../helpers'; /** * @typedef { import("../platform/platform.base").ChartEvent } ChartEvent @@ -139,7 +139,10 @@ function createTooltipItem(chart, item) { function getTooltipSize(tooltip, options) { const ctx = tooltip._chart.ctx; const {body, footer, title} = tooltip; - const {bodyFont, footerFont, titleFont, boxWidth, boxHeight} = options; + const {boxWidth, boxHeight} = options; + const bodyFont = toFont(options.bodyFont); + const titleFont = toFont(options.titleFont); + const footerFont = toFont(options.footerFont); const titleLineCount = title.length; const footerLineCount = footer.length; const bodyLineItemCount = body.length; @@ -153,20 +156,20 @@ function getTooltipSize(tooltip, options) { combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length; if (titleLineCount) { - height += titleLineCount * titleFont.size + height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom; } if (combinedBodyLength) { // Body lines may include some extra height depending on boxHeight - const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.size) : bodyFont.size; + const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight; height += bodyLineItemCount * bodyLineHeight - + (combinedBodyLength - bodyLineItemCount) * bodyFont.size + + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing; } if (footerLineCount) { height += options.footerMarginTop - + footerLineCount * footerFont.size + + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing; } @@ -178,11 +181,11 @@ function getTooltipSize(tooltip, options) { ctx.save(); - ctx.font = toFontString(titleFont); + ctx.font = titleFont.string; each(tooltip.title, maxLineWidth); // Body width - ctx.font = toFontString(bodyFont); + ctx.font = bodyFont.string; each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth); // Body lines may include some extra width due to the color box @@ -197,7 +200,7 @@ function getTooltipSize(tooltip, options) { widthPadding = 0; // Footer width - ctx.font = toFontString(footerFont); + ctx.font = footerFont.string; each(tooltip.footer, maxLineWidth); ctx.restore(); @@ -652,15 +655,15 @@ export class Tooltip extends Element { ctx.textAlign = rtlHelper.textAlign(options.titleAlign); ctx.textBaseline = 'middle'; - titleFont = options.titleFont; + titleFont = toFont(options.titleFont); titleSpacing = options.titleSpacing; ctx.fillStyle = options.titleColor; - ctx.font = toFontString(titleFont); + ctx.font = titleFont.string; for (i = 0; i < length; ++i) { - ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.size / 2); - pt.y += titleFont.size + titleSpacing; // Line Height and spacing + ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2); + pt.y += titleFont.lineHeight + titleSpacing; // Line Height and spacing if (i + 1 === length) { pt.y += options.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing @@ -676,10 +679,11 @@ export class Tooltip extends Element { const me = this; const labelColors = me.labelColors[i]; const labelPointStyle = me.labelPointStyles[i]; - const {boxHeight, boxWidth, bodyFont} = options; + const {boxHeight, boxWidth} = options; + const bodyFont = toFont(options.bodyFont); const colorX = getAlignedX(me, 'left', options); const rtlColorX = rtlHelper.x(colorX); - const yOffSet = boxHeight < bodyFont.size ? (bodyFont.size - boxHeight) / 2 : 0; + const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0; const colorY = pt.y + yOffSet; if (options.usePointStyle) { @@ -725,8 +729,9 @@ export class Tooltip extends Element { drawBody(pt, ctx, options) { const me = this; const {body} = me; - const {bodyFont, bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth} = options; - let bodyLineHeight = bodyFont.size; + const {bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth} = options; + const bodyFont = toFont(options.bodyFont); + let bodyLineHeight = bodyFont.lineHeight; let xLinePadding = 0; const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width); @@ -741,7 +746,7 @@ export class Tooltip extends Element { ctx.textAlign = bodyAlign; ctx.textBaseline = 'middle'; - ctx.font = toFontString(bodyFont); + ctx.font = bodyFont.string; pt.x = getAlignedX(me, bodyAlignForCalculation, options); @@ -765,13 +770,13 @@ export class Tooltip extends Element { // Draw Legend-like boxes if needed if (displayColors && lines.length) { me._drawColorBox(ctx, pt, i, rtlHelper, options); - bodyLineHeight = Math.max(bodyFont.size, boxHeight); + bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight); } for (j = 0, jlen = lines.length; j < jlen; ++j) { fillLineOfText(lines[j]); // Reset for any lines that don't include colorbox - bodyLineHeight = bodyFont.size; + bodyLineHeight = bodyFont.lineHeight; } each(bodyItem.after, fillLineOfText); @@ -779,7 +784,7 @@ export class Tooltip extends Element { // Reset back to 0 for after body xLinePadding = 0; - bodyLineHeight = bodyFont.size; + bodyLineHeight = bodyFont.lineHeight; // After body lines each(me.afterBody, fillLineOfText); @@ -801,14 +806,14 @@ export class Tooltip extends Element { ctx.textAlign = rtlHelper.textAlign(options.footerAlign); ctx.textBaseline = 'middle'; - footerFont = options.footerFont; + footerFont = toFont(options.footerFont); ctx.fillStyle = options.footerColor; - ctx.font = toFontString(footerFont); + ctx.font = footerFont.string; for (i = 0; i < length; ++i) { - ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.size / 2); - pt.y += footerFont.size + options.footerSpacing; + ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2); + pt.y += footerFont.lineHeight + options.footerSpacing; } } } diff --git a/test/specs/plugin.tooltip.tests.js b/test/specs/plugin.tooltip.tests.js index 836b499fa78..9dbd198e627 100644 --- a/test/specs/plugin.tooltip.tests.js +++ b/test/specs/plugin.tooltip.tests.js @@ -163,8 +163,8 @@ describe('Plugin.Tooltip', function() { }] })); - expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(155); + expect(tooltip.x).toBeCloseToPixel(266); + expect(tooltip.y).toBeCloseToPixel(150); }); it('Should only display if intersecting if intersect is set', async function() { @@ -311,7 +311,7 @@ describe('Plugin.Tooltip', function() { }]); expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(312); + expect(tooltip.y).toBeCloseToPixel(308); }); it('Should display information from user callbacks', async function() { @@ -475,7 +475,7 @@ describe('Plugin.Tooltip', function() { })); expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(75); + expect(tooltip.y).toBeCloseToPixel(58); }); it('Should provide context object to user callbacks', async function() { @@ -581,7 +581,7 @@ describe('Plugin.Tooltip', function() { })); expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(155); + expect(tooltip.y).toBeCloseToPixel(150); }); it('Should allow reversing items', async function() { @@ -649,7 +649,7 @@ describe('Plugin.Tooltip', function() { })); expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(155); + expect(tooltip.y).toBeCloseToPixel(150); }); it('Should follow dataset order', async function() { @@ -718,7 +718,7 @@ describe('Plugin.Tooltip', function() { })); expect(tooltip.x).toBeCloseToPixel(267); - expect(tooltip.y).toBeCloseToPixel(155); + expect(tooltip.y).toBeCloseToPixel(150); }); it('should filter items from the tooltip using the callback', async function() { @@ -1393,18 +1393,18 @@ describe('Plugin.Tooltip', function() { {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['title', 105, 111]}, + {name: 'fillText', args: ['title', 105, 112.2]}, {name: 'setTextAlign', args: ['left']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFont', args: ["normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFillStyle', args: ['#fff']}, - {name: 'fillText', args: ['label', 105, 129]}, + {name: 'fillText', args: ['label', 105, 132.6]}, {name: 'setTextAlign', args: ['left']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['footer', 105, 147]}, + {name: 'fillText', args: ['footer', 105, 153]}, {name: 'restore', args: []} ])); }); @@ -1419,18 +1419,18 @@ describe('Plugin.Tooltip', function() { {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['title', 195, 111]}, + {name: 'fillText', args: ['title', 195, 112.2]}, {name: 'setTextAlign', args: ['right']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFont', args: ["normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFillStyle', args: ['#fff']}, - {name: 'fillText', args: ['label', 195, 129]}, + {name: 'fillText', args: ['label', 195, 132.6]}, {name: 'setTextAlign', args: ['right']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['footer', 195, 147]}, + {name: 'fillText', args: ['footer', 195, 153]}, {name: 'restore', args: []} ])); }); @@ -1445,18 +1445,18 @@ describe('Plugin.Tooltip', function() { {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['title', 150, 111]}, + {name: 'fillText', args: ['title', 150, 112.2]}, {name: 'setTextAlign', args: ['center']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFont', args: ["normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFillStyle', args: ['#fff']}, - {name: 'fillText', args: ['label', 150, 129]}, + {name: 'fillText', args: ['label', 150, 132.6]}, {name: 'setTextAlign', args: ['center']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['footer', 150, 147]}, + {name: 'fillText', args: ['footer', 150, 153]}, {name: 'restore', args: []} ])); }); @@ -1471,18 +1471,18 @@ describe('Plugin.Tooltip', function() { {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['title', 195, 111]}, + {name: 'fillText', args: ['title', 195, 112.2]}, {name: 'setTextAlign', args: ['center']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFont', args: ["normal 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFillStyle', args: ['#fff']}, - {name: 'fillText', args: ['label', 150, 129]}, + {name: 'fillText', args: ['label', 150, 132.6]}, {name: 'setTextAlign', args: ['left']}, {name: 'setTextBaseline', args: ['middle']}, {name: 'setFillStyle', args: ['#fff']}, {name: 'setFont', args: ["bold 12px 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"]}, - {name: 'fillText', args: ['footer', 105, 147]}, + {name: 'fillText', args: ['footer', 105, 153]}, {name: 'restore', args: []} ])); });