Skip to content

Commit

Permalink
Enable event filtering per plugin (#8876)
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle committed Apr 10, 2021
1 parent d6d189c commit 9e70913
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 13 deletions.
30 changes: 25 additions & 5 deletions docs/configuration/interactions.md
Expand Up @@ -17,7 +17,7 @@ Namespace: `options`

| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `events` | `string[]` | `['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']` | The `events` option defines the browser events that the chart should listen to for tooltips and hovering. [more...](#event-option)
| `events` | `string[]` | `['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']` | The `events` option defines the browser events that the chart should listen to for. Each of these events trigger hover and are passed to plugins. [more...](#event-option)
| `onHover` | `function` | `null` | Called when any of the events fire. Passed the event, an array of active elements (bars, points, etc), and the chart.
| `onClick` | `function` | `null` | Called if the event is of type `'mouseup'` or `'click'`. Passed the event, an array of active elements, and the chart.

Expand All @@ -27,12 +27,32 @@ For example, to have the chart only respond to click events, you could do:

```javascript
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
// This chart will not respond to mousemove, etc
type: 'line',
data: data,
options: {
// This chart will not respond to mousemove, etc
events: ['click']
}
});
```

Events for each plugin can be further limited by defining (allowed) events array in plugin options:

```javascript
var chart = new Chart(ctx, {
type: 'line',
data: data,
options: {
// All of these (default) events trigger a hover and are passed to all plugins,
// unless limited at plugin options
events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
plugins: {
tooltip: {
// Tooltip will only receive click events
events: ['click']
}
}
}
});
```

Expand Down
10 changes: 6 additions & 4 deletions src/core/core.controller.js
Expand Up @@ -1018,10 +1018,11 @@ class Chart {
* returned value can be used, for instance, to interrupt the current action.
* @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
* @param {Object} [args] - Extra arguments to apply to the hook call.
* @param {import('./core.plugins').filterCallback} [filter] - Filtering function for limiting which plugins are notified
* @returns {boolean} false if any of the plugins return false, else returns true.
*/
notifyPlugins(hook, args) {
return this._plugins.notify(this, hook, args);
notifyPlugins(hook, args, filter) {
return this._plugins.notify(this, hook, args, filter);
}

/**
Expand Down Expand Up @@ -1049,15 +1050,16 @@ class Chart {
_eventHandler(e, replay) {
const me = this;
const args = {event: e, replay, cancelable: true};
const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.type);

if (me.notifyPlugins('beforeEvent', args) === false) {
if (me.notifyPlugins('beforeEvent', args, eventFilter) === false) {
return;
}

const changed = me._handleEvent(e, replay);

args.cancelable = false;
me.notifyPlugins('afterEvent', args);
me.notifyPlugins('afterEvent', args, eventFilter);

if (changed || args.changed) {
me.render();
Expand Down
15 changes: 13 additions & 2 deletions src/core/core.plugins.js
Expand Up @@ -7,6 +7,16 @@ import {callback as callCallback, isNullOrUndef, valueOrDefault} from '../helper
* @typedef { import("../plugins/plugin.tooltip").default } Tooltip
*/

/**
* @callback filterCallback
* @param {{plugin: object, options: object}} value
* @param {number} [index]
* @param {array} [array]
* @param {object} [thisArg]
* @return {boolean}
*/


export default class PluginService {
constructor() {
this._init = [];
Expand All @@ -19,17 +29,18 @@ export default class PluginService {
* @param {Chart} chart - The chart instance for which plugins should be called.
* @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
* @param {object} [args] - Extra arguments to apply to the hook call.
* @param {filterCallback} [filter] - Filtering function for limiting which plugins are notified
* @returns {boolean} false if any of the plugins return false, else returns true.
*/
notify(chart, hook, args) {
notify(chart, hook, args, filter) {
const me = this;

if (hook === 'beforeInit') {
me._init = me._createDescriptors(chart, true);
me._notify(me._init, chart, 'install');
}

const descriptors = me._descriptors(chart);
const descriptors = filter ? me._descriptors(chart).filter(filter) : me._descriptors(chart);
const result = me._notify(descriptors, chart, hook, args);

if (hook === 'destroy') {
Expand Down
27 changes: 27 additions & 0 deletions test/specs/core.plugin.tests.js
Expand Up @@ -393,5 +393,32 @@ describe('Chart.plugins', function() {
plugins: [plugin]
});
});

it('should filter event callbacks by plugin events array', async function() {
const results = [];
const chart = window.acquireChart({
options: {
events: ['mousemove', 'test', 'test2'],
plugins: {
testPlugin: {
events: ['test']
}
}
},
plugins: [{
id: 'testPlugin',
beforeEvent: function(_chart, args) {
results.push('before' + args.event.type);
},
afterEvent: function(_chart, args) {
results.push('after' + args.event.type);
}
}]
});
await jasmine.triggerMouseEvent(chart, 'mousemove', {x: 0, y: 0});
await jasmine.triggerMouseEvent(chart, 'test', {x: 0, y: 0});
await jasmine.triggerMouseEvent(chart, 'test2', {x: 0, y: 0});
expect(results).toEqual(['beforetest', 'aftertest']);
});
});
});
36 changes: 34 additions & 2 deletions test/specs/plugin.tooltip.tests.js
Expand Up @@ -1536,8 +1536,8 @@ describe('Plugin.Tooltip', function() {
});
});

describe('active events', function() {
it('should set the active events', function() {
describe('active elements', function() {
it('should set the active elements', function() {
var chart = window.acquireChart({
type: 'line',
data: {
Expand All @@ -1556,4 +1556,36 @@ describe('Plugin.Tooltip', function() {
expect(chart.tooltip.getActiveElements()[0].element).toBe(meta.data[0]);
});
});

describe('events', function() {
it('should not be called on events not in plugin events array', async function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
label: 'Dataset 1',
data: [10, 20, 30],
pointHoverBorderColor: 'rgb(255, 0, 0)',
pointHoverBackgroundColor: 'rgb(0, 255, 0)'
}],
labels: ['Point 1', 'Point 2', 'Point 3']
},
options: {
plugins: {
tooltip: {
events: ['click']
}
}
}
});

const meta = chart.getDatasetMeta(0);
const point = meta.data[1];

await jasmine.triggerMouseEvent(chart, 'mousemove', point);
expect(chart.tooltip.opacity).toEqual(0);
await jasmine.triggerMouseEvent(chart, 'click', point);
expect(chart.tooltip.opacity).toEqual(1);
});
});
});

0 comments on commit 9e70913

Please sign in to comment.