From: Evert Timberg Date: Fri, 28 Jul 2017 22:07:28 +0000 (-0400) Subject: index-y interaction mode + convert horizontal bar defaults to new mode (#4458) X-Git-Tag: v2.7.0~1^2~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=326991ce50c36fd714aefc5bafd4cbe53806051f;p=thirdparty%2FChart.js.git index-y interaction mode + convert horizontal bar defaults to new mode (#4458) index-y interaction mode + convert horizontal bar defaults to new mode --- diff --git a/docs/general/interactions/README.md b/docs/general/interactions/README.md index 1ff1d55d8..585e65582 100644 --- a/docs/general/interactions/README.md +++ b/docs/general/interactions/README.md @@ -6,4 +6,5 @@ The hover configuration is passed into the `options.hover` namespace. The global | ---- | ---- | ------- | ----------- | `mode` | `String` | `'nearest'` | Sets which elements appear in the tooltip. See [Interaction Modes](./modes.md#interaction-modes) for details. | `intersect` | `Boolean` | `true` | if true, the hover mode only applies when the mouse position intersects an item on the chart. +| `axis` | `String` | `'x'` | Can be set to `'x'`, `'y'`, or `'xy'` to define which directions are used in calculating distances. Defaults to `'x'` for `index` mode and `'xy'` in `dataset` and `nearest` modes. | `animationDuration` | `Number` | `400` | Duration in milliseconds it takes to animate hover style changes. \ No newline at end of file diff --git a/docs/general/interactions/modes.md b/docs/general/interactions/modes.md index 6775c590c..a46f047e7 100644 --- a/docs/general/interactions/modes.md +++ b/docs/general/interactions/modes.md @@ -41,7 +41,7 @@ Finds the first item that intersects the point and returns it. Behaves like 'nea See `'index'` mode ## index -Finds item at the same index. If the `intersect` setting is true, the first intersecting item is used to determine the index in the data. If `intersect` false the nearest item is used to determine the index. +Finds item at the same index. If the `intersect` setting is true, the first intersecting item is used to determine the index in the data. If `intersect` false the nearest item, in the x direction, is used to determine the index. ```javascript var chart = new Chart(ctx, { @@ -55,6 +55,21 @@ var chart = new Chart(ctx, { }) ``` +To use index mode in a chart like the horizontal bar chart, where we search along the y direction, you can use the `axis` setting introduced in v2.7.0. By setting this value to `'y'` on the y direction is used. + +```javascript +var chart = new Chart(ctx, { + type: 'horizontalBar', + data: data, + options: { + tooltips: { + mode: 'index', + axis: 'y' + } + } +}) +``` + ## x-axis (deprecated) Behaves like `'index'` mode with `intersect = false`. diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index dc410e97f..5cc626809 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -31,7 +31,8 @@ defaults._set('bar', { defaults._set('horizontalBar', { hover: { - mode: 'label' + mode: 'index', + axis: 'y' }, scales: { @@ -82,7 +83,9 @@ defaults._set('horizontalBar', { var datasetLabel = data.datasets[item.datasetIndex].label || ''; return datasetLabel + ': ' + item.xLabel; } - } + }, + mode: 'index', + axis: 'y' } }); diff --git a/src/core/core.interaction.js b/src/core/core.interaction.js index 793468d43..453726952 100644 --- a/src/core/core.interaction.js +++ b/src/core/core.interaction.js @@ -66,17 +66,13 @@ function getIntersectItems(chart, position) { * @param chart {Chart} the chart to look at elements from * @param position {Point} the point to be nearest to * @param intersect {Boolean} if true, only consider items that intersect the position - * @param distanceMetric {Function} Optional function to provide the distance between + * @param distanceMetric {Function} function to provide the distance between points * @return {ChartElement[]} the nearest items */ function getNearestItems(chart, position, intersect, distanceMetric) { var minDistance = Number.POSITIVE_INFINITY; var nearestItems = []; - if (!distanceMetric) { - distanceMetric = helpers.distanceBetweenPoints; - } - parseVisibleItems(chart, function(element) { if (intersect && !element.inRange(position.x, position.y)) { return; @@ -97,11 +93,27 @@ function getNearestItems(chart, position, intersect, distanceMetric) { return nearestItems; } +/** + * Get a distance metric function for two points based on the + * axis mode setting + * @param {String} axis the axis mode. x|y|xy + */ +function getDistanceMetricForAxis(axis) { + var useX = axis.indexOf('x') !== -1; + var useY = axis.indexOf('y') !== -1; + + return function(pt1, pt2) { + var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; +} + function indexMode(chart, e, options) { var position = getRelativePosition(e, chart); - var distanceMetric = function(pt1, pt2) { - return Math.abs(pt1.x - pt2.x); - }; + // Default axis for index mode is 'x' to match old behaviour + options.axis = options.axis || 'x'; + var distanceMetric = getDistanceMetricForAxis(options.axis); var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); var elements = []; @@ -185,7 +197,9 @@ module.exports = { */ dataset: function(chart, e, options) { var position = getRelativePosition(e, chart); - var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); if (items.length > 0) { items = chart.getDatasetMeta(items[0]._datasetIndex).data; @@ -201,7 +215,7 @@ module.exports = { * @private */ 'x-axis': function(chart, e) { - return indexMode(chart, e, true); + return indexMode(chart, e, {intersect: true}); }, /** @@ -227,7 +241,9 @@ module.exports = { */ nearest: function(chart, e, options) { var position = getRelativePosition(e, chart); - var nearestItems = getNearestItems(chart, position, options.intersect); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); // We have multiple items at the same distance from the event. Now sort by smallest if (nearestItems.length > 1) { diff --git a/test/specs/core.interaction.tests.js b/test/specs/core.interaction.tests.js index 377fb401b..49cd3bd19 100644 --- a/test/specs/core.interaction.tests.js +++ b/test/specs/core.interaction.tests.js @@ -3,8 +3,8 @@ // Test the rectangle element describe('Core.Interaction', function() { describe('point mode', function() { - it ('should return all items under the point', function() { - var chart = window.acquireChart({ + beforeEach(function() { + this.chart = window.acquireChart({ type: 'line', data: { datasets: [{ @@ -21,22 +21,20 @@ describe('Core.Interaction', function() { labels: ['Point 1', 'Point 2', 'Point 3'] } }); + }); - // Trigger an event over top of the + it ('should return all items under the point', function() { + var chart = this.chart; var meta0 = chart.getDatasetMeta(0); var meta1 = chart.getDatasetMeta(1); var point = meta0.data[1]; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + point._model.x, - clientY: rect.top + point._model.y, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: point._model.x, + y: point._model.y, }; var elements = Chart.Interaction.modes.point(chart, evt); @@ -44,33 +42,13 @@ describe('Core.Interaction', function() { }); it ('should return an empty array when no items are found', 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)' - }, { - label: 'Dataset 2', - data: [40, 20, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } - }); - - // Trigger an event at (0, 0) - var node = chart.canvas; + var chart = this.chart; var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: 0, - clientY: 0, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 }; var elements = Chart.Interaction.modes.point(chart, evt); @@ -79,489 +57,505 @@ describe('Core.Interaction', function() { }); describe('index mode', function() { - it ('should return all items at the same index', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + describe('intersect: true', function() { + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'Dataset 1', + data: [10, 20, 30], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] + } + }); }); - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); - var meta1 = chart.getDatasetMeta(1); - var point = meta0.data[1]; + it ('gets correct items', function() { + var chart = this.chart; + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + var point = meta0.data[1]; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: point._model.x, + y: point._model.y, + }; + + var elements = Chart.Interaction.modes.index(chart, evt, {intersect: true}); + expect(elements).toEqual([point, meta1.data[1]]); + }); - var node = chart.canvas; - var rect = node.getBoundingClientRect(); + it ('returns empty array when nothing found', function() { + var chart = this.chart; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0, + }; + + var elements = Chart.Interaction.modes.index(chart, evt, {intersect: true}); + expect(elements).toEqual([]); + }); + }); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + point._model.x, - clientY: rect.top + point._model.y, - currentTarget: node + describe ('intersect: false', function() { + var data = { + datasets: [{ + label: 'Dataset 1', + data: [10, 20, 30], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] }; - var elements = Chart.Interaction.modes.index(chart, evt, {intersect: true}); - expect(elements).toEqual([point, meta1.data[1]]); - }); - - it ('should return all items at the same index when intersect is false', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: data + }); }); - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); - var meta1 = chart.getDatasetMeta(1); - - var node = chart.canvas; - var rect = node.getBoundingClientRect(); + it ('axis: x gets correct items', function() { + var chart = this.chart; + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; + + var elements = Chart.Interaction.modes.index(chart, evt, {intersect: false}); + expect(elements).toEqual([meta0.data[0], meta1.data[0]]); + }); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left, - clientY: rect.top, - currentTarget: node - }; + it ('axis: y gets correct items', function() { + var chart = window.acquireChart({ + type: 'horizontalBar', + data: data + }); + + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + var center = meta0.data[0].getCenterPoint(); + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: center.x, + y: center.y + 30, + }; + + var elements = Chart.Interaction.modes.index(chart, evt, {axis: 'y', intersect: false}); + expect(elements).toEqual([meta0.data[0], meta1.data[0]]); + }); - var elements = Chart.Interaction.modes.index(chart, evt, {intersect: false}); - expect(elements).toEqual([meta0.data[0], meta1.data[0]]); + it ('axis: xy gets correct items', function() { + var chart = this.chart; + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; + + var elements = Chart.Interaction.modes.index(chart, evt, {axis: 'xy', intersect: false}); + expect(elements).toEqual([meta0.data[0], meta1.data[0]]); + }); }); }); describe('dataset mode', function() { - it ('should return all items in the dataset of the first item found', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + describe('intersect: true', function() { + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'Dataset 1', + data: [10, 20, 30], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] + } + }); }); - // Trigger an event over top of the - var meta = chart.getDatasetMeta(0); - var point = meta.data[1]; + it ('should return all items in the dataset of the first item found', function() { + var chart = this.chart; + var meta = chart.getDatasetMeta(0); + var point = meta.data[1]; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: point._model.x, + y: point._model.y + }; + + var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: true}); + expect(elements).toEqual(meta.data); + }); - var node = chart.canvas; - var rect = node.getBoundingClientRect(); + it ('should return an empty array if nothing found', function() { + var chart = this.chart; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; + + var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: true}); + expect(elements).toEqual([]); + }); + }); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + point._model.x, - clientY: rect.top + point._model.y, - currentTarget: node + describe('intersect: false', function() { + var data = { + datasets: [{ + label: 'Dataset 1', + data: [10, 20, 30], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] }; - var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: true}); - expect(elements).toEqual(meta.data); - }); - - it ('should return all items in the dataset of the first item found when intersect is false', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: data + }); }); - var node = chart.canvas; - var rect = node.getBoundingClientRect(); + it ('axis: x gets correct items', function() { + var chart = window.acquireChart({ + type: 'horizontalBar', + data: data + }); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left, - clientY: rect.top, - currentTarget: node - }; - - var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: false}); + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; - var meta = chart.getDatasetMeta(1); - expect(elements).toEqual(meta.data); - }); - }); + var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'x', intersect: false}); - describe('nearest mode', function() { - it ('should return the nearest item', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + var meta = chart.getDatasetMeta(0); + expect(elements).toEqual(meta.data); }); - // Trigger an event over top of the - var meta = chart.getDatasetMeta(1); - var node = chart.canvas; - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: 0, - clientY: 0, - currentTarget: node - }; + it ('axis: y gets correct items', function() { + var chart = this.chart; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); - expect(elements).toEqual([meta.data[0]]); - }); + var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'y', intersect: false}); - it ('should return the smallest item if more than 1 are at the same distance', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 5, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + var meta = chart.getDatasetMeta(1); + expect(elements).toEqual(meta.data); }); - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); - var meta1 = chart.getDatasetMeta(1); + it ('axis: xy gets correct items', function() { + var chart = this.chart; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; - // Halfway between 2 mid points - var pt = { - x: meta0.data[1]._view.x, - y: (meta0.data[1]._view.y + meta1.data[1]._view.y) / 2 - }; - - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node - }; + var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: false}); - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); - expect(elements).toEqual([meta0.data[1]]); - }); - - it ('should return the lowest dataset index if size and area are the same', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 10, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + var meta = chart.getDatasetMeta(1); + expect(elements).toEqual(meta.data); }); - - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); - var meta1 = chart.getDatasetMeta(1); - - // Halfway between 2 mid points - var pt = { - x: meta0.data[1]._view.x, - y: (meta0.data[1]._view.y + meta1.data[1]._view.y) / 2 - }; - - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node - }; - - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); - expect(elements).toEqual([meta0.data[1]]); }); }); - describe('nearest intersect mode', function() { - it ('should return the nearest item', 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)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + describe('nearest mode', function() { + describe('intersect: false', function() { + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'Dataset 1', + data: [10, 40, 30], + pointRadius: [5, 5, 5], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointRadius: [10, 10, 10], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] + } + }); }); - // Trigger an event over top of the - var meta = chart.getDatasetMeta(1); - var point = meta.data[1]; - - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + point._view.x + 15, - clientY: rect.top + point._view.y, - currentTarget: node - }; + it ('axis: xy should return the nearest item', function() { + var chart = this.chart; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: 0, + y: 0 + }; + + // Nearest to 0,0 (top left) will be first point of dataset 2 + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); + var meta = chart.getDatasetMeta(1); + expect(elements).toEqual([meta.data[0]]); + }); - // Nothing intersects so find nothing - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); - expect(elements).toEqual([]); + it ('should return the smallest item if more than 1 are at the same distance', function() { + var chart = this.chart; + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + + // Halfway between 2 mid points + var pt = { + x: meta0.data[1]._view.x, + y: (meta0.data[1]._view.y + meta1.data[1]._view.y) / 2 + }; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y + }; + + // Nearest to 0,0 (top left) will be first point of dataset 2 + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); + expect(elements).toEqual([meta0.data[1]]); + }); - evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + point._view.x, - clientY: rect.top + point._view.y, - currentTarget: node - }; - elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); - expect(elements).toEqual([point]); + it ('should return the lowest dataset index if size and area are the same', function() { + var chart = this.chart; + // Make equal sized points at index: 1 + chart.data.datasets[0].pointRadius[1] = 10; + chart.update(); + + // Trigger an event over top of the + var meta0 = chart.getDatasetMeta(0); + var meta1 = chart.getDatasetMeta(1); + + // Halfway between 2 mid points + var pt = { + x: meta0.data[1]._view.x, + y: (meta0.data[1]._view.y + meta1.data[1]._view.y) / 2 + }; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y + }; + + // Nearest to 0,0 (top left) will be first point of dataset 2 + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: false}); + expect(elements).toEqual([meta0.data[1]]); + }); }); - it ('should return the nearest item even if 2 intersect', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 39, 30], - pointRadius: [5, 30, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } + describe('intersect: true', function() { + beforeEach(function() { + this.chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + label: 'Dataset 1', + data: [10, 20, 30], + pointHoverBorderColor: 'rgb(255, 0, 0)', + pointHoverBackgroundColor: 'rgb(0, 255, 0)' + }, { + label: 'Dataset 2', + data: [40, 40, 40], + pointHoverBorderColor: 'rgb(0, 0, 255)', + pointHoverBackgroundColor: 'rgb(0, 255, 255)' + }], + labels: ['Point 1', 'Point 2', 'Point 3'] + } + }); }); - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); + it ('should return the nearest item', function() { + var chart = this.chart; + var meta = chart.getDatasetMeta(1); + var point = meta.data[1]; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: point._view.x + 15, + y: point._view.y + }; + + // Nothing intersects so find nothing + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); + expect(elements).toEqual([]); + + evt = { + type: 'click', + chart: chart, + native: true, + x: point._view.x, + y: point._view.y + }; + elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); + expect(elements).toEqual([point]); + }); - // Halfway between 2 mid points - var pt = { - x: meta0.data[1]._view.x, - y: meta0.data[1]._view.y - }; + it ('should return the nearest item even if 2 intersect', function() { + var chart = this.chart; + chart.data.datasets[0].pointRadius = [5, 30, 5]; + chart.data.datasets[0].data[1] = 39; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node - }; + chart.data.datasets[1].pointRadius = [10, 10, 10]; - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); - expect(elements).toEqual([meta0.data[1]]); - }); + // Trigger an event over top of the + var meta0 = chart.getDatasetMeta(0); - it ('should return the smallest item if more than 1 are at the same distance', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 5, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } - }); + // Halfway between 2 mid points + var pt = { + x: meta0.data[1]._view.x, + y: meta0.data[1]._view.y + }; - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y + }; - // Halfway between 2 mid points - var pt = { - x: meta0.data[1]._view.x, - y: meta0.data[1]._view.y - }; + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); + expect(elements).toEqual([meta0.data[1]]); + }); - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node - }; + it ('should return the smallest item if more than 1 are at the same distance', function() { + var chart = this.chart; + chart.data.datasets[0].pointRadius = [5, 5, 5]; + chart.data.datasets[0].data[1] = 40; - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); - expect(elements).toEqual([meta0.data[1]]); - }); + chart.data.datasets[1].pointRadius = [10, 10, 10]; - it ('should return the item at the lowest dataset index if distance and area are the same', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 10, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } - }); + // Trigger an event over top of the + var meta0 = chart.getDatasetMeta(0); - // Trigger an event over top of the - var meta0 = chart.getDatasetMeta(0); + // Halfway between 2 mid points + var pt = { + x: meta0.data[1]._view.x, + y: meta0.data[1]._view.y + }; - // Halfway between 2 mid points - var pt = { - x: meta0.data[1]._view.x, - y: meta0.data[1]._view.y - }; + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y + }; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); - var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node - }; + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); + expect(elements).toEqual([meta0.data[1]]); + }); - // Nearest to 0,0 (top left) will be first point of dataset 2 - var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); - expect(elements).toEqual([meta0.data[1]]); + it ('should return the item at the lowest dataset index if distance and area are the same', function() { + var chart = this.chart; + chart.data.datasets[0].pointRadius = [5, 10, 5]; + chart.data.datasets[0].data[1] = 40; + + chart.data.datasets[1].pointRadius = [10, 10, 10]; + + // Trigger an event over top of the + var meta0 = chart.getDatasetMeta(0); + + // Halfway between 2 mid points + var pt = { + x: meta0.data[1]._view.x, + y: meta0.data[1]._view.y + }; + + var evt = { + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y + }; + + // Nearest to 0,0 (top left) will be first point of dataset 2 + var elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}); + expect(elements).toEqual([meta0.data[1]]); + }); }); }); describe('x mode', function() { - it('should return items at the same x value when intersect is false', function() { - var chart = window.acquireChart({ + beforeEach(function() { + this.chart = window.acquireChart({ type: 'line', data: { datasets: [{ @@ -580,8 +574,10 @@ describe('Core.Interaction', function() { labels: ['Point 1', 'Point 2', 'Point 3'] } }); + }); - // Trigger an event over top of the + it('should return items at the same x value when intersect is false', function() { + var chart = this.chart; var meta0 = chart.getDatasetMeta(0); var meta1 = chart.getDatasetMeta(1); @@ -591,27 +587,23 @@ describe('Core.Interaction', function() { y: meta0.data[1]._view.y }; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: 0 }; var elements = Chart.Interaction.modes.x(chart, evt, {intersect: false}); expect(elements).toEqual([meta0.data[1], meta1.data[1]]); evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x + 20, // out of range - clientY: rect.top, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x + 20, + y: 0 }; elements = Chart.Interaction.modes.x(chart, evt, {intersect: false}); @@ -619,27 +611,7 @@ describe('Core.Interaction', function() { }); it('should return items at the same x value when intersect is true', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 10, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } - }); - - // Trigger an event over top of the + var chart = this.chart; var meta0 = chart.getDatasetMeta(0); var meta1 = chart.getDatasetMeta(1); @@ -649,27 +621,23 @@ describe('Core.Interaction', function() { y: meta0.data[1]._view.y }; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: 0 }; var elements = Chart.Interaction.modes.x(chart, evt, {intersect: true}); expect(elements).toEqual([]); // we don't intersect anything evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node + type: 'click', + chart: chart, + native: true, // needed otherwise things its a DOM event + x: pt.x, + y: pt.y }; elements = Chart.Interaction.modes.x(chart, evt, {intersect: true}); @@ -678,8 +646,8 @@ describe('Core.Interaction', function() { }); describe('y mode', function() { - it('should return items at the same y value when intersect is false', function() { - var chart = window.acquireChart({ + beforeEach(function() { + this.chart = window.acquireChart({ type: 'line', data: { datasets: [{ @@ -698,8 +666,10 @@ describe('Core.Interaction', function() { labels: ['Point 1', 'Point 2', 'Point 3'] } }); + }); - // Trigger an event over top of the + it('should return items at the same y value when intersect is false', function() { + var chart = this.chart; var meta0 = chart.getDatasetMeta(0); var meta1 = chart.getDatasetMeta(1); @@ -709,27 +679,23 @@ describe('Core.Interaction', function() { y: meta0.data[1]._view.y }; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left, - clientY: rect.top + pt.y, - currentTarget: node + type: 'click', + chart: chart, + native: true, + x: 0, + y: pt.y, }; var elements = Chart.Interaction.modes.y(chart, evt, {intersect: false}); expect(elements).toEqual([meta0.data[1], meta1.data[0], meta1.data[1], meta1.data[2]]); evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y + 20, // out of range - currentTarget: node + type: 'click', + chart: chart, + native: true, + x: pt.x, + y: pt.y + 20, // out of range }; elements = Chart.Interaction.modes.y(chart, evt, {intersect: false}); @@ -737,27 +703,7 @@ describe('Core.Interaction', function() { }); it('should return items at the same y value when intersect is true', function() { - var chart = window.acquireChart({ - type: 'line', - data: { - datasets: [{ - label: 'Dataset 1', - data: [10, 40, 30], - pointRadius: [5, 10, 5], - pointHoverBorderColor: 'rgb(255, 0, 0)', - pointHoverBackgroundColor: 'rgb(0, 255, 0)' - }, { - label: 'Dataset 2', - data: [40, 40, 40], - pointRadius: [10, 10, 10], - pointHoverBorderColor: 'rgb(0, 0, 255)', - pointHoverBackgroundColor: 'rgb(0, 255, 255)' - }], - labels: ['Point 1', 'Point 2', 'Point 3'] - } - }); - - // Trigger an event over top of the + var chart = this.chart; var meta0 = chart.getDatasetMeta(0); var meta1 = chart.getDatasetMeta(1); @@ -767,27 +713,23 @@ describe('Core.Interaction', function() { y: meta0.data[1]._view.y }; - var node = chart.canvas; - var rect = node.getBoundingClientRect(); var evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left, - clientY: rect.top + pt.y, - currentTarget: node + type: 'click', + chart: chart, + native: true, + x: 0, + y: pt.y }; var elements = Chart.Interaction.modes.y(chart, evt, {intersect: true}); expect(elements).toEqual([]); // we don't intersect anything evt = { - view: window, - bubbles: true, - cancelable: true, - clientX: rect.left + pt.x, - clientY: rect.top + pt.y, - currentTarget: node + type: 'click', + chart: chart, + native: true, + x: pt.x, + y: pt.y, }; elements = Chart.Interaction.modes.y(chart, evt, {intersect: true}); diff --git a/test/specs/core.tooltip.tests.js b/test/specs/core.tooltip.tests.js index 0fbc46799..b1896da33 100755 --- a/test/specs/core.tooltip.tests.js +++ b/test/specs/core.tooltip.tests.js @@ -46,6 +46,10 @@ describe('Core.Tooltip', function() { }, options: { tooltips: { + mode: 'index', + intersect: false, + }, + hover: { mode: 'index', intersect: false }