diff --git a/docs/charts/bar.md b/docs/charts/bar.md index 88e2744c713..5cee8d359a6 100644 --- a/docs/charts/bar.md +++ b/docs/charts/bar.md @@ -71,7 +71,8 @@ the color of the bars is generally set this way. | [`backgroundColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderSkipped`](#borderskipped) | `string` | Yes | Yes | `'bottom'` -| [`borderWidth`](#styling) | `number` | Yes | Yes | `0` +| [`borderWidth`](#borderwidth) | number|object | Yes | Yes | `0` +| [`borderRadius`](#styling) | `number` | Yes | Yes | `0` | [`data`](#data-structure) | `object[]` | - | - | **required** | [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | - | Yes | `undefined` @@ -97,7 +98,8 @@ The style of each bar can be controlled with the following properties: | `backgroundColor` | The bar background color. | `borderColor` | The bar border color. | [`borderSkipped`](#borderskipped) | The edge to skip when drawing bar. -| `borderWidth` | The bar border width (in pixels). +| [`borderWidth`](#borderwidth) | The bar border width (in pixels). +| `borderRadius` | Rounding corners of outer border edge. All these values, if `undefined`, fallback to the associated [`elements.rectangle.*`](../configuration/elements.md#rectangle-configuration) options. @@ -107,11 +109,18 @@ This setting is used to avoid drawing the bar stroke at the base of the fill. In general, this does not need to be changed except when creating chart types that derive from a bar chart. +**Note:** for negative bars in vertical chart, `top` and `bottom` are flipped. Same goes for `left` and `right` in horizontal chart. + Options are: * `'bottom'` * `'left'` * `'top'` * `'right'` +* `false` + +#### borderWidth + +If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly the `right`, `top` and `bottom` properties can also be specified. Omitted borders and [`borderSkipped`](#borderskipped) are skipped. ### Interactions diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index aef53b68afe..f0cdd1ddc7e 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -158,6 +158,7 @@ module.exports = DatasetController.extend({ borderColor: options.borderColor, borderSkipped: options.borderSkipped, borderWidth: options.borderWidth, + borderRadius: options.borderRadius, datasetLabel: dataset.label, label: me.chart.data.labels[index] }; @@ -399,7 +400,8 @@ module.exports = DatasetController.extend({ 'backgroundColor', 'borderColor', 'borderSkipped', - 'borderWidth' + 'borderWidth', + 'borderRadius' ]; for (i = 0, ilen = keys.length; i < ilen; ++i) { diff --git a/src/elements/element.rectangle.js b/src/elements/element.rectangle.js index fe3702cda4e..741110e29d3 100644 --- a/src/elements/element.rectangle.js +++ b/src/elements/element.rectangle.js @@ -2,8 +2,10 @@ var defaults = require('../core/core.defaults'); var Element = require('../core/core.element'); +var helpers = require('../helpers/index'); var defaultColor = defaults.global.defaultColor; +var valueOrDefault = helpers.valueOrDefault; defaults._set('global', { elements: { @@ -28,22 +30,20 @@ function isVertical(bar) { */ function getBarBounds(bar) { var vm = bar._view; - var x1, x2, y1, y2; + var x1, x2, y1, y2, half; if (isVertical(bar)) { - // vertical - var halfWidth = vm.width / 2; - x1 = vm.x - halfWidth; - x2 = vm.x + halfWidth; + half = vm.width / 2; + x1 = vm.x - half; + x2 = vm.x + half; y1 = Math.min(vm.y, vm.base); y2 = Math.max(vm.y, vm.base); } else { - // horizontal bar - var halfHeight = vm.height / 2; + half = vm.height / 2; x1 = Math.min(vm.x, vm.base); x2 = Math.max(vm.x, vm.base); - y1 = vm.y - halfHeight; - y2 = vm.y + halfHeight; + y1 = vm.y - half; + y2 = vm.y + half; } return { @@ -54,95 +54,200 @@ function getBarBounds(bar) { }; } -module.exports = Element.extend({ - draw: function() { - var ctx = this._chart.ctx; - var vm = this._view; - var left, right, top, bottom, signX, signY, borderSkipped; - var borderWidth = vm.borderWidth; +function parseBorderWidth(value, skipped, maxWidth, maxHeight) { + var t, r, b, l; - if (!vm.horizontal) { - // bar - left = vm.x - vm.width / 2; - right = vm.x + vm.width / 2; - top = vm.y; - bottom = vm.base; - signX = 1; - signY = bottom > top ? 1 : -1; - borderSkipped = vm.borderSkipped || 'bottom'; - } else { - // horizontal bar - left = vm.base; - right = vm.x; - top = vm.y - vm.height / 2; - bottom = vm.y + vm.height / 2; - signX = right > left ? 1 : -1; - signY = 1; - borderSkipped = vm.borderSkipped || 'left'; + if (helpers.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + top: Math.min(maxHeight, skipped === 'top' ? 0 : t), + right: Math.min(maxWidth, skipped === 'right' ? 0 : r), + bottom: Math.min(maxHeight, skipped === 'bottom' ? 0 : b), + left: Math.min(maxWidth, skipped === 'left' ? 0 : l) + }; +} + +function flip(orig, v1, v2) { + return orig === v1 ? v2 : orig === v2 ? v1 : orig; +} + +function parseBorderSkipped(bar) { + var vm = bar._view; + var vertical = isVertical(bar); + var borderSkipped = valueOrDefault(vm.borderSkipped, vertical ? 'bottom' : 'left'); + + if (vertical && vm.base < vm.y) { + borderSkipped = flip(borderSkipped, 'bottom', 'top'); + } + if (!vertical && vm.base > vm.x) { + borderSkipped = flip(borderSkipped, 'left', 'right'); + } + return borderSkipped; +} + +// border can be in 1 or 2 sections. build corners for each border in each section +function buildBorderSections(bounds, inner, border) { + var top = 'top'; + var left = 'left'; + var right = 'right'; + var bottom = 'bottom'; + var borderKeys = [top, right, bottom, left]; + var corners = [[left, top], [right, top], [right, bottom], [left, bottom]]; + var sections = []; + var current = []; + var borderCount = 0; + var startIdx = 0; + var i, idx, nextIdx, c1, c2; + + // start from a skipped border, so we can close the path + for (i = 0; i < 4; ++i) { + if (!border[borderKeys[i]]) { + startIdx = i; + break; } + } - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (borderWidth) { - // borderWidth shold be less than bar width and bar height. - var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); - borderWidth = borderWidth > barSize ? barSize : borderWidth; - var halfStroke = borderWidth / 2; - // Adjust borderWidth when bar top position is near vm.base(zero). - var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); - var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); - var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); - var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); - // not become a vertical line? - if (borderLeft !== borderRight) { - top = borderTop; - bottom = borderBottom; + for (i = 0; i < 4; ++i) { + idx = (i + startIdx) % 4; + if (border[borderKeys[idx]]) { + nextIdx = (i + startIdx + 1) % 4; + c1 = corners[idx]; + c2 = corners[nextIdx]; + // if this is first border, or previous border is skipped, add start corner + // previous index is found by adding 3 to keep the index positive in all cases + if (i === 0 || !border[borderKeys[(i + startIdx + 3) % 4]]) { + if (current.length) { + sections.push({count: borderCount, corners: current}); + current = []; + borderCount = 0; + } + current.push({x1: bounds[c1[0]], y1: bounds[c1[1]], x2: inner[c1[0]], y2: inner[c1[1]]}); } - // not become a horizontal line? - if (borderTop !== borderBottom) { - left = borderLeft; - right = borderRight; + // if this is not last border or next border is skipped, add end corner + if (i < 3 || !border[borderKeys[nextIdx]]) { + current.push({x1: bounds[c2[0]], y1: bounds[c2[1]], x2: inner[c2[0]], y2: inner[c2[1]]}); } + borderCount++; } + } + if (current.length) { + sections.push({count: borderCount, corners: current}); + } + return sections; +} - ctx.beginPath(); - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = borderWidth; - - // Corner points, from bottom-left to bottom-right clockwise - // | 1 2 | - // | 0 3 | - var corners = [ - [left, bottom], - [left, top], - [right, top], - [right, bottom] - ]; - - // Find first (starting) corner with fallback to 'bottom' - var borders = ['bottom', 'left', 'top', 'right']; - var startCorner = borders.indexOf(borderSkipped, 0); - if (startCorner === -1) { - startCorner = 0; +function drawBorderSection(ctx, section, borderRadius) { + ctx.beginPath(); + + var corners = section.corners; + var borderCount = section.count; + var cornerCount = corners.length; + var startCorner = corners[corners.length - 1]; + var endCorner = corners[0]; + var ilen = borderCount === 4 ? 5 : borderCount; + var i; + + // if all borders are drawn and we have a border radius, + // move starting point out ouf the rounded section + if (borderRadius && borderCount === 4) { + // clone so original corner stays intact + endCorner = {x1: endCorner.x1, y1: endCorner.y1, x2: endCorner.x2, y2: endCorner.y2}; + if (endCorner.x1 === startCorner.x1) { + endCorner.x1 += endCorner.x2 > endCorner.x1 ? borderRadius : -borderRadius; } - - function cornerAt(index) { - return corners[(startCorner + index) % 4]; + if (endCorner.y1 === startCorner.y1) { + endCorner.y1 += endCorner.y2 > endCorner.y1 ? borderRadius : -borderRadius; } + } + + // draw outer edge first + ctx.moveTo(endCorner.x1, endCorner.y1); + for (i = 0; i < ilen; i++) { + startCorner = endCorner; + endCorner = corners[(i + 1) % cornerCount]; + ctx.arcTo(startCorner.x1, startCorner.y1, endCorner.x1, endCorner.y1, borderRadius); + } + + // in between edges + if (borderCount < 4) { + ctx.lineTo(endCorner.x1, endCorner.y1); + ctx.lineTo(endCorner.x2, endCorner.y2); + } else { + // all corners are drawn, close the outer edge + startCorner = endCorner; + endCorner = corners[0]; + ctx.closePath(); + ctx.moveTo(endCorner.x2, endCorner.y2); + } + + // draw the inner edge in reverse direction + for (; i > 0; --i) { + startCorner = endCorner; + endCorner = corners[(i - 1) % cornerCount]; + ctx.arcTo(startCorner.x2, startCorner.y2, endCorner.x2, endCorner.y2, 0); + } + ctx.lineTo(endCorner.x2, endCorner.y2); + ctx.closePath(); + ctx.fill('evenodd'); +} - // Draw rectangle from 'startCorner' - var corner = cornerAt(0); - ctx.moveTo(corner[0], corner[1]); +module.exports = Element.extend({ + draw: function() { + var me = this; + var ctx = me._chart.ctx; + var vm = me._view; + var borderWidth = vm.borderWidth; + var bounds = getBarBounds(me); + var left = bounds.left; + var top = bounds.top; + var right = bounds.right; + var bottom = bounds.bottom; + var width = right - left; + var height = bottom - top; + var borderRadius = vm.borderRadius || 0; + var inner, i, sections; - for (var i = 1; i < 4; i++) { - corner = cornerAt(i); - ctx.lineTo(corner[0], corner[1]); + ctx.fillStyle = vm.backgroundColor; + + if (!borderWidth) { + ctx.fillRect(left, top, width, height); + return; } - ctx.fill(); - if (borderWidth) { - ctx.stroke(); + borderWidth = parseBorderWidth( + borderWidth, + parseBorderSkipped(me), + width / 2, + height / 2); + + // limit borderRadius to smallest non zero borderWidth + borderRadius = Math.min( + borderWidth.left || borderRadius, + borderWidth.top || borderRadius, + borderWidth.right || borderRadius, + borderWidth.bottom || borderRadius + ); + + inner = { + left: left + borderWidth.left, + top: top + borderWidth.top, + right: right - borderWidth.right, + bottom: bottom - borderWidth.bottom + }; + + ctx.fillRect(inner.left, inner.top, inner.right - inner.left, inner.bottom - inner.top); + + ctx.fillStyle = vm.borderColor; + sections = buildBorderSections(bounds, inner, borderWidth); + for (i = 0; i < sections.length; i++) { + drawBorderSection(ctx, sections[i], borderRadius); } }, diff --git a/test/fixtures/controller.bar/borderRadius/indexable.js b/test/fixtures/controller.bar/borderRadius/indexable.js new file mode 100644 index 00000000000..60f65f0850b --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/indexable.js @@ -0,0 +1,56 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 5, + borderRadius: [ + 0, + 1, + 2, + 3, + 4, + 5 + ] + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 5, + borderRadius: [ + 5, + 4, + 3, + 2, + 1, + 0 + ] + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/indexable.png b/test/fixtures/controller.bar/borderRadius/indexable.png new file mode 100644 index 00000000000..4ad7ed05c32 Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/indexable.png differ diff --git a/test/fixtures/controller.bar/borderRadius/scriptable.js b/test/fixtures/controller.bar/borderRadius/scriptable.js new file mode 100644 index 00000000000..2ac4d612129 --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/scriptable.js @@ -0,0 +1,54 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 10, + borderRadius: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return Math.abs(value); + } + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5] + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 10, + borderRadius: function(ctx) { + return ctx.dataIndex * 2; + } + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [ + { + display: false, + ticks: { + beginAtZero: true + } + } + ] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/scriptable.png b/test/fixtures/controller.bar/borderRadius/scriptable.png new file mode 100644 index 00000000000..ee3951e5b25 Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/scriptable.png differ diff --git a/test/fixtures/controller.bar/borderRadius/value.js b/test/fixtures/controller.bar/borderRadius/value.js new file mode 100644 index 00000000000..21c9e4a6862 --- /dev/null +++ b/test/fixtures/controller.bar/borderRadius/value.js @@ -0,0 +1,42 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 2, + borderRadius: 4 + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderWidth: 10, + borderRadius: 8 + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderRadius/value.png b/test/fixtures/controller.bar/borderRadius/value.png new file mode 100644 index 00000000000..45b2e7bdb60 Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/value.png differ diff --git a/test/fixtures/controller.bar/borderSkipped/value.js b/test/fixtures/controller.bar/borderSkipped/value.js index 139a2c68905..fed9592636b 100644 --- a/test/fixtures/controller.bar/borderSkipped/value.js +++ b/test/fixtures/controller.bar/borderSkipped/value.js @@ -22,6 +22,11 @@ module.exports = { { // option in element (fallback) data: [0, 5, -10, null], + }, + { + // option in dataset + data: [0, 5, -10, null], + borderSkipped: false } ] }, diff --git a/test/fixtures/controller.bar/borderSkipped/value.png b/test/fixtures/controller.bar/borderSkipped/value.png index 56ef05a41fe..80c7df6be2f 100644 Binary files a/test/fixtures/controller.bar/borderSkipped/value.png and b/test/fixtures/controller.bar/borderSkipped/value.png differ diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.js b/test/fixtures/controller.bar/borderWidth/indexable-object.js new file mode 100644 index 00000000000..97f9af5d46f --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/indexable-object.js @@ -0,0 +1,56 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: [ + {}, + {bottom: 1, left: 1, top: 1, right: 1}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 5, top: 1, right: 5} + ] + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#80808080', + borderSkipped: false, + borderWidth: [ + {bottom: 1, left: 5, top: 1, right: 5}, + {bottom: 1, left: 4, top: 1, right: 4}, + {bottom: 1, left: 3, top: 1, right: 3}, + {bottom: 1, left: 2, top: 1, right: 2}, + {bottom: 1, left: 1, top: 1, right: 1}, + {} + ] + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/indexable-object.png b/test/fixtures/controller.bar/borderWidth/indexable-object.png new file mode 100644 index 00000000000..693806a18da Binary files /dev/null and b/test/fixtures/controller.bar/borderWidth/indexable-object.png differ diff --git a/test/fixtures/controller.bar/borderWidth/object.js b/test/fixtures/controller.bar/borderWidth/object.js new file mode 100644 index 00000000000..9133b30aec5 --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/object.js @@ -0,0 +1,42 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: {bottom: 1, left: 2, top: 3, right: 4} + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#888', + borderSkipped: false, + borderWidth: {bottom: 4, left: 3, top: 2, right: 1} + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/object.png b/test/fixtures/controller.bar/borderWidth/object.png new file mode 100644 index 00000000000..04576006af4 Binary files /dev/null and b/test/fixtures/controller.bar/borderWidth/object.png differ diff --git a/test/fixtures/controller.bar/borderWidth/scriptable-object.js b/test/fixtures/controller.bar/borderWidth/scriptable-object.js new file mode 100644 index 00000000000..5bd2903efa7 --- /dev/null +++ b/test/fixtures/controller.bar/borderWidth/scriptable-object.js @@ -0,0 +1,54 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderWidth: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return {top: Math.abs(value)}; + } + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5] + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: 'transparent', + borderColor: '#80808080', + borderSkipped: false, + borderWidth: function(ctx) { + return {left: ctx.dataIndex * 2}; + } + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [ + { + display: false, + ticks: { + beginAtZero: true + } + } + ] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/borderWidth/scriptable-object.png b/test/fixtures/controller.bar/borderWidth/scriptable-object.png new file mode 100644 index 00000000000..9fa5d45a772 Binary files /dev/null and b/test/fixtures/controller.bar/borderWidth/scriptable-object.png differ diff --git a/test/fixtures/controller.bar/horizontal-borders.js b/test/fixtures/controller.bar/horizontal-borders.js new file mode 100644 index 00000000000..c1f07126d0c --- /dev/null +++ b/test/fixtures/controller.bar/horizontal-borders.js @@ -0,0 +1,42 @@ +module.exports = { + threshold: 0.01, + config: { + type: 'horizontalBar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 2 + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + } + ] + }, + options: { + legend: false, + title: false, + elements: { + rectangle: { + backgroundColor: '#AAAAAA80', + borderColor: '#80808080', + borderWidth: {bottom: 6, left: 15, top: 6, right: 15} + } + }, + scales: { + xAxes: [{display: false}], + yAxes: [{display: false}] + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/horizontal-borders.png b/test/fixtures/controller.bar/horizontal-borders.png new file mode 100644 index 00000000000..85b881cebd2 Binary files /dev/null and b/test/fixtures/controller.bar/horizontal-borders.png differ diff --git a/test/specs/element.rectangle.tests.js b/test/specs/element.rectangle.tests.js index d8d08476f49..d6932359ed9 100644 --- a/test/specs/element.rectangle.tests.js +++ b/test/specs/element.rectangle.tests.js @@ -1,7 +1,7 @@ // Test the rectangle element describe('Rectangle element tests', function() { - it ('Should be constructed', function() { + it('Should be constructed', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -12,7 +12,7 @@ describe('Rectangle element tests', function() { expect(rectangle._index).toBe(1); }); - it ('Should correctly identify as in range', function() { + it('Should correctly identify as in range', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -61,7 +61,7 @@ describe('Rectangle element tests', function() { expect(negativeRectangle.inRange(10, -5)).toBe(true); }); - it ('should get the correct height', function() { + it('should get the correct height', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -93,7 +93,7 @@ describe('Rectangle element tests', function() { expect(negativeRectangle.height()).toBe(5); }); - it ('should get the correct tooltip position', function() { + it('should get the correct tooltip position', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -132,7 +132,7 @@ describe('Rectangle element tests', function() { }); }); - it ('should get the correct vertical area', function() { + it('should get the correct vertical area', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -149,7 +149,7 @@ describe('Rectangle element tests', function() { expect(rectangle.getArea()).toEqual(60); }); - it ('should get the correct horizontal area', function() { + it('should get the correct horizontal area', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -166,7 +166,7 @@ describe('Rectangle element tests', function() { expect(rectangle.getArea()).toEqual(40); }); - it ('should get the center', function() { + it('should get the center', function() { var rectangle = new Chart.elements.Rectangle({ _datasetIndex: 2, _index: 1 @@ -182,173 +182,4 @@ describe('Rectangle element tests', function() { expect(rectangle.getCenterPoint()).toEqual({x: 10, y: 7.5}); }); - - it ('should draw correctly', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _datasetIndex: 2, - _index: 1, - _chart: { - ctx: mockContext, - } - }); - - // Attach a view object as if we were the controller - rectangle._view = { - backgroundColor: 'rgb(255, 0, 0)', - base: 0, - borderColor: 'rgb(0, 0, 255)', - borderWidth: 1, - ctx: mockContext, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - expect(mockContext.getCalls()).toEqual([{ - name: 'beginPath', - args: [], - }, { - name: 'setFillStyle', - args: ['rgb(255, 0, 0)'] - }, { - name: 'setStrokeStyle', - args: ['rgb(0, 0, 255)'], - }, { - name: 'setLineWidth', - args: [1] - }, { - name: 'moveTo', - args: [8.5, 0] - }, { - name: 'lineTo', - args: [8.5, 14.5] // This is a minus bar. Not 15.5 - }, { - name: 'lineTo', - args: [11.5, 14.5] - }, { - name: 'lineTo', - args: [11.5, 0] - }, { - name: 'fill', - args: [], - }, { - name: 'stroke', - args: [] - }]); - }); - - it ('should draw correctly with no stroke', function() { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _datasetIndex: 2, - _index: 1, - _chart: { - ctx: mockContext, - } - }); - - // Attach a view object as if we were the controller - rectangle._view = { - backgroundColor: 'rgb(255, 0, 0)', - base: 0, - borderColor: 'rgb(0, 0, 255)', - ctx: mockContext, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - expect(mockContext.getCalls()).toEqual([{ - name: 'beginPath', - args: [], - }, { - name: 'setFillStyle', - args: ['rgb(255, 0, 0)'] - }, { - name: 'setStrokeStyle', - args: ['rgb(0, 0, 255)'], - }, { - name: 'setLineWidth', - args: [undefined] - }, { - name: 'moveTo', - args: [8, 0] - }, { - name: 'lineTo', - args: [8, 15] - }, { - name: 'lineTo', - args: [12, 15] - }, { - name: 'lineTo', - args: [12, 0] - }, { - name: 'fill', - args: [], - }]); - }); - - function testBorderSkipped(borderSkipped, expectedDrawCalls) { - var mockContext = window.createMockContext(); - var rectangle = new Chart.elements.Rectangle({ - _chart: {ctx: mockContext} - }); - - // Attach a view object as if we were the controller - rectangle._view = { - borderSkipped: borderSkipped, // set tested 'borderSkipped' parameter - ctx: mockContext, - base: 0, - width: 4, - x: 10, - y: 15, - }; - - rectangle.draw(); - - var drawCalls = rectangle._view.ctx.getCalls().splice(4, 4); - expect(drawCalls).toEqual(expectedDrawCalls); - } - - it ('should draw correctly respecting "borderSkipped" == "bottom"', function() { - testBorderSkipped ('bottom', [ - {name: 'moveTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, - ]); - }); - - it ('should draw correctly respecting "borderSkipped" == "left"', function() { - testBorderSkipped ('left', [ - {name: 'moveTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - ]); - }); - - it ('should draw correctly respecting "borderSkipped" == "top"', function() { - testBorderSkipped ('top', [ - {name: 'moveTo', args: [12, 15]}, - {name: 'lineTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - ]); - }); - - it ('should draw correctly respecting "borderSkipped" == "right"', function() { - testBorderSkipped ('right', [ - {name: 'moveTo', args: [12, 0]}, - {name: 'lineTo', args: [8, 0]}, - {name: 'lineTo', args: [8, 15]}, - {name: 'lineTo', args: [12, 15]}, - ]); - }); - });