Skip to content

Commit

Permalink
fix(tooltip): Fix wrong tooltip position with padding option
Browse files Browse the repository at this point in the history
Reflect padding top/left option on tooltip positioning.

Fix #3473
  • Loading branch information
netil committed Oct 23, 2023
1 parent 157229f commit 041e144
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 16 deletions.
25 changes: 19 additions & 6 deletions src/ChartInternal/internals/size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import {document} from "../../module/browser";
import {$AXIS, $SUBCHART} from "../../config/classes";
import {ceil10, capitalize, isNumber, isEmpty, isUndefined} from "../../module/util";
import {ceil10, capitalize, isNumber, isEmpty, isString, isUndefined} from "../../module/util";

export default {
/**
Expand Down Expand Up @@ -87,13 +87,26 @@ export default {
getSvgLeft(withoutRecompute?: boolean): number {
const $$ = this;
const {config, $el} = $$;
const hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner);
const leftAxisClass = config.axis_rotated ? $AXIS.axisX : $AXIS.axisY;
const isRotated = config.axis_rotated;
const hasLeftAxisRect = isRotated || (!isRotated && !config.axis_y_inner);
const leftAxisClass = isRotated ? $AXIS.axisX : $AXIS.axisY;
const leftAxis = $el.main.select(`.${leftAxisClass}`).node();
const leftLabel = config[`axis_${isRotated ? "x" : "y"}_label`];
let labelWidth = 0;

// if axis label position set to inner, exclude from the value
if (isString(leftLabel) || isString(leftLabel.text) || /^inner-/.test(leftLabel?.position)) {
const label = $el.main.select(`.${leftAxisClass}-label`);

if (!label.empty()) {
labelWidth = label.node().getBoundingClientRect().left;
}
}

const svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : {right: 0};
const chartRect = $el.chart.node().getBoundingClientRect();
const chartRectLeft = $el.chart.node().getBoundingClientRect().left + labelWidth;
const hasArc = $$.hasArcType();
const svgLeft = svgRect.right - chartRect.left -
const svgLeft = svgRect.right - chartRectLeft -
(hasArc ? 0 : $$.getCurrentPaddingByDirection("left", withoutRecompute));

return svgLeft > 0 ? svgLeft : 0;
Expand Down Expand Up @@ -204,7 +217,7 @@ export default {
padding += isRotated ? (
!isFitPadding && isUndefined(paddingOption) ? 10 : 2
) : !isAxisShow || isAxisInner ? (isFitPadding ? 2 : 1) : 0;
} else if (type === "left" && isRotated) {
} else if (type === "left" && isRotated && isUndefined(paddingOption)) {
padding = !config.axis_x_show ?
1 : (isFitPadding ? axisSize : Math.max(axisSize, 40));
}
Expand Down
21 changes: 13 additions & 8 deletions src/ChartInternal/internals/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,34 +330,39 @@ export default {
const hasGauge = $$.hasType("gauge") && !config.gauge_fullCircle;
const hasTreemap = state.hasTreemap;
const isRotated = config.axis_rotated;
const hasArcType = $$.hasArcType();
const svgLeft = $$.getSvgLeft(true);
let chartRight = svgLeft + current.width - $$.getCurrentPaddingByDirection("right");
const chartLeft = $$.getCurrentPaddingByDirection("left", true);
const size = 20;
let {x, y} = currPos;

// Determine tooltip position
if ($$.hasArcType()) {
if (hasArcType) {
const raw = inputType === "touch" || $$.hasType("radar");

if (!raw) {
y += hasGauge ? height : height / 2;
x += (width - (isLegendRight ? $$.getLegendWidth() : 0)) / 2;
y += hasGauge ? height : height / 2;
}
} else if (!hasTreemap) {
const padding = {
top: $$.getCurrentPaddingByDirection("top", true),
left: $$.getCurrentPaddingByDirection("left", true)
};

if (isRotated) {
y = currPos.xAxis + size;
x += svgLeft;
x += svgLeft + padding.left + size;
y = padding.top + currPos.xAxis + size;
chartRight -= svgLeft;
} else {
y -= 5;
x = svgLeft + chartLeft + size + (scale.zoom ? x : currPos.xAxis);
x = svgLeft + padding.left + size + (scale.zoom ? x : currPos.xAxis);
y += padding.top - 5;
}
}

// when tooltip left + tWidth > chart's width
if ((x + tWidth + 15) > chartRight) {
x -= isRotated ? tWidth - chartLeft : tWidth + (hasTreemap ? 0 : chartLeft);
x -= tWidth + (hasTreemap || hasArcType ? 0 : (isRotated ? size * 2 : 38));
}

if (y + tHeight > current.height) {
Expand Down
2 changes: 1 addition & 1 deletion test/internals/text-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ describe("TEXT", () => {
});

it("should locate labels above each data point", () => {
const expectedXs = [72, 537, 72, 527]; // 72.50132230092231
const expectedXs = [72, 527, 72, 527]; // 72.50132230092231
const expectedYs = [9, 157, 305, 434];

chart.$.main.selectAll(`.${$TEXT.texts}-data1 text`)
Expand Down
131 changes: 130 additions & 1 deletion test/internals/tooltip-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
namespaces as d3Namespaces
} from "d3-selection";
import util from "../assets/util";
import {$SHAPE, $TOOLTIP} from "../../src/config/classes";
import {$CIRCLE, $SHAPE, $TOOLTIP} from "../../src/config/classes";
import {isNumber, isUndefined, isString} from "../../src/module/util";

describe("TOOLTIP", function() {
Expand Down Expand Up @@ -667,6 +667,135 @@ describe("TOOLTIP", function() {

});

describe("with padding", () => {
before(() => {
args = {
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 130, 340, 200, 500, 250, 350]
],
type: "line"
},
padding: {
left: 400
},
axis: {
rotated: false,
y: {
label: "y axis label"
}
}
}
});

it("should displayed in correct position with padding.left", () => {
const x = 4;

// when
chart.tooltip.show({x});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointLeft = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().left;

expect(tooltip.left + tooltip.width).to.be.below(pointLeft);
expect(tooltip.left + tooltip.width).to.be.closeTo(pointLeft, 15);
});

it("set options: axis.y.label", () => {
args.axis.y.label = {
text: "y axis label"
};
});

it("should displayed in correct position with padding.left", () => {
const x = 4;

// when
chart.tooltip.show({x});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointLeft = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().left;

expect(tooltip.left + tooltip.width).to.be.below(pointLeft);
expect(tooltip.left + tooltip.width).to.be.closeTo(pointLeft, 15);
});

it("set options: axis.y.label", () => {
args.axis.y.label = {
text: "y axis label",
position: "inner-middle"
};
});

it("should displayed in correct position with padding.left", () => {
const x = 4;

// when
chart.tooltip.show({x});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointLeft = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().left;

expect(tooltip.left + tooltip.width).to.be.below(pointLeft);
expect(tooltip.left + tooltip.width).to.be.closeTo(pointLeft, 15);
});

it("set options: axis.rotated=true", () => {
args.axis.rotated = true;
});

it("should displayed in correct position with padding.left on rotated axis", () => {
const x = 2;

// when
util.hoverChart(chart, "mousemove", {clientX: 0, clientY: 200});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointLeft = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().left;

expect(pointLeft).to.be.below(tooltip.left + tooltip.width);
expect(tooltip.left).to.be.closeTo(pointLeft, 30);
});

it("set options: axis.rotated=true", () => {
args.axis.rotated = false;
args.padding = {
top: 100
};
});

it("should displayed in correct position with padding.top", () => {
const x = 3;

// when
chart.tooltip.show({x});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointTop = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().top;

expect(tooltip.top).to.be.below(pointTop);
expect(tooltip.top + tooltip.height).to.be.closeTo(pointTop, 30);
});

it("set options: axis.rotated=true", () => {
args.axis.rotated = true;
});

it("should displayed in correct position with padding.top on rotated axis", () => {
const x = 2;

// when
util.hoverChart(chart, "mousemove", {clientX: 0, clientY: 250});

const tooltip = chart.$.tooltip.node().getBoundingClientRect();
const pointTop = chart.$.circles.filter(`.${$CIRCLE.circle}-${x}`).node().getBoundingClientRect().top;

expect(pointTop).to.be.below(tooltip.top + tooltip.height);
expect(tooltip.top).to.be.closeTo(pointTop, 30);
});
});

describe("on rotated axis", () => {
before(() => {
args = {
Expand Down

0 comments on commit 041e144

Please sign in to comment.