base: reset ? yScalePoint : this.calculateBarBase(this.index, index),
width: this.calculateBarWidth(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
+ borderSkipped: rectangle.custom && rectangle.custom.borderSkipped ? rectangle.custom.borderSkipped : this.chart.options.elements.rectangle.borderSkipped,
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth)
}
Chart.defaults.global.elements.rectangle = {
backgroundColor: Chart.defaults.global.defaultColor,
borderWidth: 0,
- borderColor: Chart.defaults.global.defaultColor
+ borderColor: Chart.defaults.global.defaultColor,
+ borderSkipped: 'bottom'
};
Chart.elements.Rectangle = Chart.Element.extend({
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
- // It'd be nice to keep this class totally generic to any rectangle
- // and simply specify which border to miss out.
- ctx.moveTo(leftX, vm.base);
- ctx.lineTo(leftX, top);
- ctx.lineTo(rightX, top);
- ctx.lineTo(rightX, vm.base);
+ // Corner points, from bottom-left to bottom-right clockwise
+ // | 1 2 |
+ // | 0 3 |
+ var corners = [
+ [leftX, vm.base],
+ [leftX, top],
+ [rightX, top],
+ [rightX, vm.base]
+ ];
+
+ // Find first (starting) corner with fallback to 'bottom'
+ var borders = ['bottom', 'left', 'top', 'right'];
+ var startCorner = borders.indexOf(vm.borderSkipped, 0);
+ if (startCorner === -1)
+ startCorner = 0;
+
+ function cornerAt(index) {
+ return corners[(startCorner + index) % 4];
+ }
+
+ // Draw rectangle from 'startCorner'
+ ctx.moveTo.apply(ctx, cornerAt(0));
+ for (var i = 1; i < 4; i++)
+ ctx.lineTo.apply(ctx, cornerAt(i));
+
ctx.fill();
if (vm.borderWidth) {
ctx.stroke();
}]);
});
+ 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] },
+ ]);
+ });
});
\ No newline at end of file