mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Refactoring to put browser specific code in a new class (#3718)
Refactoring to put browser specific code in a new class, BrowserPlatform. BrowserPlatform implements IPlatform. Chart.Platform is the constructor for the platform object that is attached to the chart instance. Plugins are notified about the event using the `onEvent` call. The legend plugin was converted to use onEvent instead of the older private `handleEvent` method. Wrote test to check that plugins are notified about events
This commit is contained in:
parent
5387c48bd8
commit
ecc35c527b
@ -410,6 +410,7 @@ Plugins will be called at the following times
|
||||
* After datasets draw
|
||||
* Resize
|
||||
* Before an animation is started
|
||||
* When an event occurs on the canvas (mousemove, click, etc). This requires the `options.events` property handled
|
||||
|
||||
Plugins should derive from Chart.PluginBase and implement the following interface
|
||||
```javascript
|
||||
@ -437,6 +438,13 @@ Plugins should derive from Chart.PluginBase and implement the following interfac
|
||||
afterDatasetsDraw: function(chartInstance, easing) { },
|
||||
|
||||
destroy: function(chartInstance) { }
|
||||
|
||||
/**
|
||||
* Called when an event occurs on the chart
|
||||
* @param e {Core.Event} the Chart.js wrapper around the native event. e.native is the original event
|
||||
* @return {Boolean} true if the chart is changed and needs to re-render
|
||||
*/
|
||||
onEvent: function(chartInstance, e) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -19,6 +19,9 @@ require('./core/core.legend')(Chart);
|
||||
require('./core/core.interaction')(Chart);
|
||||
require('./core/core.tooltip')(Chart);
|
||||
|
||||
// By default, we only load the browser platform.
|
||||
Chart.platform = require('./platforms/platform.dom')(Chart);
|
||||
|
||||
require('./elements/element.arc')(Chart);
|
||||
require('./elements/element.line')(Chart);
|
||||
require('./elements/element.point')(Chart);
|
||||
|
||||
@ -14,140 +14,6 @@ module.exports = function(Chart) {
|
||||
// Controllers available for dataset visualization eg. bar, line, slice, etc.
|
||||
Chart.controllers = {};
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* TODO(SB) Move this method in the upcoming core.platform class.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {Number} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
function readUsedSize(element, property) {
|
||||
var value = helpers.getStyle(element, property);
|
||||
var matches = value && value.match(/(\d+)px/);
|
||||
return matches? Number(matches[1]) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||||
* to determine the aspect ratio to apply in case no explicit height has been specified.
|
||||
* TODO(SB) Move this method in the upcoming core.platform class.
|
||||
*/
|
||||
function initCanvas(canvas, config) {
|
||||
var style = canvas.style;
|
||||
|
||||
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
|
||||
// returns null or '' if no explicit value has been set to the canvas attribute.
|
||||
var renderHeight = canvas.getAttribute('height');
|
||||
var renderWidth = canvas.getAttribute('width');
|
||||
|
||||
// Chart.js modifies some canvas values that we want to restore on destroy
|
||||
canvas._chartjs = {
|
||||
initial: {
|
||||
height: renderHeight,
|
||||
width: renderWidth,
|
||||
style: {
|
||||
display: style.display,
|
||||
height: style.height,
|
||||
width: style.width
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Force canvas to display as block to avoid extra space caused by inline
|
||||
// elements, which would interfere with the responsive resize process.
|
||||
// https://github.com/chartjs/Chart.js/issues/2538
|
||||
style.display = style.display || 'block';
|
||||
|
||||
if (renderWidth === null || renderWidth === '') {
|
||||
var displayWidth = readUsedSize(canvas, 'width');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.width = displayWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if (renderHeight === null || renderHeight === '') {
|
||||
if (canvas.style.height === '') {
|
||||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||||
// which one can be specified by the user but also by charts as default option
|
||||
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
|
||||
canvas.height = canvas.width / (config.options.aspectRatio || 2);
|
||||
} else {
|
||||
var displayHeight = readUsedSize(canvas, 'height');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the canvas initial state, such as render/display sizes and style.
|
||||
* TODO(SB) Move this method in the upcoming core.platform class.
|
||||
*/
|
||||
function releaseCanvas(canvas) {
|
||||
if (!canvas._chartjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var initial = canvas._chartjs.initial;
|
||||
['height', 'width'].forEach(function(prop) {
|
||||
var value = initial[prop];
|
||||
if (value === undefined || value === null) {
|
||||
canvas.removeAttribute(prop);
|
||||
} else {
|
||||
canvas.setAttribute(prop, value);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(initial.style || {}, function(value, key) {
|
||||
canvas.style[key] = value;
|
||||
});
|
||||
|
||||
// The canvas render size might have been changed (and thus the state stack discarded),
|
||||
// we can't use save() and restore() to restore the initial state. So make sure that at
|
||||
// least the canvas context is reset to the default state by setting the canvas width.
|
||||
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
|
||||
canvas.width = canvas.width;
|
||||
|
||||
delete canvas._chartjs;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(SB) Move this method in the upcoming core.platform class.
|
||||
*/
|
||||
function acquireContext(item, config) {
|
||||
if (typeof item === 'string') {
|
||||
item = document.getElementById(item);
|
||||
} else if (item.length) {
|
||||
// Support for array based queries (such as jQuery)
|
||||
item = item[0];
|
||||
}
|
||||
|
||||
if (item && item.canvas) {
|
||||
// Support for any object associated to a canvas (including a context2d)
|
||||
item = item.canvas;
|
||||
}
|
||||
|
||||
if (item instanceof HTMLCanvasElement) {
|
||||
// To prevent canvas fingerprinting, some add-ons undefine the getContext
|
||||
// method, for example: https://github.com/kkapsner/CanvasBlocker
|
||||
// https://github.com/chartjs/Chart.js/issues/2807
|
||||
var context = item.getContext && item.getContext('2d');
|
||||
if (context instanceof CanvasRenderingContext2D) {
|
||||
initCanvas(item, config);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given config with global and chart default values.
|
||||
*/
|
||||
@ -197,7 +63,7 @@ module.exports = function(Chart) {
|
||||
|
||||
config = initConfig(config);
|
||||
|
||||
var context = acquireContext(item, config);
|
||||
var context = Chart.platform.acquireContext(item, config);
|
||||
var canvas = context && context.canvas;
|
||||
var height = canvas && canvas.height;
|
||||
var width = canvas && canvas.width;
|
||||
@ -696,7 +562,7 @@ module.exports = function(Chart) {
|
||||
helpers.unbindEvents(me, me.events);
|
||||
helpers.removeResizeListener(canvas.parentNode);
|
||||
helpers.clear(me.chart);
|
||||
releaseCanvas(canvas);
|
||||
Chart.platform.releaseContext(me.chart.ctx);
|
||||
me.chart.canvas = null;
|
||||
me.chart.ctx = null;
|
||||
}
|
||||
@ -742,7 +608,6 @@ module.exports = function(Chart) {
|
||||
|
||||
eventHandler: function(e) {
|
||||
var me = this;
|
||||
var legend = me.legend;
|
||||
var tooltip = me.tooltip;
|
||||
var hoverOptions = me.options.hover;
|
||||
|
||||
@ -750,9 +615,12 @@ module.exports = function(Chart) {
|
||||
me._bufferedRender = true;
|
||||
me._bufferedRequest = null;
|
||||
|
||||
var changed = me.handleEvent(e);
|
||||
changed |= legend && legend.handleEvent(e);
|
||||
changed |= tooltip && tooltip.handleEvent(e);
|
||||
// Create platform agnostic chart event using platform specific code
|
||||
var chartEvent = Chart.platform.createEvent(e, me.chart);
|
||||
|
||||
var changed = me.handleEvent(chartEvent);
|
||||
changed |= tooltip && tooltip.handleEvent(chartEvent);
|
||||
changed |= Chart.plugins.notify(me, 'onEvent', [chartEvent]);
|
||||
|
||||
var bufferedRequest = me._bufferedRequest;
|
||||
if (bufferedRequest) {
|
||||
@ -776,7 +644,7 @@ module.exports = function(Chart) {
|
||||
/**
|
||||
* Handle an event
|
||||
* @private
|
||||
* param e {Event} the event to handle
|
||||
* param e {Core.Event} the event to handle
|
||||
* @return {Boolean} true if the chart needs to re-render
|
||||
*/
|
||||
handleEvent: function(e) {
|
||||
@ -796,12 +664,14 @@ module.exports = function(Chart) {
|
||||
|
||||
// On Hover hook
|
||||
if (hoverOptions.onHover) {
|
||||
hoverOptions.onHover.call(me, e, me.active);
|
||||
// Need to call with native event here to not break backwards compatibility
|
||||
hoverOptions.onHover.call(me, e.native, me.active);
|
||||
}
|
||||
|
||||
if (e.type === 'mouseup' || e.type === 'click') {
|
||||
if (options.onClick) {
|
||||
options.onClick.call(me, e, me.active);
|
||||
// Use e.native here for backwards compatibility
|
||||
options.onClick.call(me, e.native, me.active);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,23 @@
|
||||
module.exports = function(Chart) {
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
/**
|
||||
* Helper function to get relative position for an event
|
||||
* @param e {Event|Core.Event} the event to get the position for
|
||||
* @param chart {chart} the chart
|
||||
* @returns {Point} the event position
|
||||
*/
|
||||
function getRelativePosition(e, chart) {
|
||||
if (e.native) {
|
||||
return {
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
}
|
||||
|
||||
return helpers.getRelativePosition(e, chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to traverse all of the visible elements in the chart
|
||||
* @param chart {chart} the chart
|
||||
@ -82,7 +99,7 @@ module.exports = function(Chart) {
|
||||
}
|
||||
|
||||
function indexMode(chart, e, options) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var distanceMetric = function(pt1, pt2) {
|
||||
return Math.abs(pt1.x - pt2.x);
|
||||
};
|
||||
@ -125,7 +142,7 @@ module.exports = function(Chart) {
|
||||
// Helper function for different modes
|
||||
modes: {
|
||||
single: function(chart, e) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var elements = [];
|
||||
|
||||
parseVisibleItems(chart, function(element) {
|
||||
@ -166,7 +183,7 @@ module.exports = function(Chart) {
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
dataset: function(chart, e, options) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
|
||||
|
||||
if (items.length > 0) {
|
||||
@ -193,7 +210,7 @@ module.exports = function(Chart) {
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
point: function(chart, e) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
return getIntersectItems(chart, position);
|
||||
},
|
||||
|
||||
@ -206,7 +223,7 @@ module.exports = function(Chart) {
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
nearest: function(chart, e, options) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var nearestItems = getNearestItems(chart, position, options.intersect);
|
||||
|
||||
// We have multiple items at the same distance from the event. Now sort by smallest
|
||||
@ -238,7 +255,7 @@ module.exports = function(Chart) {
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
x: function(chart, e, options) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
@ -269,7 +286,7 @@ module.exports = function(Chart) {
|
||||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||||
*/
|
||||
y: function(chart, e, options) {
|
||||
var position = helpers.getRelativePosition(e, chart.chart);
|
||||
var position = getRelativePosition(e, chart.chart);
|
||||
var items = [];
|
||||
var intersectsItem = false;
|
||||
|
||||
|
||||
@ -439,7 +439,7 @@ module.exports = function(Chart) {
|
||||
/**
|
||||
* Handle an event
|
||||
* @private
|
||||
* @param e {Event} the event to handle
|
||||
* @param e {Core.Event} the event to handle
|
||||
* @return {Boolean} true if a change occured
|
||||
*/
|
||||
handleEvent: function(e) {
|
||||
@ -460,9 +460,9 @@ module.exports = function(Chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
var position = helpers.getRelativePosition(e, me.chart.chart),
|
||||
x = position.x,
|
||||
y = position.y;
|
||||
// Chart event already has relative position in it
|
||||
var x = e.x,
|
||||
y = e.y;
|
||||
|
||||
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
|
||||
// See if we are touching one of the dataset boxes
|
||||
@ -473,11 +473,13 @@ module.exports = function(Chart) {
|
||||
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
|
||||
// Touching an element
|
||||
if (type === 'click') {
|
||||
opts.onClick.call(me, e, me.legendItems[i]);
|
||||
// use e.native for backwards compatibility
|
||||
opts.onClick.call(me, e.native, me.legendItems[i]);
|
||||
changed = true;
|
||||
break;
|
||||
} else if (type === 'mousemove') {
|
||||
opts.onHover.call(me, e, me.legendItems[i]);
|
||||
// use e.native for backwards compatibility
|
||||
opts.onHover.call(me, e.native, me.legendItems[i]);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
@ -523,6 +525,12 @@ module.exports = function(Chart) {
|
||||
Chart.layoutService.removeBox(chartInstance, chartInstance.legend);
|
||||
delete chartInstance.legend;
|
||||
}
|
||||
},
|
||||
onEvent: function(chartInstance, e) {
|
||||
var legend = chartInstance.legend;
|
||||
if (legend) {
|
||||
legend.handleEvent(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -763,7 +763,7 @@ module.exports = function(Chart) {
|
||||
/**
|
||||
* Handle an event
|
||||
* @private
|
||||
* @param e {Event} the event to handle
|
||||
* @param e {Core.Event} the event to handle
|
||||
* @returns {Boolean} true if the tooltip changed
|
||||
*/
|
||||
handleEvent: function(e) {
|
||||
@ -785,7 +785,10 @@ module.exports = function(Chart) {
|
||||
me._lastActive = me._active;
|
||||
|
||||
if (options.enabled || options.custom) {
|
||||
me._eventPosition = helpers.getRelativePosition(e, me._chart);
|
||||
me._eventPosition = {
|
||||
x: e.x,
|
||||
y: e.y
|
||||
};
|
||||
|
||||
var model = me._model;
|
||||
me.update(true);
|
||||
|
||||
237
src/platforms/platform.dom.js
Normal file
237
src/platforms/platform.dom.js
Normal file
@ -0,0 +1,237 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @interface IPlatform
|
||||
* Allows abstracting platform dependencies away from the chart
|
||||
*/
|
||||
/**
|
||||
* Creates a chart.js event from a platform specific event
|
||||
* @method IPlatform#createEvent
|
||||
* @param e {Event} : the platform event to translate
|
||||
* @returns {Core.Event} chart.js event
|
||||
*/
|
||||
/**
|
||||
* @method IPlatform#acquireContext
|
||||
* @param item {Object} the context or canvas to use
|
||||
* @param config {ChartOptions} the chart options
|
||||
* @returns {CanvasRenderingContext2D} a context2d instance implementing the w3c Canvas 2D context API standard.
|
||||
*/
|
||||
/**
|
||||
* @method IPlatform#releaseContext
|
||||
* @param context {CanvasRenderingContext2D} the context to release. This is the item returned by @see {@link IPlatform#acquireContext}
|
||||
*/
|
||||
|
||||
// Chart.Platform implementation for targeting a web browser
|
||||
module.exports = function(Chart) {
|
||||
var helpers = Chart.helpers;
|
||||
|
||||
/*
|
||||
* Key is the browser event type
|
||||
* Chart.js internal events are:
|
||||
* mouseenter
|
||||
* mousedown
|
||||
* mousemove
|
||||
* mouseup
|
||||
* mouseout
|
||||
* click
|
||||
* dblclick
|
||||
* contextmenu
|
||||
* keydown
|
||||
* keypress
|
||||
* keyup
|
||||
*/
|
||||
var typeMap = {
|
||||
// Mouse events
|
||||
mouseenter: 'mouseenter',
|
||||
mousedown: 'mousedown',
|
||||
mousemove: 'mousemove',
|
||||
mouseup: 'mouseup',
|
||||
mouseout: 'mouseout',
|
||||
mouseleave: 'mouseout',
|
||||
click: 'click',
|
||||
dblclick: 'dblclick',
|
||||
contextmenu: 'contextmenu',
|
||||
|
||||
// Touch events
|
||||
touchstart: 'mousedown',
|
||||
touchmove: 'mousemove',
|
||||
touchend: 'mouseup',
|
||||
|
||||
// Pointer events
|
||||
pointerenter: 'mouseenter',
|
||||
pointerdown: 'mousedown',
|
||||
pointermove: 'mousemove',
|
||||
pointerup: 'mouseup',
|
||||
pointerleave: 'mouseout',
|
||||
pointerout: 'mouseout',
|
||||
|
||||
// Key events
|
||||
keydown: 'keydown',
|
||||
keypress: 'keypress',
|
||||
keyup: 'keyup',
|
||||
};
|
||||
|
||||
/**
|
||||
* The "used" size is the final value of a dimension property after all calculations have
|
||||
* been performed. This method uses the computed style of `element` but returns undefined
|
||||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||||
* for example because of `display: none` on a parent node.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||||
* @returns {Number} Size in pixels or undefined if unknown.
|
||||
*/
|
||||
function readUsedSize(element, property) {
|
||||
var value = helpers.getStyle(element, property);
|
||||
var matches = value && value.match(/(\d+)px/);
|
||||
return matches? Number(matches[1]) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||||
* to determine the aspect ratio to apply in case no explicit height has been specified.
|
||||
*/
|
||||
function initCanvas(canvas, config) {
|
||||
var style = canvas.style;
|
||||
|
||||
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
|
||||
// returns null or '' if no explicit value has been set to the canvas attribute.
|
||||
var renderHeight = canvas.getAttribute('height');
|
||||
var renderWidth = canvas.getAttribute('width');
|
||||
|
||||
// Chart.js modifies some canvas values that we want to restore on destroy
|
||||
canvas._chartjs = {
|
||||
initial: {
|
||||
height: renderHeight,
|
||||
width: renderWidth,
|
||||
style: {
|
||||
display: style.display,
|
||||
height: style.height,
|
||||
width: style.width
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Force canvas to display as block to avoid extra space caused by inline
|
||||
// elements, which would interfere with the responsive resize process.
|
||||
// https://github.com/chartjs/Chart.js/issues/2538
|
||||
style.display = style.display || 'block';
|
||||
|
||||
if (renderWidth === null || renderWidth === '') {
|
||||
var displayWidth = readUsedSize(canvas, 'width');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.width = displayWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if (renderHeight === null || renderHeight === '') {
|
||||
if (canvas.style.height === '') {
|
||||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||||
// which one can be specified by the user but also by charts as default option
|
||||
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
|
||||
canvas.height = canvas.width / (config.options.aspectRatio || 2);
|
||||
} else {
|
||||
var displayHeight = readUsedSize(canvas, 'height');
|
||||
if (displayWidth !== undefined) {
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Creates a Chart.js event from a raw event
|
||||
* @method BrowserPlatform#createEvent
|
||||
* @implements IPlatform.createEvent
|
||||
* @param e {Event} the raw event (such as a mouse event)
|
||||
* @param chart {Chart} the chart to use
|
||||
* @returns {Core.Event} the chart.js event for this event
|
||||
*/
|
||||
createEvent: function(e, chart) {
|
||||
var relativePosition = helpers.getRelativePosition(e, chart);
|
||||
return {
|
||||
// allow access to the native event
|
||||
native: e,
|
||||
|
||||
// our interal event type
|
||||
type: typeMap[e.type],
|
||||
|
||||
// width and height of chart
|
||||
width: chart.width,
|
||||
height: chart.height,
|
||||
|
||||
// Position relative to the canvas
|
||||
x: relativePosition.x,
|
||||
y: relativePosition.y
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @method BrowserPlatform#acquireContext
|
||||
* @implements IPlatform#acquireContext
|
||||
*/
|
||||
acquireContext: function(item, config) {
|
||||
if (typeof item === 'string') {
|
||||
item = document.getElementById(item);
|
||||
} else if (item.length) {
|
||||
// Support for array based queries (such as jQuery)
|
||||
item = item[0];
|
||||
}
|
||||
|
||||
if (item && item.canvas) {
|
||||
// Support for any object associated to a canvas (including a context2d)
|
||||
item = item.canvas;
|
||||
}
|
||||
|
||||
if (item instanceof HTMLCanvasElement) {
|
||||
// To prevent canvas fingerprinting, some add-ons undefine the getContext
|
||||
// method, for example: https://github.com/kkapsner/CanvasBlocker
|
||||
// https://github.com/chartjs/Chart.js/issues/2807
|
||||
var context = item.getContext && item.getContext('2d');
|
||||
if (context instanceof CanvasRenderingContext2D) {
|
||||
initCanvas(item, config);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Restores the canvas initial state, such as render/display sizes and style.
|
||||
* @method BrowserPlatform#releaseContext
|
||||
* @implements IPlatform#releaseContext
|
||||
*/
|
||||
releaseContext: function(context) {
|
||||
var canvas = context.canvas;
|
||||
if (!canvas._chartjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var initial = canvas._chartjs.initial;
|
||||
['height', 'width'].forEach(function(prop) {
|
||||
var value = initial[prop];
|
||||
if (value === undefined || value === null) {
|
||||
canvas.removeAttribute(prop);
|
||||
} else {
|
||||
canvas.setAttribute(prop, value);
|
||||
}
|
||||
});
|
||||
|
||||
helpers.each(initial.style || {}, function(value, key) {
|
||||
canvas.style[key] = value;
|
||||
});
|
||||
|
||||
// The canvas render size might have been changed (and thus the state stack discarded),
|
||||
// we can't use save() and restore() to restore the initial state. So make sure that at
|
||||
// least the canvas context is reset to the default state by setting the canvas width.
|
||||
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
|
||||
canvas.width = canvas.width;
|
||||
|
||||
delete canvas._chartjs;
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -13,74 +13,6 @@ describe('Chart.Controller', function() {
|
||||
Chart.helpers.addEvent(content, state !== 'complete'? 'load' : 'resize', handler);
|
||||
}
|
||||
|
||||
describe('context acquisition', function() {
|
||||
var canvasId = 'chartjs-canvas';
|
||||
|
||||
beforeEach(function() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.setAttribute('id', canvasId);
|
||||
window.document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.getElementById(canvasId).remove();
|
||||
});
|
||||
|
||||
// see https://github.com/chartjs/Chart.js/issues/2807
|
||||
it('should gracefully handle invalid item', function() {
|
||||
var chart = new Chart('foobar');
|
||||
|
||||
expect(chart).not.toBeValidChart();
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a DOM element id', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart(canvasId);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a canvas element', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart(canvas);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a canvas context2D', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var context = canvas.getContext('2d');
|
||||
var chart = new Chart(context);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(context);
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept an array containing canvas', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart([canvas]);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('config initialization', function() {
|
||||
it('should create missing config.data properties', function() {
|
||||
var chart = acquireChart({});
|
||||
@ -164,152 +96,7 @@ describe('Chart.Controller', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.options.aspectRatio', function() {
|
||||
it('should use default "global" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 620px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 620, dh: 310,
|
||||
rw: 620, rh: 310,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default "chart" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
type: 'doughnut',
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 425px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 425, dh: 425,
|
||||
rw: 425, rh: 425,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use "user" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false,
|
||||
aspectRatio: 3
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 405px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 405, dh: 135,
|
||||
rw: 405, rh: 135,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not apply aspect ratio when height specified', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false,
|
||||
aspectRatio: 3
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 400px; height: 410px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 400, dh: 410,
|
||||
rw: 400, rh: 410,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.options.responsive: false', function() {
|
||||
it('should use default canvas size for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: ''
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 300, dh: 150,
|
||||
rw: 300, rh: 150,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use canvas attributes for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: '',
|
||||
width: 305,
|
||||
height: 245,
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 305, dh: 245,
|
||||
rw: 305, rh: 245,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use canvas style for render and display sizes (if no attributes)', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 345px; height: 125px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 345, dh: 125,
|
||||
rw: 345, rh: 125,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use attributes for the render size and style for the display size', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 345px; height: 125px;',
|
||||
width: 165,
|
||||
height: 85,
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 345, dh: 125,
|
||||
rw: 165, rh: 85,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not inject the resizer element', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
@ -563,27 +350,6 @@ describe('Chart.Controller', function() {
|
||||
});
|
||||
|
||||
describe('config.options.responsive: true (maintainAspectRatio: true)', function() {
|
||||
it('should fill parent width and use aspect ratio to calculate height', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 150px; height: 245px'
|
||||
},
|
||||
wrapper: {
|
||||
style: 'width: 300px; height: 350px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 300, dh: 490,
|
||||
rw: 300, rh: 490,
|
||||
});
|
||||
});
|
||||
|
||||
it('should resize the canvas with correct aspect ratio when parent width changes', function(done) {
|
||||
var chart = acquireChart({
|
||||
type: 'line', // AR == 2
|
||||
@ -714,69 +480,6 @@ describe('Chart.Controller', function() {
|
||||
});
|
||||
|
||||
describe('controller.destroy', function() {
|
||||
it('should reset context to default values', function() {
|
||||
var chart = acquireChart({});
|
||||
var context = chart.chart.ctx;
|
||||
|
||||
chart.destroy();
|
||||
|
||||
// https://www.w3.org/TR/2dcontext/#conformance-requirements
|
||||
Chart.helpers.each({
|
||||
fillStyle: '#000000',
|
||||
font: '10px sans-serif',
|
||||
lineJoin: 'miter',
|
||||
lineCap: 'butt',
|
||||
lineWidth: 1,
|
||||
miterLimit: 10,
|
||||
shadowBlur: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0)',
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
strokeStyle: '#000000',
|
||||
textAlign: 'start',
|
||||
textBaseline: 'alphabetic'
|
||||
}, function(value, key) {
|
||||
expect(context[key]).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
it('should restore canvas initial values', function(done) {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
width: 180,
|
||||
style: 'width: 512px; height: 480px'
|
||||
},
|
||||
wrapper: {
|
||||
style: 'width: 450px; height: 450px; position: relative'
|
||||
}
|
||||
});
|
||||
|
||||
var canvas = chart.chart.canvas;
|
||||
var wrapper = canvas.parentNode;
|
||||
wrapper.style.width = '475px';
|
||||
waitForResize(chart, function() {
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 475, dh: 450,
|
||||
rw: 475, rh: 450,
|
||||
});
|
||||
|
||||
chart.destroy();
|
||||
|
||||
expect(canvas.getAttribute('width')).toBe('180');
|
||||
expect(canvas.getAttribute('height')).toBe(null);
|
||||
expect(canvas.style.width).toBe('512px');
|
||||
expect(canvas.style.height).toBe('480px');
|
||||
expect(canvas.style.display).toBe('');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove the resizer element when responsive: true', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
|
||||
373
test/platform.dom.tests.js
Normal file
373
test/platform.dom.tests.js
Normal file
@ -0,0 +1,373 @@
|
||||
describe('Platform.dom', function() {
|
||||
|
||||
function waitForResize(chart, callback) {
|
||||
var resizer = chart.chart.canvas.parentNode._chartjs.resizer;
|
||||
var content = resizer.contentWindow || resizer;
|
||||
var state = content.document.readyState || 'complete';
|
||||
var handler = function() {
|
||||
Chart.helpers.removeEvent(content, 'load', handler);
|
||||
Chart.helpers.removeEvent(content, 'resize', handler);
|
||||
setTimeout(callback, 50);
|
||||
};
|
||||
|
||||
Chart.helpers.addEvent(content, state !== 'complete'? 'load' : 'resize', handler);
|
||||
}
|
||||
|
||||
describe('context acquisition', function() {
|
||||
var canvasId = 'chartjs-canvas';
|
||||
|
||||
beforeEach(function() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.setAttribute('id', canvasId);
|
||||
window.document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.getElementById(canvasId).remove();
|
||||
});
|
||||
|
||||
// see https://github.com/chartjs/Chart.js/issues/2807
|
||||
it('should gracefully handle invalid item', function() {
|
||||
var chart = new Chart('foobar');
|
||||
|
||||
expect(chart).not.toBeValidChart();
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a DOM element id', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart(canvasId);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a canvas element', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart(canvas);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept a canvas context2D', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var context = canvas.getContext('2d');
|
||||
var chart = new Chart(context);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(context);
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
|
||||
it('should accept an array containing canvas', function() {
|
||||
var canvas = document.getElementById(canvasId);
|
||||
var chart = new Chart([canvas]);
|
||||
|
||||
expect(chart).toBeValidChart();
|
||||
expect(chart.chart.canvas).toBe(canvas);
|
||||
expect(chart.chart.ctx).toBe(canvas.getContext('2d'));
|
||||
|
||||
chart.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.options.aspectRatio', function() {
|
||||
it('should use default "global" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 620px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 620, dh: 310,
|
||||
rw: 620, rh: 310,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use default "chart" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
type: 'doughnut',
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 425px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 425, dh: 425,
|
||||
rw: 425, rh: 425,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use "user" aspect ratio for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false,
|
||||
aspectRatio: 3
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 405px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 405, dh: 135,
|
||||
rw: 405, rh: 135,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not apply aspect ratio when height specified', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false,
|
||||
aspectRatio: 3
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 400px; height: 410px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 400, dh: 410,
|
||||
rw: 400, rh: 410,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.options.responsive: false', function() {
|
||||
it('should use default canvas size for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: ''
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 300, dh: 150,
|
||||
rw: 300, rh: 150,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use canvas attributes for render and display sizes', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: '',
|
||||
width: 305,
|
||||
height: 245,
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 305, dh: 245,
|
||||
rw: 305, rh: 245,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use canvas style for render and display sizes (if no attributes)', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 345px; height: 125px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 345, dh: 125,
|
||||
rw: 345, rh: 125,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use attributes for the render size and style for the display size', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 345px; height: 125px;',
|
||||
width: 165,
|
||||
height: 85,
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 345, dh: 125,
|
||||
rw: 165, rh: 85,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('config.options.responsive: true (maintainAspectRatio: true)', function() {
|
||||
it('should fill parent width and use aspect ratio to calculate height', function() {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
style: 'width: 150px; height: 245px'
|
||||
},
|
||||
wrapper: {
|
||||
style: 'width: 300px; height: 350px'
|
||||
}
|
||||
});
|
||||
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 300, dh: 490,
|
||||
rw: 300, rh: 490,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('controller.destroy', function() {
|
||||
it('should reset context to default values', function() {
|
||||
var chart = acquireChart({});
|
||||
var context = chart.chart.ctx;
|
||||
|
||||
chart.destroy();
|
||||
|
||||
// https://www.w3.org/TR/2dcontext/#conformance-requirements
|
||||
Chart.helpers.each({
|
||||
fillStyle: '#000000',
|
||||
font: '10px sans-serif',
|
||||
lineJoin: 'miter',
|
||||
lineCap: 'butt',
|
||||
lineWidth: 1,
|
||||
miterLimit: 10,
|
||||
shadowBlur: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0)',
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
strokeStyle: '#000000',
|
||||
textAlign: 'start',
|
||||
textBaseline: 'alphabetic'
|
||||
}, function(value, key) {
|
||||
expect(context[key]).toBe(value);
|
||||
});
|
||||
});
|
||||
|
||||
it('should restore canvas initial values', function(done) {
|
||||
var chart = acquireChart({
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
}, {
|
||||
canvas: {
|
||||
width: 180,
|
||||
style: 'width: 512px; height: 480px'
|
||||
},
|
||||
wrapper: {
|
||||
style: 'width: 450px; height: 450px; position: relative'
|
||||
}
|
||||
});
|
||||
|
||||
var canvas = chart.chart.canvas;
|
||||
var wrapper = canvas.parentNode;
|
||||
wrapper.style.width = '475px';
|
||||
waitForResize(chart, function() {
|
||||
expect(chart).toBeChartOfSize({
|
||||
dw: 475, dh: 450,
|
||||
rw: 475, rh: 450,
|
||||
});
|
||||
|
||||
chart.destroy();
|
||||
|
||||
expect(canvas.getAttribute('width')).toBe('180');
|
||||
expect(canvas.getAttribute('height')).toBe(null);
|
||||
expect(canvas.style.width).toBe('512px');
|
||||
expect(canvas.style.height).toBe('480px');
|
||||
expect(canvas.style.display).toBe('');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('event handling', function() {
|
||||
it('should notify plugins about events', function() {
|
||||
var notifiedEvent;
|
||||
var plugin = {
|
||||
onEvent: function(chart, e) {
|
||||
notifiedEvent = e;
|
||||
}
|
||||
};
|
||||
var chart = acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['A', 'B', 'C', 'D'],
|
||||
datasets: [{
|
||||
data: [10, 20, 30, 100]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true
|
||||
},
|
||||
plugins: [plugin]
|
||||
});
|
||||
|
||||
var node = chart.chart.canvas;
|
||||
var rect = node.getBoundingClientRect();
|
||||
var clientX = (rect.left + rect.right) / 2;
|
||||
var clientY = (rect.top + rect.bottom) / 2;
|
||||
|
||||
var evt = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: clientX,
|
||||
clientY: clientY
|
||||
});
|
||||
|
||||
// Manually trigger rather than having an async test
|
||||
node.dispatchEvent(evt);
|
||||
|
||||
// Check that notifiedEvent is correct
|
||||
expect(notifiedEvent).not.toBe(undefined);
|
||||
expect(notifiedEvent.native).toBe(evt);
|
||||
|
||||
// Is type correctly translated
|
||||
expect(notifiedEvent.type).toBe(evt.type);
|
||||
|
||||
// Canvas width and height
|
||||
expect(notifiedEvent.width).toBe(chart.chart.width);
|
||||
expect(notifiedEvent.height).toBe(chart.chart.height);
|
||||
|
||||
// Relative Position
|
||||
expect(notifiedEvent.x).toBe(chart.chart.width / 2);
|
||||
expect(notifiedEvent.y).toBe(chart.chart.height / 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user