diff --git a/docs/01-Chart-Configuration.md b/docs/01-Chart-Configuration.md index c08e23d44..c12a102af 100644 --- a/docs/01-Chart-Configuration.md +++ b/docs/01-Chart-Configuration.md @@ -85,6 +85,15 @@ onClick | Function | null | Called if the event is of type 'mouseup' or 'click'. legendCallback | Function | ` function (chart) { }` | Function to generate a legend. Receives the chart object to generate a legend from. Default implementation returns an HTML string. onResize | Function | null | Called when a resize occurs. Gets passed two arguments: the chart instance and the new size. +### Layout Configuration + +The layout configuration is passed into the `options.layout` namespace. The global options for the chart layout is defined in `Chart.defaults.global.layout`. + +Name | Type | Default | Description +--- | --- | --- | --- +padding | Number or Object | 0 | The padding to add inside the chart. If this value is a number, it is applied to all sides of the chart (left, top, right, bottom). If this value is an object, the `left` property defines the left padding. Similarly the `right`, `top`, and `bottom` properties can also be specified. + + ### Title Configuration The title configuration is passed into the `options.title` namespace. The global options for the chart title is defined in `Chart.defaults.global.title`. diff --git a/gulpfile.js b/gulpfile.js index 58b8bf8fa..530ae2101 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -39,11 +39,7 @@ var preTestFiles = [ ]; var testFiles = [ - './test/*.js', - - // Disable tests which need to be rewritten based on changes introduced by - // the following changes: https://github.com/chartjs/Chart.js/pull/2346 - '!./test/core.layoutService.tests.js', + './test/*.js' ]; gulp.task('bower', bowerTask); diff --git a/src/core/core.layoutService.js b/src/core/core.layoutService.js index 7336deac0..220a25344 100644 --- a/src/core/core.layoutService.js +++ b/src/core/core.layoutService.js @@ -32,8 +32,26 @@ module.exports = function(Chart) { return; } - var xPadding = 0; - var yPadding = 0; + var layoutOptions = chartInstance.options.layout; + var padding = layoutOptions ? layoutOptions.padding : null; + + var leftPadding = 0; + var rightPadding = 0; + var topPadding = 0; + var bottomPadding = 0; + + if (!isNaN(padding)) { + // options.layout.padding is a number. assign to all + leftPadding = padding; + rightPadding = padding; + topPadding = padding; + bottomPadding = padding; + } else { + leftPadding = padding.left || 0; + rightPadding = padding.right || 0; + topPadding = padding.top || 0; + bottomPadding = padding.bottom || 0; + } var leftBoxes = helpers.where(chartInstance.boxes, function(box) { return box.options.position === 'left'; @@ -99,8 +117,8 @@ module.exports = function(Chart) { // 9. Tell any axes that overlay the chart area the positions of the chart area // Step 1 - var chartWidth = width - (2 * xPadding); - var chartHeight = height - (2 * yPadding); + var chartWidth = width - leftPadding - rightPadding; + var chartHeight = height - topPadding - bottomPadding; var chartAreaWidth = chartWidth / 2; // min 50% var chartAreaHeight = chartHeight / 2; // min 50% @@ -140,10 +158,10 @@ module.exports = function(Chart) { // be if the axes are drawn at their minimum sizes. // Steps 5 & 6 - var totalLeftBoxesWidth = xPadding; - var totalRightBoxesWidth = xPadding; - var totalTopBoxesHeight = yPadding; - var totalBottomBoxesHeight = yPadding; + var totalLeftBoxesWidth = leftPadding; + var totalRightBoxesWidth = rightPadding; + var totalTopBoxesHeight = topPadding; + var totalBottomBoxesHeight = bottomPadding; // Function to fit a box function fitBox(box) { @@ -213,10 +231,10 @@ module.exports = function(Chart) { helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) - totalLeftBoxesWidth = xPadding; - totalRightBoxesWidth = xPadding; - totalTopBoxesHeight = yPadding; - totalBottomBoxesHeight = yPadding; + totalLeftBoxesWidth = leftPadding; + totalRightBoxesWidth = rightPadding; + totalTopBoxesHeight = topPadding; + totalBottomBoxesHeight = bottomPadding; helpers.each(leftBoxes, function(box) { totalLeftBoxesWidth += box.width; @@ -265,13 +283,13 @@ module.exports = function(Chart) { } // Step 7 - Position the boxes - var left = xPadding; - var top = yPadding; + var left = leftPadding; + var top = topPadding; function placeBox(box) { if (box.isHorizontal()) { - box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth; - box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth; + box.left = box.options.fullWidth ? leftPadding : totalLeftBoxesWidth; + box.right = box.options.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; box.top = top; box.bottom = top + box.height; diff --git a/test/core.layoutService.tests.js b/test/core.layoutService.tests.js index 4fa1039d5..fcc2b7dcb 100644 --- a/test/core.layoutService.tests.js +++ b/test/core.layoutService.tests.js @@ -1,6 +1,9 @@ // Tests of the scale service describe('Test the layout service', function() { - it('should fit a simple chart with 2 scales', function() { + // Disable tests which need to be rewritten based on changes introduced by + // the following changes: https://github.com/chartjs/Chart.js/pull/2346 + // using xit marks the test as pending: http://jasmine.github.io/2.0/introduction.html#section-Pending_Specs + xit('should fit a simple chart with 2 scales', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -48,7 +51,7 @@ describe('Test the layout service', function() { expect(chart.scales.yScale.labelRotation).toBeCloseTo(0); }); - it('should fit scales that are in the top and right positions', function() { + xit('should fit scales that are in the top and right positions', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -124,7 +127,7 @@ describe('Test the layout service', function() { expect(chart.scale.height).toBeCloseToPixel(480); }); - it('should fit multiple axes in the same position', function() { + xit('should fit multiple axes in the same position', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -185,7 +188,7 @@ describe('Test the layout service', function() { expect(chart.scales.yScale2.labelRotation).toBeCloseTo(0); }); - it ('should fix a full width box correctly', function() { + xit ('should fix a full width box correctly', function() { var chart = window.acquireChart({ type: 'bar', data: { @@ -239,4 +242,146 @@ describe('Test the layout service', function() { expect(chart.scales.yScale.right).toBeCloseToPixel(45); expect(chart.scales.yScale.top).toBeCloseToPixel(60); }); + + describe('padding settings', function() { + it('should apply a single padding to all dimensions', function() { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [ + { data: [10, 5, 0, 25, 78, -10] } + ], + labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6'] + }, + options: { + scales: { + xAxes: [{ + id: 'xScale', + type: 'category', + display: false + }], + yAxes: [{ + id: 'yScale', + type: 'linear', + display: false + }] + }, + legend: { + display: false + }, + title: { + display: false + }, + layout: { + padding: 10 + } + } + }, { + canvas: { + height: 150, + width: 250 + } + }); + + expect(chart.chartArea.bottom).toBeCloseToPixel(140); + expect(chart.chartArea.left).toBeCloseToPixel(10); + expect(chart.chartArea.right).toBeCloseToPixel(240); + expect(chart.chartArea.top).toBeCloseToPixel(10); + }); + + it('should apply padding in all positions', function() { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [ + { data: [10, 5, 0, 25, 78, -10] } + ], + labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6'] + }, + options: { + scales: { + xAxes: [{ + id: 'xScale', + type: 'category', + display: false + }], + yAxes: [{ + id: 'yScale', + type: 'linear', + display: false + }] + }, + legend: { + display: false + }, + title: { + display: false + }, + layout: { + padding: { + left: 5, + right: 15, + top: 8, + bottom: 12 + } + } + } + }, { + canvas: { + height: 150, + width: 250 + } + }); + + expect(chart.chartArea.bottom).toBeCloseToPixel(138); + expect(chart.chartArea.left).toBeCloseToPixel(5); + expect(chart.chartArea.right).toBeCloseToPixel(235); + expect(chart.chartArea.top).toBeCloseToPixel(8); + }); + + it('should default to 0 padding if no dimensions specified', function() { + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [ + { data: [10, 5, 0, 25, 78, -10] } + ], + labels: ['tick1', 'tick2', 'tick3', 'tick4', 'tick5', 'tick6'] + }, + options: { + scales: { + xAxes: [{ + id: 'xScale', + type: 'category', + display: false + }], + yAxes: [{ + id: 'yScale', + type: 'linear', + display: false + }] + }, + legend: { + display: false + }, + title: { + display: false + }, + layout: { + padding: {} + } + } + }, { + canvas: { + height: 150, + width: 250 + } + }); + + expect(chart.chartArea.bottom).toBeCloseToPixel(150); + expect(chart.chartArea.left).toBeCloseToPixel(0); + expect(chart.chartArea.right).toBeCloseToPixel(250); + expect(chart.chartArea.top).toBeCloseToPixel(0); + }); + }); });