diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 760fd8887ef..c24767b6789 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -37,7 +37,7 @@ function computeMinSampleSize(scale, pixels) { var prev, curr, i, ilen; for (i = 1, ilen = pixels.length; i < ilen; ++i) { - min = Math.min(min, pixels[i] - pixels[i - 1]); + min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); } for (i = 0, ilen = ticks.length; i < ilen; ++i) { @@ -95,8 +95,8 @@ function computeFlexCategoryTraits(index, ruler, options) { if (prev === null) { // first data: its size is double based on the next point or, - // if it's also the last data, we use the scale end extremity. - prev = curr - (next === null ? ruler.end - curr : next - curr); + // if it's also the last data, we use the scale size. + prev = curr - (next === null ? ruler.end - ruler.start : next - curr); } if (next === null) { @@ -104,8 +104,8 @@ function computeFlexCategoryTraits(index, ruler, options) { next = curr + curr - prev; } - start = curr - ((curr - prev) / 2) * percent; - size = ((next - prev) / 2) * percent; + start = curr - (curr - Math.min(prev, next)) / 2 * percent; + size = Math.abs(next - prev) / 2 * percent; return { chunk: size / ruler.stackCount, diff --git a/src/scales/scale.time.js b/src/scales/scale.time.js index a2892bfd6cd..144fff3a6ae 100644 --- a/src/scales/scale.time.js +++ b/src/scales/scale.time.js @@ -356,34 +356,36 @@ function generate(min, max, capacity, options) { } /** - * Returns the end and start offsets from edges in the form of {start, end}. + * Returns the start and end offsets from edges in the form of {start, end} + * where each value is a relative width to the scale and ranges between 0 and 1. + * They add extra margins on the both sides by scaling down the original scale. * Offsets are added when the `offset` option is true. */ function computeOffsets(table, ticks, min, max, options) { var start = 0; var end = 0; - var upper, lower; + var first, last; if (options.offset && ticks.length) { if (!options.time.min) { - upper = ticks.length > 1 ? ticks[1] : max; - lower = ticks[0]; - start = ( - interpolate(table, 'time', upper, 'pos') - - interpolate(table, 'time', lower, 'pos') - ) / 2; + first = interpolate(table, 'time', ticks[0], 'pos'); + if (ticks.length === 1) { + start = 1 - first; + } else { + start = (interpolate(table, 'time', ticks[1], 'pos') - first) / 2; + } } if (!options.time.max) { - upper = ticks[ticks.length - 1]; - lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; - end = ( - interpolate(table, 'time', upper, 'pos') - - interpolate(table, 'time', lower, 'pos') - ) / 2; + last = interpolate(table, 'time', ticks[ticks.length - 1], 'pos'); + if (ticks.length === 1) { + end = last; + } else { + end = (last - interpolate(table, 'time', ticks[ticks.length - 2], 'pos')) / 2; + } } } - return options.ticks.reverse ? {start: end, end: start} : {start: start, end: end}; + return {start: start, end: end}; } function ticksFromTimestamps(values, majorUnit) { diff --git a/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.json b/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.json new file mode 100644 index 00000000000..a220e537fc1 --- /dev/null +++ b/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.json @@ -0,0 +1,42 @@ +{ + "config": { + "type": "bar", + "data": { + "labels": ["2016", "2018", "2020", "2024", "2030"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [1] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "type": "time", + "display": false, + "barThickness": "flex", + "barPercentage": 1, + "categoryPercentage": 1, + "ticks": { + "source": "labels", + "reverse": true + } + }], + "yAxes": [{ + "display": false, + "ticks": { + "beginAtZero": true + } + }] + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.png b/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.png new file mode 100644 index 00000000000..25a19957b0a Binary files /dev/null and b/test/fixtures/controller.bar/bar-thickness-flex-single-reverse.png differ diff --git a/test/fixtures/controller.bar/bar-thickness-flex-single.json b/test/fixtures/controller.bar/bar-thickness-flex-single.json new file mode 100644 index 00000000000..4ae6973dba6 --- /dev/null +++ b/test/fixtures/controller.bar/bar-thickness-flex-single.json @@ -0,0 +1,41 @@ +{ + "config": { + "type": "bar", + "data": { + "labels": ["2016", "2018", "2020", "2024", "2030"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [1] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "type": "time", + "display": false, + "barThickness": "flex", + "barPercentage": 1, + "categoryPercentage": 1, + "ticks": { + "source": "labels" + } + }], + "yAxes": [{ + "display": false, + "ticks": { + "beginAtZero": true + } + }] + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/bar-thickness-flex-single.png b/test/fixtures/controller.bar/bar-thickness-flex-single.png new file mode 100644 index 00000000000..520accf0469 Binary files /dev/null and b/test/fixtures/controller.bar/bar-thickness-flex-single.png differ diff --git a/test/fixtures/controller.bar/bar-thickness-reverse.json b/test/fixtures/controller.bar/bar-thickness-reverse.json new file mode 100644 index 00000000000..72b335023be --- /dev/null +++ b/test/fixtures/controller.bar/bar-thickness-reverse.json @@ -0,0 +1,47 @@ +{ + "config": { + "type": "bar", + "data": { + "labels": ["2016", "2018", "2020", "2024", "2030"], + "datasets": [{ + "backgroundColor": "#FF6384", + "data": [1, null, 3, 4, 5] + }, { + "backgroundColor": "#36A2EB", + "data": [5, 4, 3, null, 1] + }, { + "backgroundColor": "#FFCE56", + "data": [3, 5, 2, null, 4] + }] + }, + "options": { + "responsive": false, + "legend": false, + "title": false, + "scales": { + "xAxes": [{ + "type": "time", + "display": false, + "barPercentage": 1, + "categoryPercentage": 1, + "ticks": { + "source": "labels", + "reverse": true + } + }], + "yAxes": [{ + "display": false, + "ticks": { + "beginAtZero": true + } + }] + } + } + }, + "options": { + "canvas": { + "height": 256, + "width": 512 + } + } +} diff --git a/test/fixtures/controller.bar/bar-thickness-reverse.png b/test/fixtures/controller.bar/bar-thickness-reverse.png new file mode 100644 index 00000000000..cf6d621cc55 Binary files /dev/null and b/test/fixtures/controller.bar/bar-thickness-reverse.png differ diff --git a/test/specs/scale.time.tests.js b/test/specs/scale.time.tests.js index fc575f3b58e..c0fd1bda93b 100755 --- a/test/specs/scale.time.tests.js +++ b/test/specs/scale.time.tests.js @@ -1320,7 +1320,7 @@ describe('Time scale tests', function() { describe('when ticks.reverse', function() { describe('is "true"', function() { - it ('should reverse the labels', function() { + beforeEach(function() { this.chart = window.acquireChart({ type: 'line', data: { @@ -1346,10 +1346,29 @@ describe('Time scale tests', function() { } } }); + }); + + it ('should reverse the labels', function() { var scale = this.chart.scales.x; expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + scale.width); expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left); }); + + it ('should reverse the bars and add offsets if offset is true', function() { + var chart = this.chart; + var scale = chart.scales.x; + var options = chart.options.scales.xAxes[0]; + + options.offset = true; + chart.update(); + + var numTicks = scale.ticks.length; + var firstTickInterval = scale.getPixelForTick(1) - scale.getPixelForTick(0); + var lastTickInterval = scale.getPixelForTick(numTicks - 1) - scale.getPixelForTick(numTicks - 2); + + expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + scale.width - lastTickInterval / 2); + expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left + firstTickInterval / 2); + }); }); });