Skip to content

Commit

Permalink
fix(interaction): Fix incorrect data.onover/out event call
Browse files Browse the repository at this point in the history
Fix onover/onout event call on same x axis bar shape.

Fix #2901
  • Loading branch information
netil committed Oct 21, 2022
1 parent 7249c49 commit 1389d85
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 22 deletions.
10 changes: 8 additions & 2 deletions src/ChartInternal/data/IData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
export interface IDataRow {
x: number;
type TDataRow = {
value: number | null;
id: string;
index: number;
name?: string;
};

export interface IDataRow extends TDataRow {
x: number;
}
export interface IArcDataRow extends TDataRow {
ratio: number;
}

export interface IData {
Expand Down
45 changes: 28 additions & 17 deletions src/ChartInternal/interactions/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {drag as d3Drag} from "d3-drag";
import {$ARC, $AXIS, $COMMON, $SHAPE} from "../../config/classes";
import {KEY} from "../../module/Cache";
import {emulateEvent, getPointer, isNumber, isObject} from "../../module/util";
import type {IArcDataRow} from "../data/IData";

export default {
selectRectForSingle(context, eventRect, index: number): void {
Expand Down Expand Up @@ -107,42 +108,52 @@ export default {
* @param {number|object} d data object
* @private
*/
setOverOut(isOver: boolean, d): void {
setOverOut(isOver: boolean, d: number | IArcDataRow): void {
const $$ = this;
const {config, state: {hasRadar}, $el: {main}} = $$;
const isArc = isObject(d);

// Call event handler
if (isArc || d !== -1) {
let callback = config[isOver ? "data_onover" : "data_onout"].bind($$.api);
const callback = config[isOver ? "data_onover" : "data_onout"].bind($$.api);

config.color_onover && $$.setOverColor(isOver, d, isArc);

if (isArc) {
callback(d, main.select(`.${$ARC.arc}${$$.getTargetSelectorSuffix(d.id)}`).node());
if (isArc && "id") {
callback(d, main.select(`.${$ARC.arc}${$$.getTargetSelectorSuffix((d as IArcDataRow).id)}`).node());
} else if (!config.tooltip_grouped) {
let last = $$.cache.get(KEY.setOverOut) || [];
const last = $$.cache.get(KEY.setOverOut) || [];

const shape = main.selectAll(`.${$SHAPE.shape}-${d}`)
// select based on the index
const shapesAtIndex = main.selectAll(`.${$SHAPE.shape}-${d}`)
.filter(function(d) {
return $$.isWithinShape(this, d);
});

shape
.each(function(d) {
if (last.length === 0 || last.every(v => v !== this)) {
callback(d, this);
last.push(this);
}
});
// filter if has new selection
const shape = shapesAtIndex.filter(function() {
return last.every(v => v !== this);
});

if (last.length > 0 && shape.empty()) {
callback = config.data_onout.bind($$.api);
// call onout callback
if (!isOver || shapesAtIndex.empty() || (
last.length === shape.size() && shape.nodes().every(((v, i) => v !== last[i]))
)) {
while (last.length) {
const target = last.pop();

last.forEach(v => callback(d3Select(v).datum(), v));
last = [];
config.data_onout.bind($$.api)(d3Select(target).datum(), target);
}
}

// call onover callback
shape.each(function() {
if (isOver) {
callback(d3Select(this).datum(), this);
last.push(this);
}
});

$$.cache.add(KEY.setOverOut, last);
} else {
if (isOver) {
Expand Down
83 changes: 80 additions & 3 deletions test/interactions/interaction-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
* billboard.js project is licensed under the MIT license
*/
/* eslint-disable */
// @ts-nocheck
/* global describe, beforeEach, it, expect */
import {expect} from "chai";
import sinon from "sinon";
import {select as d3Select} from "d3-selection";
import util from "../assets/util";
import {$ARC, $AXIS, $BAR, $CIRCLE, $COMMON, $FOCUS, $EVENT, $SELECT, $SHAPE} from "../../src/config/classes";
import bb from "../../src";

describe("INTERACTION", () => {
let chart;
Expand Down Expand Up @@ -479,13 +479,90 @@ describe("INTERACTION", () => {
expect(v.d.x).to.be.equal(index);
expect(v.element.tagName).to.be.equal("circle");

expect(itemOut[i].d).to.be.deep.equal(v.d);
expect(itemOut[i].element).to.be.deep.equal(v.element);
expect(itemOut.some(t => JSON.stringify(t) === JSON.stringify(v))).to.be.true;
expect(itemOut.some(t => t.element === v.element)).to.be.true;
});

done();
}, 500);
});

it("set options", () => {
args = {
data: {
columns: [
["data1", 30, 200, 200, 130, 150, 250],
["data2", 130, 100, 140, 150, 150, 50],
["data3", 130, 100, 140, 220, 150, 50]
],
groups: [
["data1", "data2"]
],
type: "bar",
onover: spyOver,
onout: spyOut
},
bar: {
sensitivity: 0
},
tooltip: {
show: false,
grouped: false,
}
};
});

it("callback should called correctly on same x Axis for bar type.", done => {
new Promise(resolve => {
util.hoverChart(chart, "mousemove", {
clientX: 360,
clientY: 300
});

setTimeout(resolve, 300);
}).then(() => {
new Promise(resolve => {
util.hoverChart(chart, "mousemove", {
clientX: 340,
clientY: 300
});

setTimeout(resolve, 300);
});
}).then(() => {
new Promise(resolve => {
util.hoverChart(chart, "mousemove", {
clientX: 340,
clientY: 200
});

setTimeout(resolve, 300);
});
})
.then(() => {
const expectedX = 3;
const expectedFlow = {
over: ["data3", "data2", "data1"],
out: ["data3", "data2"]
};

itemOver
.map(({d: {x, id}}) => ({x, id}))
.forEach((v, i) => {
expect(v.x).to.be.equal(expectedX);
expect(expectedFlow.over[i]).to.be.equal(v.id);
});

itemOut
.map(({d: {x, id}}) => ({x, id}))
.forEach((v, i) => {
expect(v.x).to.be.equal(expectedX);
expect(expectedFlow.out[i]).to.be.equal(v.id);
});

done();
});
});
});

describe("check for data.onclick", () => {
Expand Down

0 comments on commit 1389d85

Please sign in to comment.