Skip to content

Commit

Permalink
feat(tooltip): Enhancement on callback options
Browse files Browse the repository at this point in the history
Pass 2 arguments to callbacks:
- ctx: current chart instance
- selectedData: current dataset object

Fix #1216
  • Loading branch information
netil authored Jan 23, 2020
1 parent b815800 commit 30a7718
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 77 deletions.
102 changes: 38 additions & 64 deletions spec/internals/tooltip-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,82 +100,56 @@ describe("TOOLTIP", function() {
});

describe("Tooltip callbacks", () => {
let called = [];
const spy = {
onshow: sinon.spy((ctx, data) => called.push({ctx, data, type: "onshow"})),
onshown: sinon.spy((ctx, data) => called.push({ctx, data, type: "onshown"})),
onhide: sinon.spy((ctx, data) => called.push({ctx, data, type: "onhide"})),
onhidden: sinon.spy((ctx, data) => called.push({ctx, data, type: "onhidden"}))
};

const check = fn => {
["show", "shown", "hide", "hidden"].forEach((v, i) => {
fn(`on${v}`, i);
});
}

before(() => {
args.tooltip.onshow = () => {
if (chart.internal.cache.callback_test === 0) {
chart.internal.cache.callback_test++;
}
};
args.tooltip.onshown = () => {
if (chart.internal.cache.callback_test === 1) {
chart.internal.cache.callback_test++;
}
};
args.tooltip.onhide = () => {
if (chart.internal.cache.callback_test === 2) {
chart.internal.cache.callback_test++;
}
};
args.tooltip.onhidden = () => {
if (chart.internal.cache.callback_test === 3) {
chart.internal.cache.callback_test++;
}
};
check((name) => {
args.tooltip[name] = spy[name];
});
});

after(() => {
args.tooltip.onshow = () => {};
args.tooltip.onshown = () => {};
args.tooltip.onhide = () => {};
args.tooltip.onhidden = () => {};
afterEach(() => {
called = [];

check((name) => {
spy[name].resetHistory();
});
});

it("chart tooltip onshow/onshown/onhide/onhidden functions should be called", () => {
chart.internal.cache.callback_test = 0;
const expectedData = JSON.stringify([
{x: 6, value: 100, id: 'data1', index: 2, name: 'data1'},
{x: 6, value: 10, id: 'data2', index: 2, name: 'data2'},
{x: 6, value: 110, id: 'data3', index: 2, name: 'data3'}
]);

checkCallback(chart, true);
let test = chart.internal.cache.callback_test;
expect(test).to.be.equal(4);
});

describe("show/shown should execute in proper order", () => {
before(() => {
chart.internal.cache.callback_test = 0;
chart.internal.cache.callback_test2 = 0;
args.tooltip.onshow = () => {
chart.internal.cache.callback_test++;
};
args.tooltip.onshown = () => {
chart.internal.cache.callback_test++;
chart.internal.cache.callback_test2 = chart.internal.cache.callback_test;
};
});
check((name, i) => {
const call = called[i];

it("onshown should execute after onshow", () => {
chart.internal.cache.callback_test = 0;
checkCallback(chart, false);
expect(chart.internal.cache.callback_test2).to.be.equal(2);
});
});
expect(spy[name].calledOnce).to.be.true;

describe("onhide/onhidden should execute in proper order", () => {
before(() => {
chart.internal.cache.callback_test = 0;
args.tooltip.onshow = () => {};
args.tooltip.onshown = () => {};
args.tooltip.onhide = () => {
chart.internal.cache.callback_test++;
};
args.tooltip.onhidden = () => {
chart.internal.cache.callback_test++;
chart.internal.cache.callback_test2 = chart.internal.cache.callback_test;
// check the call order: onshow -> onshown -> onhide -> onhidden
expect(call.type).to.be.equal(name);

};
});
// check the passed context argument
expect(call.ctx).to.be.equal(chart);

it("onhidden should execute after onhide", () => {
chart.internal.cache.callback_test = 0;
checkCallback(chart, true);
expect(chart.internal.cache.callback_test2).to.be.equal(2);
// check the passed data argument
expect(JSON.stringify(call.data)).to.be.equal(expectedData);
});
});
});
Expand Down
35 changes: 31 additions & 4 deletions src/config/Options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3777,13 +3777,40 @@ export default class Options {
* },
*
* // fires prior tooltip is shown
* onshow: function() { ...},
* onshow: function(ctx, selectedData) {
* ctx; // current chart instance
*
* // current dataset selected
* // ==> [{x: 4, value: 150, id: "data2", index: 4, name: "data2"}, ...]
* selectedData;
* },
*
* // fires prior tooltip is hidden
* onhide: function() { ... },
* onhide: function(ctx, selectedData) {
* ctx; // current chart instance
*
* // current dataset selected
* // ==> [{x: 4, value: 150, id: "data2", index: 4, name: "data2"}, ...]
* selectedData;
* },
*
* // fires after tooltip is shown
* onshown: function() { ... },
* onshown: function(ctx, selectedData) {
* ctx; // current chart instance
*
* // current dataset selected
* // ==> [{x: 4, value: 150, id: "data2", index: 4, name: "data2"}, ...]
* selectedData;
* },
*
* // fires after tooltip is hidden
* onhidden: function() { ... },
* onhidden: function(ctx, selectedData) {
* ctx; // current chart instance
*
* // current dataset selected
* // ==> [{x: 4, value: 150, id: "data2", index: 4, name: "data2"}, ...]
* selectedData;
* },
*
* // Link any tooltips when multiple charts are on the screen where same x coordinates are available
* // Useful for timeseries correlation
Expand Down
12 changes: 7 additions & 5 deletions src/internals/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ extend(ChartInternal.prototype, {
if (!datum || datum.current !== dataStr) {
const index = selectedData.concat().sort()[0].index;

callFn(config.tooltip_onshow, $$);
callFn(config.tooltip_onshow, $$, $$.api, selectedData);

// set tooltip content
$$.tooltip
Expand All @@ -320,7 +320,7 @@ extend(ChartInternal.prototype, {
height: height = $$.tooltip.property("offsetHeight")
});

callFn(config.tooltip_onshown, $$);
callFn(config.tooltip_onshown, $$, $$.api, selectedData);
$$._handleLinkedCharts(true, index);
}

Expand All @@ -344,16 +344,18 @@ extend(ChartInternal.prototype, {
const $$ = this;
const config = $$.config;

if (!config.tooltip_doNotHide || force) {
callFn(config.tooltip_onhide, $$);
if (this.tooltip.style("display") !== "none" && (!config.tooltip_doNotHide || force)) {
const selectedData = JSON.parse(this.tooltip.datum().current);

callFn(config.tooltip_onhide, $$, $$.api, selectedData);

// hide tooltip
this.tooltip
.style("display", "none")
.style("visibility", "hidden") // for IE9
.datum(null);

callFn(config.tooltip_onhidden, $$);
callFn(config.tooltip_onhidden, $$, $$.api, selectedData);
}
},

Expand Down
8 changes: 4 additions & 4 deletions types/options.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,22 +930,22 @@ export interface TooltipOptions {
/**
* Set a callback that will be invoked before the tooltip is shown.
*/
onshow?(): void;
onshow?(ctx: Chart, selectedData: DataItem): void;

/**
* Set a callback that will be invoked before the tooltip is hidden.
*/
onhide?(): void;
onhide?(ctx: Chart, selectedData: DataItem): void;

/**
* Set a callback that will be invoked after the tooltip is shown
*/
onshown?(): void;
onshown?(ctx: Chart, selectedData: DataItem): void;

/**
* Set a callback that will be invoked after the tooltip is hidden.
*/
onhidden?(): void;
onhidden?(ctx: Chart, selectedData: DataItem): void;

/**
* Set if tooltips on all visible charts with like x points are shown together when one is shown.
Expand Down

0 comments on commit 30a7718

Please sign in to comment.