Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tooltip positioning issues #8646

Merged
merged 5 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 29 additions & 30 deletions src/plugins/plugin.tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Element from '../core/core.element';
import {each, noop, isNullOrUndef, isArray, _elementsEqual} from '../helpers/helpers.core';
import {toFont, toPadding} from '../helpers/helpers.options';
import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
import {distanceBetweenPoints} from '../helpers/helpers.math';
import {distanceBetweenPoints, _limitValue} from '../helpers/helpers.math';
import {drawPoint} from '../helpers';

/**
Expand Down Expand Up @@ -211,24 +211,23 @@ function getTooltipSize(tooltip, options) {
return {width, height};
}

/**
* Helper to get the alignment of a tooltip given the size
*/
function determineAlignment(chart, options, size) {
const {x, y, width, height} = size;
const chartArea = chart.chartArea;
let xAlign = 'center';
let yAlign = 'center';
function determineYAlign(chart, size) {
const {y, height} = size;

if (y < height / 2) {
yAlign = 'top';
return 'top';
} else if (y > (chart.height - height / 2)) {
yAlign = 'bottom';
return 'bottom';
}
return 'center';
}

let lf, rf; // functions to determine left, right alignment
function determineXAlign(chart, options, size, yAlign) {
const {x, width} = size;
const chartArea = chart.chartArea;
const midX = (chartArea.left + chartArea.right) / 2;
const midY = (chartArea.top + chartArea.bottom) / 2;
let xAlign = 'center';
let lf, rf; // functions to determine left, right alignment

if (yAlign === 'center') {
lf = (value) => value <= midX;
Expand All @@ -241,46 +240,43 @@ function determineAlignment(chart, options, size) {
// functions to determine if left/right alignment causes tooltip to go outside chart
const olf = (value) => value + width + options.caretSize + options.caretPadding > chart.width;
const orf = (value) => value - width - options.caretSize - options.caretPadding < 0;
// function to get the y alignment if the tooltip goes outside of the left or right edges
const yf = (value) => value <= midY ? 'top' : 'bottom';

if (lf(x)) {
xAlign = 'left';

// Is tooltip too wide and goes over the right side of the chart.?
if (olf(x)) {
xAlign = 'center';
yAlign = yf(y);
}
} else if (rf(x)) {
xAlign = 'right';

// Is tooltip too wide and goes outside left edge of canvas?
if (orf(x)) {
xAlign = 'center';
yAlign = yf(y);
}
}
return xAlign;
}

/**
* Helper to get the alignment of a tooltip given the size
*/
function determineAlignment(chart, options, size) {
const yAlign = options.yAlign || determineYAlign(chart, size);

return {
xAlign: options.xAlign ? options.xAlign : xAlign,
yAlign: options.yAlign ? options.yAlign : yAlign
xAlign: options.xAlign || determineXAlign(chart, options, size, yAlign),
yAlign
};
}

function alignX(size, xAlign, chartWidth) {
// eslint-disable-next-line prefer-const
function alignX(size, xAlign) {
let {x, width} = size;
if (xAlign === 'right') {
x -= width;
x = x - width;
} else if (xAlign === 'center') {
x -= (width / 2);
if (x + width > chartWidth) {
x = chartWidth - width;
}
if (x < 0) {
x = 0;
}
}
return x;
}
Expand All @@ -307,7 +303,7 @@ function getBackgroundPoint(options, size, alignment, chart) {
const paddingAndSize = caretSize + caretPadding;
const radiusAndPadding = cornerRadius + caretPadding;

let x = alignX(size, xAlign, chart.width);
let x = alignX(size, xAlign);
const y = alignY(size, yAlign, paddingAndSize);

if (yAlign === 'center') {
Expand All @@ -322,7 +318,10 @@ function getBackgroundPoint(options, size, alignment, chart) {
x += radiusAndPadding;
}

return {x, y};
return {
x: _limitValue(x, 0, chart.width - size.width),
y: _limitValue(y, 0, chart.height - size.height)
};
}

function getAlignedX(tooltip, align, options) {
Expand Down
19 changes: 10 additions & 9 deletions test/fixtures/plugin.tooltip/opacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,18 @@ module.exports = {
plugins: {
legend: false,
title: false,
filler: false
},
tooltips: {
mode: 'nearest',
intersect: false,
callbacks: {
label: function() {
return '\u200b';
filler: false,
tooltip: {
mode: 'nearest',
intersect: false,
callbacks: {
label: function() {
return '\u200b';
},
}
}
},
},

layout: {
padding: 15
}
Expand Down
Binary file modified test/fixtures/plugin.tooltip/opacity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 11 additions & 10 deletions test/fixtures/plugin.tooltip/point-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ module.exports = {
plugins: {
legend: false,
title: false,
filler: false
},
tooltips: {
mode: 'nearest',
intersect: false,
usePointStyle: true,
callbacks: {
label: function() {
return '\u200b';
filler: false,
tooltip: {
mode: 'nearest',
intersect: false,
padding: 5,
usePointStyle: true,
callbacks: {
label: function() {
return '\u200b';
}
}
}
},
},
layout: {
padding: 15
Expand Down
Binary file modified test/fixtures/plugin.tooltip/point-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 66 additions & 0 deletions test/fixtures/plugin.tooltip/positioning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const data = [];
for (let x = 0; x < 3; x++) {
for (let y = 0; y < 3; y++) {
data.push({x, y});
}
}

module.exports = {
config: {
type: 'scatter',
data: {
datasets: [{
data,
// radius: 8
}],
},
options: {
scales: {
x: {display: false},
y: {display: false}
},
plugins: {
legend: false,
title: false,
filler: false,
tooltip: {
mode: 'point',
intersect: true,
callbacks: {
beforeLabel: () => 'before label',
label: () => 'label',
afterLabel: () => 'after1\nafter2\nafter3\nafter4\nafter5'
}
}
},
},
plugins: [{
afterDraw: function(chart) {
const canvas = chart.canvas;
const rect = canvas.getBoundingClientRect();
const meta = chart.getDatasetMeta(0);
let point, event;

for (let i = 0; i < data.length; i++) {
point = meta.data[i];
event = {
type: 'mousemove',
target: canvas,
clientX: rect.left + point.x,
clientY: rect.top + point.y
};
chart._handleEvent(event);
chart.tooltip.handleEvent(event);
chart.tooltip.draw(chart.ctx);
}
}
}]
},
options: {
spriteText: true,
canvas: {
height: 512,
width: 512
}
}
};
Binary file added test/fixtures/plugin.tooltip/positioning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/specs/plugin.tooltip.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const tooltipPlugin = Chart.registry.getPlugin('tooltip');
const Tooltip = tooltipPlugin._element;

describe('Plugin.Tooltip', function() {
describe('auto', jasmine.fixture.specs('core.tooltip'));
describe('auto', jasmine.fixture.specs('plugin.tooltip'));

describe('config', function() {
it('should not include the dataset label in the body string if not defined', function() {
Expand Down