Skip to content

Commit

Permalink
Add reverse support to time scale (chartjs#5927)
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle authored and simonbrunel committed Dec 20, 2018
1 parent 784c2dd commit 999881f
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 9 deletions.
24 changes: 15 additions & 9 deletions src/scales/scale.time.js
Expand Up @@ -356,34 +356,34 @@ function generate(min, max, capacity, options) {
}

/**
* Returns the right and left offsets from edges in the form of {left, right}.
* Returns the end and start offsets from edges in the form of {start, end}.
* Offsets are added when the `offset` option is true.
*/
function computeOffsets(table, ticks, min, max, options) {
var left = 0;
var right = 0;
var start = 0;
var end = 0;
var upper, lower;

if (options.offset && ticks.length) {
if (!options.time.min) {
upper = ticks.length > 1 ? ticks[1] : max;
lower = ticks[0];
left = (
start = (
interpolate(table, 'time', upper, 'pos') -
interpolate(table, 'time', lower, 'pos')
) / 2;
}
if (!options.time.max) {
upper = ticks[ticks.length - 1];
lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
right = (
end = (
interpolate(table, 'time', upper, 'pos') -
interpolate(table, 'time', lower, 'pos')
) / 2;
}
}

return {left: left, right: right};
return options.ticks.reverse ? {start: end, end: start} : {start: start, end: end};
}

function ticksFromTimestamps(values, majorUnit) {
Expand Down Expand Up @@ -645,6 +645,10 @@ module.exports = function() {
me._offsets = computeOffsets(me._table, ticks, min, max, options);
me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);

if (options.ticks.reverse) {
ticks.reverse();
}

return ticksFromTimestamps(ticks, me._majorUnit);
},

Expand Down Expand Up @@ -706,11 +710,13 @@ module.exports = function() {
*/
getPixelForOffset: function(time) {
var me = this;
var isReverse = me.options.ticks.reverse;
var size = me._horizontal ? me.width : me.height;
var start = me._horizontal ? me.left : me.top;
var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top;
var pos = interpolate(me._table, 'time', time, 'pos');
var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end);

return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
return isReverse ? start - offset : start + offset;
},

getPixelForValue: function(value, index, datasetIndex) {
Expand Down Expand Up @@ -741,7 +747,7 @@ module.exports = function() {
var me = this;
var size = me._horizontal ? me.width : me.height;
var start = me._horizontal ? me.left : me.top;
var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end;
var time = interpolate(me._table, 'pos', pos, 'time');

return moment(time);
Expand Down
187 changes: 187 additions & 0 deletions test/specs/scale.time.tests.js
Expand Up @@ -1317,4 +1317,191 @@ describe('Time scale tests', function() {
});
});
});

describe('when ticks.reverse', function() {
describe('is "true"', function() {
it ('should reverse the labels', function() {
this.chart = window.acquireChart({
type: 'line',
data: {
labels: ['2017', '2019', '2020', '2025', '2042'],
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
},
options: {
scales: {
xAxes: [{
id: 'x',
type: 'time',
time: {
parser: 'YYYY',
},
ticks: {
source: 'labels',
reverse: true
}
}],
yAxes: [{
display: false
}]
}
}
});
var scale = this.chart.scales.x;
expect(scale.getPixelForValue('2017')).toBeCloseToPixel(scale.left + scale.width);
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(scale.left);
});
});
});

describe('when ticks.reverse is "true" and distribution', function() {
describe('is "series"', function() {
beforeEach(function() {
this.chart = window.acquireChart({
type: 'line',
data: {
labels: ['2017', '2019', '2020', '2025', '2042'],
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
},
options: {
scales: {
xAxes: [{
id: 'x',
type: 'time',
time: {
parser: 'YYYY'
},
distribution: 'series',
ticks: {
source: 'labels',
reverse: true
}
}],
yAxes: [{
display: false
}]
}
}
});
});

it ('should reverse the labels and space data out with the same gap, whatever their time values', function() {
var scale = this.chart.scales.x;
var start = scale.left;
var slice = scale.width / 4;

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * 4);
expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice * 3);
expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * 2);
expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice);
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start);
});

it ('should reverse the labels and should add a step before if scale.min is before the first data', function() {
var chart = this.chart;
var scale = chart.scales.x;
var options = chart.options.scales.xAxes[0];

options.time.min = '2012';
chart.update();

var start = scale.left;
var slice = scale.width / 5;

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * 4);
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start);
});

it ('should reverse the labels and should add a step after if scale.max is after the last data', function() {
var chart = this.chart;
var scale = chart.scales.x;
var options = chart.options.scales.xAxes[0];

options.time.max = '2050';
chart.update();

var start = scale.left;
var slice = scale.width / 5;

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * 5);
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice);
});

it ('should reverse the labels and should add steps before and after if scale.min/max are outside the data range', function() {
var chart = this.chart;
var scale = chart.scales.x;
var options = chart.options.scales.xAxes[0];

options.time.min = '2012';
options.time.max = '2050';
chart.update();

var start = scale.left;
var slice = scale.width / 6;

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * 5);
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice);
});
});
describe('is "linear"', function() {
beforeEach(function() {
this.chart = window.acquireChart({
type: 'line',
data: {
labels: ['2017', '2019', '2020', '2025', '2042'],
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
},
options: {
scales: {
xAxes: [{
id: 'x',
type: 'time',
time: {
parser: 'YYYY'
},
distribution: 'linear',
ticks: {
source: 'labels',
reverse: true
}
}],
yAxes: [{
display: false
}]
}
}
});
});

it ('should reverse the labels and should space data out with a gap relative to their time values', function() {
var scale = this.chart.scales.x;
var start = scale.left;
var slice = scale.width / (2042 - 2017);

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * (2042 - 2017));
expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice * (2042 - 2019));
expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * (2042 - 2020));
expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice * (2042 - 2025));
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start);
});

it ('should reverse the labels and should take in account scale min and max if outside the ticks range', function() {
var chart = this.chart;
var scale = chart.scales.x;
var options = chart.options.scales.xAxes[0];

options.time.min = '2012';
options.time.max = '2050';
chart.update();

var start = scale.left;
var slice = scale.width / (2050 - 2012);

expect(scale.getPixelForValue('2017')).toBeCloseToPixel(start + slice * (2050 - 2017));
expect(scale.getPixelForValue('2019')).toBeCloseToPixel(start + slice * (2050 - 2019));
expect(scale.getPixelForValue('2020')).toBeCloseToPixel(start + slice * (2050 - 2020));
expect(scale.getPixelForValue('2025')).toBeCloseToPixel(start + slice * (2050 - 2025));
expect(scale.getPixelForValue('2042')).toBeCloseToPixel(start + slice * (2050 - 2042));
});
});
});
});

0 comments on commit 999881f

Please sign in to comment.