Skip to content

Commit

Permalink
fix(tooltip): Correct tooltip position when is zoomed
Browse files Browse the repository at this point in the history
- When has zoomScale, make to be based on mouse x coordinate
- Some refactorings on tooltip

Fix #197
Fix #281
Fix #431
Close #546
  • Loading branch information
netil committed Aug 17, 2018
1 parent b0dc53a commit 45785ab
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 59 deletions.
46 changes: 32 additions & 14 deletions spec/internals/tooltip-spec.js
Expand Up @@ -40,7 +40,7 @@ describe("TOOLTIP", function() {

// hover chart
const hoverChart = (hoverChart, eventName = "mousemove") => {
const eventRect = hoverChart.internal.main
const eventRect = hoverChart.$.main
.select(`.${CLASS.eventRect}-2`)
.node();

Expand All @@ -54,8 +54,8 @@ describe("TOOLTIP", function() {
const checkTooltip = (checkChart, expected) => {
hoverChart(checkChart);

const tooltips = d3.select(checkChart.element)
.selectAll(`.${CLASS.tooltip} tr`)
const tooltips = checkChart.$.tooltip
.selectAll("tr")
.nodes();

if (expected) {
Expand All @@ -69,7 +69,7 @@ describe("TOOLTIP", function() {
const checkLinkedTooltip = (chart1, chart2, expected) => {
hoverChart(chart1);

const tooltips = chart2.internal.tooltip
const tooltips = chart2.$.tooltip
.selectAll("tr")
.nodes();

Expand Down Expand Up @@ -193,7 +193,7 @@ describe("TOOLTIP", function() {
it("should show tooltip on proper position", () => {
hoverChart(chart);

const tooltipContainer = chart.internal.tooltip;
const tooltipContainer = chart.$.tooltip;
const top = Math.floor(+tooltipContainer.style("top").replace(/px/, ""));
const left = Math.floor(+tooltipContainer.style("left").replace(/px/, ""));
const tooltipPos = {
Expand All @@ -219,7 +219,7 @@ describe("TOOLTIP", function() {
it("should show tooltip on proper position", () => {
hoverChart(chart);

const tooltipContainer = d3.select(chart.element).select(`.${CLASS.tooltipContainer}`);
const tooltipContainer = chart.$.tooltip;
const top = Math.floor(+tooltipContainer.style("top").replace(/px/, ""));
const left = Math.floor(+tooltipContainer.style("left").replace(/px/, ""));
const tooltipPos = {
Expand All @@ -231,6 +231,29 @@ describe("TOOLTIP", function() {
expect(left).to.be.above(tooltipPos.left);
});
});

describe("when zoomed", () => {
before(() => {
args.zoom = {enabled: true};
});

it("should show tooltip on proper position", () => {
chart.zoom([4,7]);

hoverChart(chart);

const tooltipContainer = chart.$.tooltip;
const top = Math.floor(+tooltipContainer.style("top").replace(/px/, ""));
const left = Math.floor(+tooltipContainer.style("left").replace(/px/, ""));
const tooltipPos = {
top: 115,
left: 150
};

expect(top).to.be.equal(tooltipPos.top);
expect(left).to.be.above(tooltipPos.left);
});
});
});

describe("tooltip positionFunction", () => {
Expand All @@ -243,7 +266,7 @@ describe("TOOLTIP", function() {
it("should be set to the coordinate where the function returned", () => {
hoverChart(chart);

const tooltipContainer = d3.select(chart.element).select(`.${CLASS.tooltipContainer}`);
const tooltipContainer = chart.$.tooltip;
const top = Math.floor(+tooltipContainer.style("top").replace(/px/, ""));
const left = Math.floor(+tooltipContainer.style("left").replace(/px/, ""));

Expand All @@ -253,11 +276,6 @@ describe("TOOLTIP", function() {
});

describe("tooltip order", () => {
after(() => {
/*delete args.data.type;
delete args.data.groups;*/
});

it("should sort values in data display order", () => {
checkTooltip(chart, [
`${CLASS.tooltipName}-data1`,
Expand Down Expand Up @@ -383,7 +401,7 @@ describe("TOOLTIP", function() {
hoverChart(chart);

[chart, chart2].forEach(v => {
const tooltipContainer = v.internal.tooltip;
const tooltipContainer = v.$.tooltip;
const top = parseInt(tooltipContainer.style("top"));
const left = parseInt(tooltipContainer.style("left"));

Expand Down Expand Up @@ -547,7 +565,7 @@ describe("TOOLTIP", function() {
});

it("tooltip should be displayed", () => {
const tooltip = chart.internal.tooltip;
const tooltip = chart.$.tooltip;
const pos = {
left: tooltip.style("left"),
top: tooltip.style("top")
Expand Down
84 changes: 39 additions & 45 deletions src/internals/tooltip.js
Expand Up @@ -28,13 +28,13 @@ extend(ChartInternal.prototype, {
if (config.tooltip_init_show) {
if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
const targets = $$.data.targets[0];
const len = targets.values.length;
let i;
let val;

config.tooltip_init_x = $$.parseDate(config.tooltip_init_x);

for (i = 0; i < len; i++) {
if ((targets.values[i].x - config.tooltip_init_x) === 0) {
for (i = 0; (val = targets.values[i]); i++) {
if ((val.x - config.tooltip_init_x) === 0) {
break;
}
}
Expand Down Expand Up @@ -68,15 +68,9 @@ extend(ChartInternal.prototype, {
const nameFormat = config.tooltip_format_name || (name => name);
const valueFormat = config.tooltip_format_value || defaultValueFormat;
const order = config.tooltip_order;
let text;
let title;
let hiValue;
let loValue;
let value;
let name;
let bgcolor;

const getRowValue = row => $$.getBaseValue(row);
const getBgColor = $$.levelColor ? row => $$.levelColor(row.value) : row => color(row.id);

if (order === null && config.data_groups.length) {
// for stacked data, order should aligned with the visually displayed data
Expand Down Expand Up @@ -108,22 +102,29 @@ extend(ChartInternal.prototype, {
d.sort(order);
}

for (let i = 0, row, len = d.length; i < len; i++) {
let text;

for (let i = 0, row, rangeContent, value, len = d.length; i < len; i++) {
if (!((row = d[i]) && (getRowValue(row) || getRowValue(row) === 0))) {
continue;
}

const isAreaRangeType = $$.isAreaRangeType(row);

if (!text) {
title = sanitise(titleFormat ? titleFormat(row.x) : row.x);
const title = sanitise(titleFormat ? titleFormat(row.x) : row.x);

text = (title || title === 0 ? `<tr><th colspan="2">${title}</th></tr>` : "");
text = `<table class="${$$.CLASS.tooltip}">${text}`;
}

if (isAreaRangeType) {
hiValue = sanitise(valueFormat($$.getAreaRangeData(row, "high"), row.ratio, row.id, row.index, d));
loValue = sanitise(valueFormat($$.getAreaRangeData(row, "low"), row.ratio, row.id, row.index, d));
if ($$.isAreaRangeType(row)) {
rangeContent = ["high", "low"]
.map(v => sanitise(
valueFormat($$.getAreaRangeData(row, v), row.ratio, row.id, row.index, d)
));

rangeContent = `<b>Mid:</b> ${value} <b>High:</b> ${rangeContent[0]} <b>Low:</b> ${rangeContent[1]}`;
} else {
rangeContent = null;
}

value = sanitise(valueFormat(getRowValue(row), row.ratio, row.id, row.index, d));
Expand All @@ -134,19 +135,16 @@ extend(ChartInternal.prototype, {
continue;
}

name = sanitise(nameFormat(row.name, row.ratio, row.id, row.index));
bgcolor = $$.levelColor ? $$.levelColor(row.value) : color(row.id);
const name = sanitise(nameFormat(row.name, row.ratio, row.id, row.index));
const bgcolor = getBgColor(row);

text += `<tr class="${$$.CLASS.tooltipName}${$$.getTargetSelectorSuffix(row.id)}"><td class="name">`;

text += $$.patterns ?
`<svg><rect style="fill:${bgcolor}" width="10" height="10"></rect></svg>` :
`<span style="background-color:${bgcolor}"></span>`;


text += `${name}</td><td class="value">${
isAreaRangeType ? `<b>Mid:</b> ${value} <b>High:</b> ${hiValue} <b>Low:</b> ${loValue}` : value
}</td></tr>`;
text += `${name}</td><td class="value">${rangeContent || value}</td></tr>`;
}
}

Expand All @@ -165,40 +163,36 @@ extend(ChartInternal.prototype, {
tooltipPosition(dataToShow, tWidth, tHeight, element) {
const $$ = this;
const config = $$.config;
const forArc = $$.hasArcType();
const isTouch = $$.inputType === "touch";
const mouse = d3Mouse(element);
let [left, top] = d3Mouse(element);

const svgLeft = $$.getSvgLeft(true);
let chartRight;
let chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();

let left;
let right;
let top;
top += 20;

// Determine tooltip position
if (forArc) {
const raw = isTouch || $$.hasType("radar");

top = mouse[1] + (raw ? 0 : $$.height / 2) + 20;
left = mouse[0] + (raw ? 0 : ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2);
if ($$.hasArcType()) {
const raw = $$.inputType === "touch" || $$.hasType("radar");

chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
right = left + tWidth;
if (!raw) {
top += $$.height / 2;
left += ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2;
}
} else {
const dataScale = $$.x(dataToShow[0].x);

if (config.axis_rotated) {
left = svgLeft + mouse[0] + 100;
right = left + tWidth;
chartRight = $$.currentWidth - $$.getCurrentPaddingRight();
top = $$.x(dataToShow[0].x) + 20;
top = dataScale + 20;
left += svgLeft + 100;
chartRight -= svgLeft;
} else {
left = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
right = left + tWidth;
chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
top = mouse[1] + 15;
top -= 5;
left = svgLeft + $$.getCurrentPaddingLeft(true) + 20 + ($$.zoomScale ? left : dataScale);
}
}

const right = left + tWidth;

if (right > chartRight) {
// 20 is needed for Firefox to keep tooltip width
left -= right - chartRight + 20;
Expand Down

0 comments on commit 45785ab

Please sign in to comment.