setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
+ // Reset position before calculating rotation
this.width = this.maxWidth;
+ this.left = 0;
+ this.right = this.width;
} else {
this.height = this.maxHeight;
+
+ // Reset position before calculating rotation
+ this.top = 0;
+ this.bottom = this.height
}
// Reset padding
expect(bar1._xScale).toBe(chart.scales.firstXScaleID);
expect(bar1._yScale).toBe(chart.scales.firstYScaleID);
expect(bar1._model).toEqual({
- x: 106.80000000000003,
+ x: 113.60000000000001,
y: 194,
label: 'label1',
datasetLabel: 'dataset2',
base: 194,
- width: 12.240000000000002,
+ width: 13.680000000000001,
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
expect(bar2._xScale).toBe(chart.scales.firstXScaleID);
expect(bar2._yScale).toBe(chart.scales.firstYScaleID);
expect(bar2._model).toEqual({
- x: 140.8,
+ x: 151.60000000000002,
y: -15,
label: 'label2',
datasetLabel: 'dataset2',
base: 194,
- width: 12.240000000000002,
+ width: 13.680000000000001,
backgroundColor: 'rgb(255, 0, 0)',
borderColor: 'rgb(0, 0, 255)',
borderWidth: 2,
tension: 0.1,
// Point
- x: 63,
+ x: 71,
y: 62,
// Control points
- controlPointPreviousX: 63,
+ controlPointPreviousX: 71,
controlPointPreviousY: 62,
- controlPointNextX: 67.5,
+ controlPointNextX: 76,
controlPointNextY: 57.3,
});
tension: 0.1,
// Point
- x: 108,
+ x: 121,
y: 15,
// Control points
- controlPointPreviousX: 105.27827106822767,
- controlPointPreviousY: 12.125364948465183,
- controlPointNextX: 114.17827106822767,
- controlPointNextY: 21.52536494846518,
+ controlPointPreviousX: 117.82889384189087,
+ controlPointPreviousY: 12.04867347661131,
+ controlPointNextX: 127.92889384189088,
+ controlPointNextY: 21.44867347661131,
});
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
tension: 0.1,
// Point
- x: 152,
+ x: 172,
y: 156,
// Control points
- controlPointPreviousX: 145.63719249781943,
- controlPointPreviousY: 143.20289277651324,
- controlPointNextX: 154.53719249781943,
- controlPointNextY: 161.10289277651324,
+ controlPointPreviousX: 164.8815225337256,
+ controlPointPreviousY: 143.38408449046415,
+ controlPointNextX: 174.98152253372558,
+ controlPointNextY: 161.28408449046415,
});
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
tension: 0.1,
// Point
- x: 197,
+ x: 222,
y: 194,
// Control points
- controlPointPreviousX: 192.5,
+ controlPointPreviousX: 217,
controlPointPreviousY: 190.2,
- controlPointNextX: 197,
+ controlPointNextX: 222,
controlPointNextY: 194,
});
tension: 0.2,
// Point
- x: 63,
+ x: 71,
y: 62,
// Control points
- controlPointPreviousX: 63,
+ controlPointPreviousX: 71,
controlPointPreviousY: 62,
- controlPointNextX: 72,
+ controlPointNextX: 81,
controlPointNextY: 52.6,
});
tension: 0.2,
// Point
- x: 108,
+ x: 121,
y: 15,
// Control points
- controlPointPreviousX: 102.55654213645535,
- controlPointPreviousY: 9.250729896930364,
- controlPointNextX: 120.35654213645535,
- controlPointNextY: 28.050729896930367,
+ controlPointPreviousX: 114.65778768378175,
+ controlPointPreviousY: 9.097346953222619,
+ controlPointNextX: 134.85778768378177,
+ controlPointNextY: 27.897346953222623,
});
expect(chart.data.datasets[0].metaData[2]._model).toEqual({
tension: 0.2,
// Point
- x: 152,
+ x: 172,
y: 156,
// Control points
- controlPointPreviousX: 139.27438499563885,
- controlPointPreviousY: 130.40578555302648,
- controlPointNextX: 157.07438499563887,
- controlPointNextY: 166.20578555302646,
+ controlPointPreviousX: 157.76304506745115,
+ controlPointPreviousY: 130.76816898092827,
+ controlPointNextX: 177.96304506745116,
+ controlPointNextY: 166.56816898092828,
});
expect(chart.data.datasets[0].metaData[3]._model).toEqual({
tension: 0.2,
// Point
- x: 197,
+ x: 222,
y: 194,
// Control points
- controlPointPreviousX: 188,
+ controlPointPreviousX: 212,
controlPointPreviousY: 186.4,
- controlPointNextX: 197,
+ controlPointNextX: 222,
controlPointNextY: 194,
});
tension: 0.15,
// Point
- x: 63,
+ x: 71,
y: 62,
// Control points
- controlPointPreviousX: 63,
+ controlPointPreviousX: 71,
controlPointPreviousY: 62,
- controlPointNextX: 69.75,
+ controlPointNextX: 78.5,
controlPointNextY: 54.95,
});
});
--- /dev/null
+// Tests of the scale service
+describe('Test the scale service', function() {
+ it('should fit a simple chart with 2 scales', function() {
+ var chartInstance = {
+ scales: [],
+ };
+
+ var xScaleID = 'xScale';
+ var yScaleID = 'yScale';
+ var mockData = {
+ datasets: [{
+ yAxisID: yScaleID,
+ data: [10, 5, 0, 25, 78, -10]
+ }],
+ labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
+ };
+ var mockContext = window.createMockContext();
+
+ var xScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
+ var XConstructor = Chart.scaleService.getScaleConstructor('category');
+ var xScale = new XConstructor({
+ ctx: mockContext,
+ options: xScaleConfig,
+ data: mockData,
+ id: xScaleID
+ });
+
+ var yScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
+ var YConstructor = Chart.scaleService.getScaleConstructor('linear');
+ var yScale = new YConstructor({
+ ctx: mockContext,
+ options: yScaleConfig,
+ data: mockData,
+ id: yScaleID
+ });
+
+ chartInstance.scales.push(xScale);
+ chartInstance.scales.push(yScale);
+
+ var canvasWidth = 250;
+ var canvasHeight = 150;
+ Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
+
+ expect(chartInstance.chartArea).toEqual({
+ left: 45,
+ right: 245,
+ top: 5,
+ bottom: 76.0423977855504,
+ });
+
+ // Is xScale at the right spot
+ expect(xScale.left).toBe(45);
+ expect(xScale.right).toBe(245);
+ expect(xScale.top).toBe(76.0423977855504);
+ expect(xScale.bottom).toBe(145);
+ expect(xScale.labelRotation).toBe(55);
+
+ // Is yScale at the right spot
+ expect(yScale.left).toBe(5);
+ expect(yScale.right).toBe(45);
+ expect(yScale.top).toBe(5);
+ expect(yScale.bottom).toBe(76.0423977855504);
+ });
+
+ it('should fit scales that are in the top and right positions', function() {
+ var chartInstance = {
+ scales: [],
+ };
+
+ var xScaleID = 'xScale';
+ var yScaleID = 'yScale';
+ var mockData = {
+ datasets: [{
+ yAxisID: yScaleID,
+ data: [10, 5, 0, 25, 78, -10]
+ }],
+ labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
+ };
+ var mockContext = window.createMockContext();
+
+ var xScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
+ xScaleConfig.position = 'top';
+ var XConstructor = Chart.scaleService.getScaleConstructor('category');
+ var xScale = new XConstructor({
+ ctx: mockContext,
+ options: xScaleConfig,
+ data: mockData,
+ id: xScaleID
+ });
+
+ var yScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
+ yScaleConfig.position = 'right';
+ var YConstructor = Chart.scaleService.getScaleConstructor('linear');
+ var yScale = new YConstructor({
+ ctx: mockContext,
+ options: yScaleConfig,
+ data: mockData,
+ id: yScaleID
+ });
+
+ chartInstance.scales.push(xScale);
+ chartInstance.scales.push(yScale);
+
+ var canvasWidth = 250;
+ var canvasHeight = 150;
+ Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
+
+ expect(chartInstance.chartArea).toEqual({
+ left: 5,
+ right: 205,
+ top: 73.9576022144496,
+ bottom: 145,
+ });
+
+ // Is xScale at the right spot
+ expect(xScale.left).toBe(5);
+ expect(xScale.right).toBe(205);
+ expect(xScale.top).toBe(5);
+ expect(xScale.bottom).toBe(73.9576022144496);
+ expect(xScale.labelRotation).toBe(55);
+
+ // Is yScale at the right spot
+ expect(yScale.left).toBe(205);
+ expect(yScale.right).toBe(245);
+ expect(yScale.top).toBe(73.9576022144496);
+ expect(yScale.bottom).toBe(145);
+ });
+
+ it('should fit multiple axes in the same position', function() {
+ var chartInstance = {
+ scales: [],
+ };
+
+ var xScaleID = 'xScale';
+ var yScaleID1 = 'yScale1';
+ var yScaleID2 = 'yScale2';
+ var mockData = {
+ datasets: [{
+ yAxisID: yScaleID1,
+ data: [10, 5, 0, 25, 78, -10]
+ }, {
+ yAxisID: yScaleID2,
+ data: [-19, -20, 0, -99, -50, 0]
+ }],
+ labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
+ };
+ var mockContext = window.createMockContext();
+
+ var xScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('category'));
+ var XConstructor = Chart.scaleService.getScaleConstructor('category');
+ var xScale = new XConstructor({
+ ctx: mockContext,
+ options: xScaleConfig,
+ data: mockData,
+ id: xScaleID
+ });
+
+ var yScaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
+ var YConstructor = Chart.scaleService.getScaleConstructor('linear');
+ var yScale1 = new YConstructor({
+ ctx: mockContext,
+ options: yScaleConfig,
+ data: mockData,
+ id: yScaleID1
+ });
+ var yScale2 = new YConstructor({
+ ctx: mockContext,
+ options: yScaleConfig,
+ data: mockData,
+ id: yScaleID2
+ });
+
+ chartInstance.scales.push(xScale);
+ chartInstance.scales.push(yScale1);
+ chartInstance.scales.push(yScale2);
+
+ var canvasWidth = 250;
+ var canvasHeight = 150;
+ Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
+
+ expect(chartInstance.chartArea).toEqual({
+ left: 95,
+ right: 245,
+ top: 5,
+ bottom: 70.01536896070459,
+ });
+
+ // Is xScale at the right spot
+ expect(xScale.left).toBe(95);
+ expect(xScale.right).toBe(245);
+ expect(xScale.top).toBe(70.01536896070459);
+ expect(xScale.bottom).toBe(145);
+
+ // Are yScales at the right spot
+ expect(yScale1.left).toBe(5);
+ expect(yScale1.right).toBe(45);
+ expect(yScale1.top).toBe(5);
+ expect(yScale1.bottom).toBe(70.01536896070459);
+
+ expect(yScale2.left).toBe(45);
+ expect(yScale2.right).toBe(95);
+ expect(yScale2.top).toBe(5);
+ expect(yScale2.bottom).toBe(70.01536896070459);
+ });
+
+ // This is an oddball case. What happens is, when the scales are fit the first time they must fit within the assigned size. In this case,
+ // the labels on the xScale need to rotate to fit. However, when the scales are fit again after the width of the left axis is determined,
+ // the labels do not need to rotate. Previously, the chart was too small because the chartArea did not expand to take up the space freed up
+ // due to the lack of label rotation
+ it('should fit scales that overlap the chart area', function() {
+ var chartInstance = {
+ scales: [],
+ };
+
+ var scaleID = 'scaleID';
+ var mockData = {
+ datasets: [{
+ yAxisID: scaleID,
+ data: [10, 5, 0, 25, 78, -10]
+ }, {
+ yAxisID: scaleID,
+ data: [-19, -20, 0, -99, -50, 0]
+ }],
+ labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5']
+ };
+ var mockContext = window.createMockContext();
+
+ var scaleConfig = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('radialLinear'));
+ var ScaleConstructor = Chart.scaleService.getScaleConstructor('radialLinear');
+ var scale = new ScaleConstructor({
+ ctx: mockContext,
+ options: scaleConfig,
+ data: mockData,
+ id: scaleID
+ });
+
+ chartInstance.scales.push(scale);
+
+ var canvasWidth = 300;
+ var canvasHeight = 350;
+ Chart.scaleService.update(chartInstance, canvasWidth, canvasHeight);
+
+ expect(chartInstance.chartArea).toEqual({
+ left: 5,
+ right: 295,
+ top: 5,
+ bottom: 345,
+ });
+
+ expect(scale.left).toBe(5);
+ expect(scale.right).toBe(295);
+ expect(scale.top).toBe(5);
+ expect(scale.bottom).toBe(345);
+ expect(scale.width).toBe(290);
+ expect(scale.height).toBe(340)
+ });
+});
\ No newline at end of file
id: scaleID
});
- var minSize = scale.update(200, 100);
+ var minSize = scale.update(600, 100);
- expect(scale.width).toBe(200);
+ expect(scale.width).toBe(600);
expect(scale.height).toBe(28);
expect(scale.paddingTop).toBe(0);
expect(scale.paddingBottom).toBe(0);
expect(scale.labelRotation).toBe(0);
expect(minSize).toEqual({
- width: 200,
+ width: 600,
height: 28,
});
scale.left = 5;
scale.top = 5;
- scale.right = 205;
+ scale.right = 605;
scale.bottom = 33;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
- expect(scale.getPixelForValue(0, 0, 0, true)).toBe(45);
+ expect(scale.getPixelForValue(0, 0, 0, true)).toBe(85);
- expect(scale.getPixelForValue(0, 4, 0, false)).toBe(132);
- expect(scale.getPixelForValue(0, 4, 0, true)).toBe(145);
+ expect(scale.getPixelForValue(0, 4, 0, false)).toBe(452);
+ expect(scale.getPixelForValue(0, 4, 0, true)).toBe(505);
config.gridLines.offsetGridLines = false;
expect(scale.getPixelForValue(0, 0, 0, false)).toBe(33);
expect(scale.getPixelForValue(0, 0, 0, true)).toBe(33);
- expect(scale.getPixelForValue(0, 4, 0, false)).toBe(157);
- expect(scale.getPixelForValue(0, 4, 0, true)).toBe(157);
+ expect(scale.getPixelForValue(0, 4, 0, false)).toBe(557);
+ expect(scale.getPixelForValue(0, 4, 0, true)).toBe(557);
});
it ('should get the correct pixel for a value when vertical', function() {
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('linear'));
config.position = "bottom";
var Constructor = Chart.scaleService.getScaleConstructor('linear');
- var verticalScale = new Constructor({
+ var horizontalScale = new Constructor({
ctx: mockContext,
options: config,
data: mockData,
id: scaleID
});
- var minSize = verticalScale.update(100, 300);
+ var minSize = horizontalScale.update(200, 300);
expect(minSize).toEqual({
- width: 100,
+ width: 200,
height: 28,
});
- expect(verticalScale.width).toBe(100);
- expect(verticalScale.height).toBe(28);
- expect(verticalScale.paddingTop).toBe(0);
- expect(verticalScale.paddingBottom).toBe(0);
- expect(verticalScale.paddingLeft).toBe(18);
- expect(verticalScale.paddingRight).toBe(13);
+ expect(horizontalScale.width).toBe(200);
+ expect(horizontalScale.height).toBe(28);
+ expect(horizontalScale.paddingTop).toBe(0);
+ expect(horizontalScale.paddingBottom).toBe(0);
+ expect(horizontalScale.paddingLeft).toBe(13);
+ expect(horizontalScale.paddingRight).toBe(8);
+ expect(horizontalScale.labelRotation).toBe(0);
// Refit with margins to see the padding go away
- minSize = verticalScale.update(100, 28, {
+ minSize = horizontalScale.update(200, 28, {
left: 10,
right: 6,
top: 15,
bottom: 3
});
expect(minSize).toEqual({
- width: 100,
+ width: 200,
height: 28,
});
- expect(verticalScale.paddingTop).toBe(0);
- expect(verticalScale.paddingBottom).toBe(0);
- expect(verticalScale.paddingLeft).toBe(8);
- expect(verticalScale.paddingRight).toBe(7);
+ expect(horizontalScale.paddingTop).toBe(0);
+ expect(horizontalScale.paddingBottom).toBe(0);
+ expect(horizontalScale.paddingLeft).toBe(3);
+ expect(horizontalScale.paddingRight).toBe(2);
// Extra size when scale label showing
config.scaleLabel.show = true;
- minSize = verticalScale.update(100, 300);
+ minSize = horizontalScale.update(200, 300);
expect(minSize).toEqual({
- width: 100,
+ width: 200,
height: 46,
});
});
id: scaleID
});
- var minSize = horizontalScale.update(100, 300);
- minSize = horizontalScale.update(100, 28, {
+ var minSize = horizontalScale.update(200, 300);
+ minSize = horizontalScale.update(200, 28, {
left: 10,
right: 6,
top: 15,
horizontalScale.left = 0;
horizontalScale.right = minSize.width;
- horizontalScale.top = 0;
- horizontalScale.bottom = minSize.height;
+ horizontalScale.top = 100;
+ horizontalScale.bottom = 100 + minSize.height;
var chartArea = {
- top: 100,
- bottom: 0,
+ top: 0,
+ bottom: 100,
left: 0,
right: minSize.width
};
"args": []
}, {
"name": "moveTo",
- "args": [8.5, 0]
+ "args": [3.5, 100]
}, {
"name": "lineTo",
- "args": [8.5, 10]
+ "args": [3.5, 110]
}, {
"name": "moveTo",
- "args": [8.5, 100]
+ "args": [3.5, 0]
}, {
"name": "lineTo",
- "args": [8.5, 0]
+ "args": [3.5, 100]
}, {
"name": "stroke",
"args": []
"args": []
}, {
"name": "translate",
- "args": [8, 10]
+ "args": [3, 110]
}, {
"name": "rotate",
"args": [-0]
}, {
"name": "fillText",
- "args": ["-10", 0, 0]
+ "args": ["-5", 0, 0]
}, {
"name": "restore",
"args": []
"args": []
}, {
"name": "moveTo",
- "args": [51.5, 0]
+ "args": [101.5, 100]
}, {
"name": "lineTo",
- "args": [51.5, 10]
+ "args": [101.5, 110]
}, {
"name": "moveTo",
- "args": [51.5, 100]
+ "args": [101.5, 0]
}, {
"name": "lineTo",
- "args": [51.5, 0]
+ "args": [101.5, 100]
}, {
"name": "stroke",
"args": []
"args": []
}, {
"name": "translate",
- "args": [51, 10]
+ "args": [101, 110]
}, {
"name": "rotate",
"args": [-0]
"args": []
}, {
"name": "moveTo",
- "args": [93.5, 0]
+ "args": [198.5, 100]
}, {
"name": "lineTo",
- "args": [93.5, 10]
+ "args": [198.5, 110]
}, {
"name": "moveTo",
- "args": [93.5, 100]
+ "args": [198.5, 0]
}, {
"name": "lineTo",
- "args": [93.5, 0]
+ "args": [198.5, 100]
}, {
"name": "stroke",
"args": []
"args": []
}, {
"name": "translate",
- "args": [93, 10]
+ "args": [198, 110]
}, {
"name": "rotate",
"args": [-0]
}, {
"name": "fillText",
- "args": ["10", 0, 0]
+ "args": ["5", 0, 0]
}, {
"name": "restore",
"args": []
"args": []
}, {
"name": "fillText",
- "args": ["myLabel", 50, 22]
+ "args": ["myLabel", 100, 122]
}]);
// Turn off display