Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reverse support to time scale #5927

Merged
merged 5 commits into from Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
});
});
});
});