Skip to content

Commit

Permalink
Merge branch 'master' into nightly
Browse files Browse the repository at this point in the history
  • Loading branch information
netil committed Oct 24, 2023
2 parents 9b35c13 + 9b90c81 commit 09d130a
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 21 deletions.
7 changes: 5 additions & 2 deletions src/ChartInternal/interactions/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export default {

if (element) {
const isMultipleX = $$.isMultipleX();
const isRotated = config.axis_rotated;
let {width, left, top} = element.getBoundingClientRect();

if (hasAxis && !hasRadar && !isMultipleX) {
Expand All @@ -197,9 +198,11 @@ export default {
}

const x = left + (mouse ? mouse[0] : 0) + (
isMultipleX || config.axis_rotated ? 0 : (width / 2)
isMultipleX || isRotated ? 0 : (width / 2)
);
const y = top + (mouse ? mouse[1] : 0);

// value 4, is to adjust coordinate value set from: scale.ts - updateScales(): $$.getResettedPadding(1)
const y = top + (mouse ? mouse[1] : 0) + (isRotated ? 4 : 0);
const params = {
screenX: x,
screenY: y,
Expand Down
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 src/config/Options/axis/x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export default {
* @memberof Options
* @type {Function|string}
* @default undefined
* @see [D3's time specifier](https://github.com/d3/d3-time-format#locale_format)
* @see [D3's time specifier](https://d3js.org/d3-time-format#locale_format)
* @example
* axis: {
* x: {
Expand Down
2 changes: 1 addition & 1 deletion src/config/Options/data/axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default {
* type: "timeseries"
* }
* }
* @see [D3's time specifier](https://github.com/d3/d3-time-format#locale_format)
* @see [D3's time specifier](https://d3js.org/d3-time-format#locale_format)
*/
data_xFormat: "%Y-%m-%d",

Expand Down
72 changes: 72 additions & 0 deletions test/api/tooltip-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,76 @@ describe("API tooltip", () => {
expect(tooltip.select(".value").text()).to.be.equal("34.6%");
});
});

describe("on rotated axis", () => {
before(() => {
args = {
data: {
columns: [
["data1", 150, 140, 110, 100, 300],
["data1", 30, 200, 100, 400, 150],
["data2", 130, 340, 200, 500, 250]
],
type: "line"
},
axis: {
rotated: true
}
};
});

function checkTooltip(x, categoryName?) {
const type = chart.config("axis.x.type");
let value = x;

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

let th = chart.$.tooltip.select("th").text();

if (type === "timeseries") {
value = chart.internal.format.xAxisTick(x);
}

expect(isNaN(th) ? th : +th).to.be.equal(categoryName ?? value);
}

it("check for indexed x axis type", () => {
chart.xs().data1.forEach(v => {
checkTooltip(v);
});
});

it("set options", () => {
args.data.x = "x";
args.data.columns.unshift(
["x", "2023-01-01", "2023-01-02", "2023-01-03", "2023-01-04", "2023-01-05"]
);
args.axis.x = {
type: "timeseries",
tick: {
format: "%Y-%m-%d"
}
};
});

it("check for timeseries x axis type", () => {
chart.xs().data1.forEach(v => {
checkTooltip(+v);
});
});

it("set options", () => {
args.data.columns[0] = ["x", "a", "b", "c", "d", "e"];
args.axis.x = {
type: "category"
};
});

it("check for category x axis type", () => {
chart.categories().forEach((v, i) => {
checkTooltip(i, v);
});
});
});
});
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
133 changes: 131 additions & 2 deletions 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 Expand Up @@ -1758,7 +1887,7 @@ describe("TOOLTIP", function() {
chart.$.chart.style("margin-top", "100px");
chart.tooltip.show({index:1});

expect(chart.$.tooltip.select("th").text()).to.be.equal("0");
expect(chart.$.tooltip.select("th").text()).to.be.equal("1");

chart.$.chart.style("margin-top", null);
});
Expand Down

0 comments on commit 09d130a

Please sign in to comment.