diff --git a/docs/guide/types/box.md b/docs/guide/types/box.md index 6d186c91f..de9981cf8 100644 --- a/docs/guide/types/box.md +++ b/docs/guide/types/box.md @@ -134,7 +134,7 @@ All of these options can be [Scriptable](../options#scriptable-options) | `xAdjust` | `number` | `0` | Adjustment along x-axis (left-right) of label relative to computed position. Negative values move the label left, positive right. | `yAdjust` | `number` | `0` | Adjustment along y-axis (top-bottom) of label relative to computed position. Negative values move the label up, positive down. -#### Position +### Position A position can be set in 2 different values types: diff --git a/docs/guide/types/label.md b/docs/guide/types/label.md index 1e3db4021..ee354140c 100644 --- a/docs/guide/types/label.md +++ b/docs/guide/types/label.md @@ -95,7 +95,6 @@ If one of the axes does not match an axis in the chart, the content will be rend The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`. - | Name | Description | ---- | ---- | `adjustScaleRange` | Should the scale range be adjusted if this annotation is out of range. @@ -155,7 +154,7 @@ If this value is a number, it is applied to all corners of the rectangle (topLef A callout connects the label by a line to the selected point. -Namespace: `options.annotations[annotationID].label.callout`, it defines options for the callout on the annotation label. +Namespace: `options.annotations[annotationID].callout`, it defines options for the callout on the annotation label. ```js chart-editor /* */ diff --git a/docs/guide/types/line.md b/docs/guide/types/line.md index b29af9113..11a1d0b53 100644 --- a/docs/guide/types/line.md +++ b/docs/guide/types/line.md @@ -158,7 +158,7 @@ All of these options can be [Scriptable](../options#scriptable-options) | `yAdjust` | `number` | `0` | Adjustment along y-axis (top-bottom) of label relative to computed position. Negative values move the label up, positive down. | `yPadding` | `number` | `6` | Padding of label to add top/bottom. This is **deprecated**. Use `padding`. -#### borderRadius +### borderRadius If this value is a number, it is applied to all corners of the rectangle (topLeft, topRight, bottomLeft, bottomRight). If this value is an object, the `topLeft` property defines the top-left corners border radius. Similarly, the `topRight`, `bottomLeft`, and `bottomRight` properties can also be specified. Omitted corners have radius of 0. diff --git a/docs/guide/types/point.md b/docs/guide/types/point.md index 467b48301..4c0c602f6 100644 --- a/docs/guide/types/point.md +++ b/docs/guide/types/point.md @@ -80,8 +80,7 @@ The following options are available for point annotations. If one of the axes does not match an axis in the chart, the point annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the point annotation will take the center of the chart as point. -The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`. - +The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`. | Name | Description | ---- | ---- diff --git a/docs/guide/types/polygon.md b/docs/guide/types/polygon.md index f2131f0c6..adabb37c9 100644 --- a/docs/guide/types/polygon.md +++ b/docs/guide/types/polygon.md @@ -63,6 +63,7 @@ The following options are available for polygon annotations. | [`borderWidth`](#styling) | `number`| Yes | `1` | [`display`](#general) | `boolean` | Yes | `true` | [`drawTime`](#general) | `string` | Yes | `'afterDatasetsDraw'` +| [`point`](#point) | `object` | Yes | `{radius: 0}` | [`radius`](#general) | `number` | Yes | `10` | [`rotation`](#general) | `number` | Yes | `0` | [`shadowBlur`](#styling) | `number` | Yes | `0` @@ -84,7 +85,7 @@ The following options are available for polygon annotations. If one of the axes does not match an axis in the chart, the polygon annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the polygon annotation will take the center of the chart. -The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`. +The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`. | Name | Description | ---- | ---- @@ -121,3 +122,58 @@ The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the bo | `shadowBlur` | The amount of blur applied to shadow. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowBlur). | `shadowOffsetX` | The distance that shadow will be offset horizontally. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetX). | `shadowOffsetY` | The distance that shadow will be offset vertically. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/shadowOffsetY). + +### Point + +Polygon consists of points. These points are actually [Point Annotations](point) and all of the [styling options](point#styling) can be configured. General options affecting the location of the point are ignored. + +Namespace: `options.annotations[annotationID].point`, it defines options for the callout on the annotation label. + +```js chart-editor +/* */ +const options = { + plugins: { + autocolors: false, + annotation: { + annotations: { + pentagon: { + type: 'polygon', + xValue: 1, + yValue: 60, + sides: 4, + radius: 60, + backgroundColor: 'rgba(255, 99, 132, 0.25)', + point: { + radius: 10, + borderWidth: 2, + borderColor: '#666', + backgroundColor: 'rgba(99, 132, 255, 0.25)', + } + } + } + } + } +}; +/* */ + +/* */ +const config = { + type: 'line', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [{ + label: 'My First Dataset', + data: [65, 59, 80, 81, 56, 55, 40], + fill: false, + borderColor: 'rgb(75, 192, 192)', + tension: 0.1 + }] + }, + options +}; +/* */ + +module.exports = { + config +}; +``` diff --git a/docs/samples/polygon/basic.md b/docs/samples/polygon/basic.md index 837de2403..7ac8b92dd 100644 --- a/docs/samples/polygon/basic.md +++ b/docs/samples/polygon/basic.md @@ -123,6 +123,20 @@ const actions = [ }); chart.update(); } + }, + { + name: 'Add a side to annotation 1', + handler: function(chart) { + chart.options.plugins.annotation.annotations.annotation1.sides++; + chart.update(); + } + }, + { + name: 'Remove a side from annotation 1', + handler: function(chart) { + chart.options.plugins.annotation.annotations.annotation1.sides--; + chart.update(); + } } ]; diff --git a/src/annotation.js b/src/annotation.js index f6ba575e7..3861b1b91 100644 --- a/src/annotation.js +++ b/src/annotation.js @@ -3,28 +3,22 @@ import {clipArea, unclipArea, isObject, isArray} from 'chart.js/helpers'; import {handleEvent, hooks, updateListeners} from './events'; import {adjustScaleRange, verifyScaleOptions} from './scale'; import {annotationTypes} from './types'; +import {requireVersion} from './helpers'; import {version} from '../package.json'; const chartStates = new Map(); -const versionParts = Chart.version.split('.'); export default { id: 'annotation', version, + beforeRegister() { + requireVersion('chart.js', '3.7', Chart.version); + }, + afterRegister() { Chart.register(annotationTypes); - - // TODO: Remove this workaround when strictly requiring Chart.js v3.7 or newer - if (versionParts[0] === '3' && parseInt(versionParts[1], 10) <= 6) { - // Workaround for https://github.com/chartjs/chartjs-plugin-annotation/issues/572 - Chart.defaults.set('elements.lineAnnotation', { - callout: {}, - font: {}, - padding: 6 - }); - } }, afterUnregister() { @@ -155,18 +149,49 @@ function updateElements(chart, state, options, mode) { const elements = resyncElements(state.elements, annotations); for (let i = 0; i < annotations.length; i++) { - const annotation = annotations[i]; - let el = elements[i]; - const elementClass = annotationTypes[resolveType(annotation.type)]; - if (!el || !(el instanceof elementClass)) { - el = elements[i] = new elementClass(); - } - const opts = resolveAnnotationOptions(annotation.setContext(getContext(chart, el, annotation))); - const properties = el.resolveElementProperties(chart, opts); + const annotationOptions = annotations[i]; + const element = getOrCreateElement(elements, i, annotationOptions.type); + const resolver = annotationOptions.setContext(getContext(chart, element, annotationOptions)); + const resolvedOptions = resolveAnnotationOptions(resolver); + const properties = element.resolveElementProperties(chart, resolvedOptions); + properties.skip = isNaN(properties.x) || isNaN(properties.y); - properties.options = opts; - animations.update(el, properties); + properties.options = resolvedOptions; + + if ('elements' in properties) { + updateSubElements(element, properties, resolver, animations); + // Remove the sub-element definitions from properties, so the actual elements + // are not overwritten by their definitions + delete properties.elements; + } + + animations.update(element, properties); + } +} + +function updateSubElements(mainElement, {elements, initProperties}, resolver, animations) { + const subElements = mainElement.elements || (mainElement.elements = []); + subElements.length = elements.length; + for (let i = 0; i < elements.length; i++) { + const definition = elements[i]; + const properties = definition.properties; + const subElement = getOrCreateElement(subElements, i, definition.type, initProperties); + const subResolver = resolver[definition.optionScope].override(definition); + properties.options = resolveAnnotationOptions(subResolver); + animations.update(subElement, properties); + } +} + +function getOrCreateElement(elements, index, type, initProperties) { + const elementClass = annotationTypes[resolveType(type)]; + let element = elements[index]; + if (!element || !(element instanceof elementClass)) { + element = elements[index] = new elementClass(); + if (isObject(initProperties)) { + Object.assign(element, initProperties); + } } + return element; } function resolveAnnotationOptions(resolver) { @@ -175,7 +200,9 @@ function resolveAnnotationOptions(resolver) { result.id = resolver.id; result.type = resolver.type; result.drawTime = resolver.drawTime; - Object.assign(result, resolveObj(resolver, elementClass.defaults), resolveObj(resolver, elementClass.defaultRoutes)); + Object.assign(result, + resolveObj(resolver, elementClass.defaults), + resolveObj(resolver, elementClass.defaultRoutes)); for (const hook of hooks) { result[hook] = resolver[hook]; } @@ -215,21 +242,20 @@ function resyncElements(elements, annotations) { function draw(chart, caller, clip) { const {ctx, chartArea} = chart; - const state = chartStates.get(chart); + const {visibleElements} = chartStates.get(chart); if (clip) { clipArea(ctx, chartArea); } - state.visibleElements.forEach(el => { - if (el.options.drawTime === caller) { - el.draw(ctx); - } - }); + + drawElements(ctx, visibleElements, caller); + drawSubElements(ctx, visibleElements, caller); + if (clip) { unclipArea(ctx); } - state.visibleElements.forEach(el => { + visibleElements.forEach(el => { if (!('drawLabel' in el)) { return; } @@ -239,3 +265,19 @@ function draw(chart, caller, clip) { } }); } + +function drawElements(ctx, elements, caller) { + for (const el of elements) { + if (el.options.drawTime === caller) { + el.draw(ctx); + } + } +} + +function drawSubElements(ctx, elements, caller) { + for (const el of elements) { + if (isArray(el.elements)) { + drawElements(ctx, el.elements, caller); + } + } +} diff --git a/src/helpers/helpers.chart.js b/src/helpers/helpers.chart.js index e38585a79..059c2e933 100644 --- a/src/helpers/helpers.chart.js +++ b/src/helpers/helpers.chart.js @@ -2,11 +2,30 @@ import {isFinite} from 'chart.js/helpers'; import {getRectCenterPoint} from './helpers.geometric'; import {isBoundToPoint} from './helpers.options'; +/** + * @typedef { import("chart.js").Chart } Chart + * @typedef { import("chart.js").Scale } Scale + * @typedef { import("chart.js").Point } Point + * @typedef { import('../../types/options').CoreAnnotationOptions } CoreAnnotationOptions + * @typedef { import('../../types/options').PointAnnotationOptions } PointAnnotationOptions + */ + +/** + * @param {Scale} scale + * @param {number|string} value + * @param {number} fallback + * @returns {number} + */ export function scaleValue(scale, value, fallback) { value = typeof value === 'number' ? value : scale.parse(value); return isFinite(value) ? scale.getPixelForValue(value) : fallback; } +/** + * @param {Scale} scale + * @param {{start: number, end: number}} options + * @returns {{start: number, end: number}} + */ function getChartDimensionByScale(scale, options) { if (scale) { const min = scaleValue(scale, options.min, options.start); @@ -22,6 +41,11 @@ function getChartDimensionByScale(scale, options) { }; } +/** + * @param {Chart} chart + * @param {CoreAnnotationOptions} options + * @returns {Point} + */ export function getChartPoint(chart, options) { const {chartArea, scales} = chart; const xScale = scales[options.xScaleID]; @@ -39,13 +63,18 @@ export function getChartPoint(chart, options) { return {x, y}; } +/** + * @param {Chart} chart + * @param {CoreAnnotationOptions} options + * @returns {{x?:number, y?: number, x2?: number, y2?: number, width?: number, height?: number}} + */ export function getChartRect(chart, options) { const xScale = chart.scales[options.xScaleID]; const yScale = chart.scales[options.yScaleID]; let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea; if (!xScale && !yScale) { - return {options: {}}; + return {}; } const xDim = getChartDimensionByScale(xScale, {min: options.xMin, max: options.xMax, start: x, end: x2}); @@ -65,6 +94,10 @@ export function getChartRect(chart, options) { }; } +/** + * @param {Chart} chart + * @param {PointAnnotationOptions} options + */ export function getChartCircle(chart, options) { const point = getChartPoint(chart, options); return { @@ -75,6 +108,11 @@ export function getChartCircle(chart, options) { }; } +/** + * @param {Chart} chart + * @param {PointAnnotationOptions} options + * @returns + */ export function resolvePointPosition(chart, options) { if (!isBoundToPoint(options)) { const box = getChartRect(chart, options); diff --git a/src/helpers/helpers.core.js b/src/helpers/helpers.core.js index f9348ccd3..ac77c1f2e 100644 --- a/src/helpers/helpers.core.js +++ b/src/helpers/helpers.core.js @@ -27,3 +27,19 @@ export function getElementCenterPoint(element, useFinalPosition) { const {x, y} = element.getProps(['x', 'y'], useFinalPosition); return {x, y}; } + +const isOlderPart = (act, req) => req > act || (act.length > req.length && act.substr(0, req.length) === req); + +export function requireVersion(pkg, min, ver) { + const parts = ver.split('.'); + let i = 0; + for (const req of min.split('.')) { + const act = parts[i++]; + if (parseInt(req, 10) < parseInt(act, 10)) { + break; + } + if (isOlderPart(act, req)) { + throw new Error(`${pkg} v${ver} is not supported. v${min} or newer is required.`); + } + } +} diff --git a/src/helpers/helpers.geometric.js b/src/helpers/helpers.geometric.js index 06b933a57..bb133292e 100644 --- a/src/helpers/helpers.geometric.js +++ b/src/helpers/helpers.geometric.js @@ -1,3 +1,11 @@ +/** + * @typedef {import('chart.js').Point} Point + */ + +/** + * @param {{x: number, y: number, width: number, height: number}} rect + * @returns {Point} + */ export function getRectCenterPoint(rect) { const {x, y, width, height} = rect; return { @@ -8,10 +16,10 @@ export function getRectCenterPoint(rect) { /** * Rotate a `point` relative to `center` point by `angle` - * @param {{x: number, y: number}} point - the point to rotate - * @param {{x: number, y: number}} center - center point for rotation + * @param {Point} point - the point to rotate + * @param {Point} center - center point for rotation * @param {number} angle - angle for rotation, in radians - * @returns {{x: number, y: number}} rotated point + * @returns {Point} rotated point */ export function rotated(point, center, angle) { var cos = Math.cos(angle); diff --git a/src/types/box.js b/src/types/box.js index 6f6776a74..319927645 100644 --- a/src/types/box.js +++ b/src/types/box.js @@ -19,22 +19,24 @@ export default class BoxAnnotation extends Element { drawLabel(ctx) { const {x, y, width, height, options} = this; - const labelOpts = options.label; - labelOpts.borderWidth = options.borderWidth; - ctx.save(); - ctx.beginPath(); - ctx.rect(x + labelOpts.borderWidth / 2, y + labelOpts.borderWidth / 2, width - labelOpts.borderWidth, height - labelOpts.borderWidth); - ctx.clip(); - const position = toPosition(labelOpts.position); - const padding = toPadding(labelOpts.padding); - const labelSize = measureLabelSize(ctx, labelOpts); + const {label, borderWidth} = options; + const halfBorder = borderWidth / 2; + const position = toPosition(label.position); + const padding = toPadding(label.padding); + const labelSize = measureLabelSize(ctx, label); const labelRect = { x: calculateX(this, labelSize, position, padding), y: calculateY(this, labelSize, position, padding), width: labelSize.width, height: labelSize.height }; - drawLabel(ctx, labelRect, labelOpts); + + ctx.save(); + ctx.beginPath(); + ctx.rect(x + halfBorder + padding.left, y + halfBorder + padding.top, + width - borderWidth - padding.width, height - borderWidth - padding.height); + ctx.clip(); + drawLabel(ctx, labelRect, label); ctx.restore(); } @@ -58,6 +60,7 @@ BoxAnnotation.defaults = { cornerRadius: undefined, // TODO: v2 remove support for cornerRadius display: true, label: { + borderWidth: undefined, color: 'black', content: null, drawTime: undefined, @@ -93,6 +96,12 @@ BoxAnnotation.defaultRoutes = { backgroundColor: 'color' }; +BoxAnnotation.descriptors = { + label: { + _fallback: true + } +}; + function calculateX(box, labelSize, position, padding) { const {x: start, x2: end, width: size, options} = box; const {xAdjust: adjust, borderWidth} = options.label; diff --git a/src/types/point.js b/src/types/point.js index e6377bf88..4eb7d8250 100644 --- a/src/types/point.js +++ b/src/types/point.js @@ -16,6 +16,9 @@ export default class PointAnnotation extends Element { draw(ctx) { const options = this.options; const borderWidth = options.borderWidth; + if (options.radius < 0.1) { + return; + } ctx.save(); ctx.fillStyle = options.backgroundColor; setShadowStyle(ctx, options); diff --git a/src/types/polygon.js b/src/types/polygon.js index 7c42fb71f..d2c528a66 100644 --- a/src/types/polygon.js +++ b/src/types/polygon.js @@ -1,12 +1,10 @@ import {Element} from 'chart.js'; -import {PI, RAD_PER_DEG, isNumber} from 'chart.js/helpers'; +import {PI, RAD_PER_DEG} from 'chart.js/helpers'; import {setBorderStyle, resolvePointPosition, getElementCenterPoint, setShadowStyle} from '../helpers'; export default class PolygonAnnotation extends Element { - inRange(mouseX, mouseY, useFinalPosition) { - const vertices = getVertices(this.getProps(['x', 'y'], useFinalPosition), this.options, true); - return vertices && vertices.length > 0 && pointIsInPolygon(vertices, mouseX, mouseY); + return this.elements.length > 1 && pointIsInPolygon(this.elements, mouseX, mouseY, useFinalPosition); } getCenterPoint(useFinalPosition) { @@ -14,18 +12,20 @@ export default class PolygonAnnotation extends Element { } draw(ctx) { - const {x, y, options} = this; - const vertices = getVertices({x, y}, options); - let vertex = vertices[0]; + const {elements, options} = this; ctx.save(); ctx.beginPath(); ctx.fillStyle = options.backgroundColor; setShadowStyle(ctx, options); const stroke = setBorderStyle(ctx, options); - ctx.moveTo(vertex.x, vertex.y); - for (let i = 1; i < vertices.length; i++) { - vertex = vertices[i]; - ctx.lineTo(vertex.x, vertex.y); + let first = true; + for (const el of elements) { + if (first) { + ctx.moveTo(el.x, el.y); + first = false; + } else { + ctx.lineTo(el.x, el.y); + } } ctx.closePath(); ctx.fill(); @@ -38,12 +38,28 @@ export default class PolygonAnnotation extends Element { } resolveElementProperties(chart, options) { - if (isNumber(options.sides) && options.sides >= 1) { - return resolvePointPosition(chart, options); + const {x, y, width, height} = resolvePointPosition(chart, options); + const {sides, radius, rotation, borderWidth} = options; + const halfBorder = borderWidth / 2; + const elements = []; + const angle = (2 * PI) / sides; + let rad = rotation * RAD_PER_DEG; + for (let i = 0; i < sides; i++, rad += angle) { + const sin = Math.sin(rad); + const cos = Math.cos(rad); + elements.push({ + type: 'point', + optionScope: 'point', + properties: { + x: x + sin * radius, + y: y - cos * radius, + bX: x + sin * (radius + halfBorder), + bY: y - cos * (radius + halfBorder) + } + }); } - return {options: {}}; + return {x, y, width, height, elements, initProperties: {x, y}}; } - } PolygonAnnotation.id = 'polygonAnnotation'; @@ -58,6 +74,9 @@ PolygonAnnotation.defaults = { borderShadowColor: 'transparent', borderWidth: 1, display: true, + point: { + radius: 0 + }, radius: 10, rotation: 0, shadowBlur: 0, @@ -81,36 +100,16 @@ PolygonAnnotation.defaultRoutes = { backgroundColor: 'color' }; -function getVertices(point, options, useBorderWidth = false) { - const {sides, radius} = options; - const hBorderWidth = useBorderWidth ? options.borderWidth / 2 || 0 : 0; - let angle = (2 * PI) / sides; - let rad = options.rotation * RAD_PER_DEG; - const vertices = new Array(); - addVertex(vertices, point, rad, radius + hBorderWidth); - for (let i = 0; i < sides; i++) { - rad += angle; - addVertex(vertices, point, rad, radius + hBorderWidth); - } - return vertices; -} - -function addVertex(array, point, rad, radius) { - array.push({ - x: point.x + Math.sin(rad) * radius, - y: point.y - Math.cos(rad) * radius - }); -} -function pointIsInPolygon(vertices, x, y) { +function pointIsInPolygon(points, x, y, useFinalPosition) { let isInside = false; - let i = 0; - let j = vertices.length - 1; - for (i, j; i < vertices.length; j = i++) { - if ((vertices[i].y > y) !== (vertices[j].y > y) && - x < (vertices[j].x - vertices[i].x) * (y - vertices[i].y) / (vertices[j].y - vertices[i].y) + vertices[i].x) { + let A = points[points.length - 1].getProps(['bX', 'bY'], useFinalPosition); + for (const point of points) { + const B = point.getProps(['bX', 'bY'], useFinalPosition); + if ((B.bY > y) !== (A.bY > y) && x < (A.bX - B.bX) * (y - B.bY) / (A.bY - B.bY) + B.bX) { isInside = !isInside; } + A = B; } return isInside; } diff --git a/test/fixtures/box/label.js b/test/fixtures/box/label.js index 12ea0fee7..612d2d1a2 100644 --- a/test/fixtures/box/label.js +++ b/test/fixtures/box/label.js @@ -38,12 +38,12 @@ module.exports = { yMin: 11, yMax: 15, backgroundColor: 'rgba(255, 99, 132, 0.5)', - borderColor: 'rgba(255, 99, 132)', - borderDash: [6, 6], - borderWidth: 5, + borderColor: 'black', + borderWidth: 15, label: { enabled: true, - content: 'This is a label', + borderColor: 'green', + content: 'This label tests clipping', position: 'start' } }, diff --git a/test/fixtures/box/label.png b/test/fixtures/box/label.png index c3ca1ad5f..7408bbf86 100644 Binary files a/test/fixtures/box/label.png and b/test/fixtures/box/label.png differ diff --git a/test/fixtures/box/labelMultiline.js b/test/fixtures/box/labelMultiline.js index b8f7a010b..ef112c088 100644 --- a/test/fixtures/box/labelMultiline.js +++ b/test/fixtures/box/labelMultiline.js @@ -1,5 +1,4 @@ module.exports = { - tolerance: 0.0075, config: { type: 'bar', options: { @@ -23,8 +22,7 @@ module.exports = { yMin: 5, yMax: 10, backgroundColor: 'rgba(178, 255, 102, 0.5)', - borderColor: 'rgb(178, 255, 102)', - borderDash: [6, 6], + borderColor: 'red', borderWidth: 5, label: { enabled: true, @@ -38,8 +36,7 @@ module.exports = { yMin: 11, yMax: 15, backgroundColor: 'rgba(178, 255, 102, 0.5)', - borderColor: 'rgb(178, 255, 102)', - borderDash: [6, 6], + borderColor: 'red', borderWidth: 5, label: { enabled: true, @@ -54,8 +51,7 @@ module.exports = { yMin: 16, yMax: 20, backgroundColor: 'rgba(178, 255, 102, 0.5)', - borderColor: 'rgb(178, 255, 102)', - borderDash: [6, 6], + borderColor: 'red', borderWidth: 5, label: { enabled: true, @@ -70,8 +66,7 @@ module.exports = { yMin: 5, yMax: 9, backgroundColor: 'rgba(178, 255, 102, 0.5)', - borderColor: 'rgb(178, 255, 102)', - borderDash: [6, 6], + borderColor: 'red', borderWidth: 5, label: { enabled: true, diff --git a/test/fixtures/box/labelMultiline.png b/test/fixtures/box/labelMultiline.png index c38ffab90..beeb3f8c9 100644 Binary files a/test/fixtures/box/labelMultiline.png and b/test/fixtures/box/labelMultiline.png differ diff --git a/test/fixtures/box/labelPadding.js b/test/fixtures/box/labelPadding.js index 9557da510..393f1280a 100644 --- a/test/fixtures/box/labelPadding.js +++ b/test/fixtures/box/labelPadding.js @@ -1,5 +1,4 @@ module.exports = { - tolerance: 0.0075, config: { type: 'bar', options: { @@ -24,7 +23,6 @@ module.exports = { yMax: 10, backgroundColor: 'rgba(255, 99, 132, 0.5)', borderColor: 'rgba(255, 99, 132)', - borderDash: [6, 6], borderWidth: 5, label: { enabled: true, @@ -41,7 +39,6 @@ module.exports = { yMax: 15, backgroundColor: 'rgba(255, 99, 132, 0.5)', borderColor: 'rgba(255, 99, 132)', - borderDash: [6, 6], borderWidth: 5, label: { enabled: true, @@ -58,7 +55,6 @@ module.exports = { yMax: 20, backgroundColor: 'rgba(255, 99, 132, 0.5)', borderColor: 'rgba(255, 99, 132)', - borderDash: [6, 6], borderWidth: 5, label: { enabled: true, @@ -75,7 +71,6 @@ module.exports = { yMax: 9, backgroundColor: 'rgba(255, 99, 132, 0.5)', borderColor: 'rgba(255, 99, 132)', - borderDash: [6, 6], borderWidth: 5, label: { enabled: true, diff --git a/test/fixtures/box/labelPadding.png b/test/fixtures/box/labelPadding.png index 7e05bb125..b7bb450f2 100644 Binary files a/test/fixtures/box/labelPadding.png and b/test/fixtures/box/labelPadding.png differ diff --git a/test/fixtures/polygon/hexagonWithPoints.js b/test/fixtures/polygon/hexagonWithPoints.js new file mode 100644 index 000000000..04dbf6b43 --- /dev/null +++ b/test/fixtures/polygon/hexagonWithPoints.js @@ -0,0 +1,52 @@ +module.exports = { + tolerance: 0.0055, + config: { + type: 'scatter', + options: { + scales: { + x: { + display: false, + min: -10, + max: 10 + }, + y: { + display: false, + min: -10, + max: 10 + } + }, + plugins: { + legend: false, + annotation: { + drawTime: 'afterDraw', + annotations: { + octagon: { + type: 'polygon', + xScaleID: 'x', + yScaleID: 'y', + xValue: 1, + yValue: 1, + sides: 6, + backgroundColor: 'rgba(153, 153, 102, 0.5)', + borderColor: 'rgb(153, 153, 102)', + borderWidth: 1, + radius: 50, + point: { + radius: 3, + backgroundColor: 'red', + borderColor: 'black', + borderWidth: 1 + } + } + } + } + } + } + }, + options: { + canvas: { + width: 256, + height: 256 + } + } +}; diff --git a/test/fixtures/polygon/hexagonWithPoints.png b/test/fixtures/polygon/hexagonWithPoints.png new file mode 100644 index 000000000..97a4cddc2 Binary files /dev/null and b/test/fixtures/polygon/hexagonWithPoints.png differ diff --git a/test/index.js b/test/index.js index b170b18d0..12857eae9 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,9 @@ import {acquireChart, addMatchers, releaseCharts, specsFromFixtures, triggerMouseEvent, afterEvent} from 'chartjs-test-utils'; import {testEvents} from './events'; import {createCanvas, scatter10x10} from './utils'; +import * as helpers from '../src/helpers'; +window.helpers = helpers; window.devicePixelRatio = 1; window.acquireChart = acquireChart; window.afterEvent = afterEvent; diff --git a/test/specs/helpers.spec.js b/test/specs/helpers.spec.js new file mode 100644 index 000000000..92e3b9e02 --- /dev/null +++ b/test/specs/helpers.spec.js @@ -0,0 +1,20 @@ +describe('helpers', function() { + + describe('requireVersion', function() { + const requireVersion = window.helpers.requireVersion; + + it('should throw error for too old version', function() { + expect(() => requireVersion('test', '3.7', '2.9.3')).toThrowError(); + expect(() => requireVersion('test', '3.7', '3.6.99-alpha3')).toThrowError(); + expect(() => requireVersion('test', '16.13.2.8', '16.13.2.8-beta')).toThrowError(); + }); + + it('should not throw error for new enough version', function() { + expect(() => requireVersion('test', '3.7', '3.7.0-beta.1')).not.toThrowError(); + expect(() => requireVersion('test', '3.7.1', '3.7.19')).not.toThrowError(); + expect(() => requireVersion('test', '3.7', '4.0.0')).not.toThrowError(); + expect(() => requireVersion('test', '16.13.2', '16.13.3-rc')).not.toThrowError(); + }); + }); + +});