diff --git a/docs/01-Chart-Configuration.md b/docs/01-Chart-Configuration.md index c12a102af9e..bf9eb5baa61 100644 --- a/docs/01-Chart-Configuration.md +++ b/docs/01-Chart-Configuration.md @@ -271,6 +271,7 @@ afterBody | `Array[tooltipItem], data` | Text to render after the body section beforeFooter | `Array[tooltipItem], data` | Text to render before the footer section footer | `Array[tooltipItem], data` | Text to render as the footer afterFooter | `Array[tooltipItem], data` | Text to render after the footer section +dataPoints | `Array[tooltipItem]` | List of matching point informations. #### Tooltip Item Interface @@ -288,7 +289,13 @@ The tooltip items passed to the tooltip callbacks implement the following interf datasetIndex: Number, // Index of this data item in the dataset - index: Number + index: Number, + + // X position of matching point + x: Number, + + // Y position of matching point + y: Number, } ``` diff --git a/docs/09-Advanced.md b/docs/09-Advanced.md index 5ea957179a6..1f5a1932a42 100644 --- a/docs/09-Advanced.md +++ b/docs/09-Advanced.md @@ -224,12 +224,12 @@ Scale instances are given the following properties during the fitting process. { left: Number, // left edge of the scale bounding box right: Number, // right edge of the bounding box' - top: Number, + top: Number, bottom: Number, width: Number, // the same as right - left height: Number, // the same as bottom - top - // Margin on each side. Like css, this is outside the bounding box. + // Margin on each side. Like css, this is outside the bounding box. margins: { left: Number, right: Number, @@ -246,7 +246,7 @@ Scale instances are given the following properties during the fitting process. ``` #### Scale Interface -To work with Chart.js, custom scale types must implement the following interface. +To work with Chart.js, custom scale types must implement the following interface. ```javascript { @@ -281,10 +281,10 @@ To work with Chart.js, custom scale types must implement the following interface Optionally, the following methods may also be overwritten, but an implementation is already provided by the `Chart.Scale` base class. ```javascript - // Transform the ticks array of the scale instance into strings. The default implementation simply calls this.options.ticks.callback(numericalTick, index, ticks); + // Transform the ticks array of the scale instance into strings. The default implementation simply calls this.options.ticks.callback(numericalTick, index, ticks); convertTicksToLabels: function() {}, - // Determine how much the labels will rotate by. The default implementation will only rotate labels if the scale is horizontal. + // Determine how much the labels will rotate by. The default implementation will only rotate labels if the scale is horizontal. calculateTickRotation: function() {}, // Fits the scale into the canvas. @@ -301,7 +301,7 @@ Optionally, the following methods may also be overwritten, but an implementation The Core.Scale base class also has some utility functions that you may find useful. ```javascript -{ +{ // Returns true if the scale instance is horizontal isHorizontal: function() {}, @@ -371,7 +371,7 @@ The following methods may optionally be overridden by derived dataset controller // chart types using a single scale linkScales: function() {}, - // Called by the main chart controller when an update is triggered. The default implementation handles the number of data points changing and creating elements appropriately. + // Called by the main chart controller when an update is triggered. The default implementation handles the number of data points changing and creating elements appropriately. buildOrUpdateElements: function() {} } ``` @@ -440,7 +440,7 @@ Plugins should derive from Chart.PluginBase and implement the following interfac ### Building Chart.js -Chart.js uses gulp to build the library into a single JavaScript file. +Chart.js uses gulp to build the library into a single JavaScript file. Firstly, we need to ensure development dependencies are installed. With node and npm installed, after cloning the Chart.js repo to a local directory, and navigating to that directory in the command line, we can run the following: diff --git a/samples/dataPoints-customTooltips.html b/samples/dataPoints-customTooltips.html new file mode 100644 index 00000000000..81441686e7a --- /dev/null +++ b/samples/dataPoints-customTooltips.html @@ -0,0 +1,104 @@ + + + + + Custom Tooltips using Data Points + + + + + + +
+ +
+
+
+ + + + diff --git a/src/core/core.tooltip.js b/src/core/core.tooltip.js index 1e02fee7780..4a976439191 100755 --- a/src/core/core.tooltip.js +++ b/src/core/core.tooltip.js @@ -118,7 +118,9 @@ module.exports = function(Chart) { xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', index: index, - datasetIndex: datasetIndex + datasetIndex: datasetIndex, + x: element._model.x, + y: element._model.y }; } @@ -504,6 +506,9 @@ module.exports = function(Chart) { model.caretPadding = helpers.getValueOrDefault(tooltipPosition.padding, 2); model.labelColors = labelColors; + // data points + model.dataPoints = tooltipItems; + // We need to determine alignment of the tooltip tooltipSize = getTooltipSize(this, model); alignment = determineAlignment(this, tooltipSize); diff --git a/test/core.tooltip.tests.js b/test/core.tooltip.tests.js index 29c6b721a49..5890e79e036 100755 --- a/test/core.tooltip.tests.js +++ b/test/core.tooltip.tests.js @@ -542,4 +542,64 @@ describe('Core.Tooltip', function() { expect(tooltip._view.x).toBeCloseToPixel(269); expect(tooltip._view.y).toBeCloseToPixel(155); }); + + it('Should have dataPoints', function() { + var chartInstance = 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'] + }, + options: { + tooltips: { + mode: 'single' + } + } + }); + + // Trigger an event over top of the + var pointIndex = 1; + var datasetIndex = 0; + var meta = chartInstance.getDatasetMeta(datasetIndex); + var point = meta.data[pointIndex]; + var node = chartInstance.chart.canvas; + var rect = node.getBoundingClientRect(); + var evt = new MouseEvent('mousemove', { + view: window, + bubbles: true, + cancelable: true, + clientX: rect.left + point._model.x, + clientY: rect.top + point._model.y + }); + + // Manully trigger rather than having an async test + node.dispatchEvent(evt); + + // Check and see if tooltip was displayed + var tooltip = chartInstance.tooltip; + + expect(tooltip._view instanceof Object).toBe(true); + expect(tooltip._view.dataPoints instanceof Array).toBe(true); + expect(tooltip._view.dataPoints.length).toEqual(1); + expect(tooltip._view.dataPoints[0].index).toEqual(pointIndex); + expect(tooltip._view.dataPoints[0].datasetIndex).toEqual(datasetIndex); + expect(tooltip._view.dataPoints[0].xLabel).toEqual( + chartInstance.config.data.labels[pointIndex] + ); + expect(tooltip._view.dataPoints[0].yLabel).toEqual( + chartInstance.config.data.datasets[datasetIndex].data[pointIndex] + ); + expect(tooltip._view.dataPoints[0].x).toBeCloseToPixel(point._model.x); + expect(tooltip._view.dataPoints[0].y).toBeCloseToPixel(point._model.y); + }); });