mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 16:26:52 +01:00
New time scale ticks.bounds option (#4556)
`ticks.bounds` (`'data'`(default)|`'label'`): `data` preserves the data range while `labels` ensures that all labels are visible. This option is bypassed by the min/max time options. Remove the useless time scale `_model` object containing private members: instead, make these members private (prefixed by `_`) part of the scale.
This commit is contained in:
@@ -336,8 +336,33 @@ module.exports = function(Chart) {
|
||||
},
|
||||
ticks: {
|
||||
autoSkip: false,
|
||||
mode: 'linear', // 'linear|series'
|
||||
source: 'auto' // 'auto|labels'
|
||||
|
||||
/**
|
||||
* Ticks distribution along the scale:
|
||||
* - 'linear': ticks and data are spread according to their time (distances can vary),
|
||||
* - 'series': ticks and data are spread at the same distance from each other.
|
||||
* @see https://github.com/chartjs/Chart.js/pull/4507
|
||||
* @since 2.7.0
|
||||
*/
|
||||
mode: 'linear',
|
||||
|
||||
/**
|
||||
* Ticks generation input values:
|
||||
* - 'labels': generates ticks from user given `data.labels` values ONLY.
|
||||
* - 'auto': generates "optimal" ticks based on scale size and time options.
|
||||
* @see https://github.com/chartjs/Chart.js/pull/4507
|
||||
* @since 2.7.0
|
||||
*/
|
||||
source: 'auto',
|
||||
|
||||
/**
|
||||
* Ticks boundary strategy (bypassed by min/max time options)
|
||||
* - `data`: make sure data are fully visible, labels outside are removed
|
||||
* - `labels`: make sure labels are fully visible, data outside are truncated
|
||||
* @see https://github.com/chartjs/Chart.js/pull/4556
|
||||
* @since 2.7.0
|
||||
*/
|
||||
bounds: 'data'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -383,8 +408,8 @@ module.exports = function(Chart) {
|
||||
var chart = me.chart;
|
||||
var options = me.options;
|
||||
var datasets = chart.data.datasets || [];
|
||||
var min = MAX_INTEGER;
|
||||
var max = MIN_INTEGER;
|
||||
var min = parse(options.time.min, me) || MAX_INTEGER;
|
||||
var max = parse(options.time.max, me) || MIN_INTEGER;
|
||||
var timestamps = [];
|
||||
var labels = [];
|
||||
var i, j, ilen, jlen, data, timestamp;
|
||||
@@ -420,29 +445,25 @@ module.exports = function(Chart) {
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce limits with user min/max options
|
||||
min = parse(options.time.min, me) || min;
|
||||
max = parse(options.time.max, me) || max;
|
||||
|
||||
// In case there is no valid min/max, let's use today limits
|
||||
min = min === MAX_INTEGER ? +moment().startOf('day') : min;
|
||||
max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max;
|
||||
|
||||
me._model = {
|
||||
datasets: timestamps,
|
||||
horizontal: me.isHorizontal(),
|
||||
labels: labels.sort(sorter), // Sort labels **after** data have been converted
|
||||
min: Math.min(min, max), // Make sure that max is **strictly** higher ...
|
||||
max: Math.max(min + 1, max), // ... than min (required by the lookup table)
|
||||
table: []
|
||||
};
|
||||
// Make sure that max is strictly higher than min (required by the lookup table)
|
||||
me.min = Math.min(min, max);
|
||||
me.max = Math.max(min + 1, max);
|
||||
|
||||
// PRIVATE
|
||||
me._datasets = timestamps;
|
||||
me._horizontal = me.isHorizontal();
|
||||
me._labels = labels.sort(sorter); // Sort labels **after** data have been converted
|
||||
me._table = [];
|
||||
},
|
||||
|
||||
buildTicks: function() {
|
||||
var me = this;
|
||||
var model = me._model;
|
||||
var min = model.min;
|
||||
var max = model.max;
|
||||
var min = me.min;
|
||||
var max = me.max;
|
||||
var timeOpts = me.options.time;
|
||||
var ticksOpts = me.options.ticks;
|
||||
var formats = timeOpts.displayFormats;
|
||||
@@ -458,14 +479,19 @@ module.exports = function(Chart) {
|
||||
|| determineStepSize(min, max, unit, capacity);
|
||||
|
||||
timestamps = generate(min, max, unit, majorUnit, stepSize, timeOpts);
|
||||
|
||||
// Expand min/max to the generated ticks
|
||||
min = helpers.isNullOrUndef(timeOpts.min) && timestamps.length ? timestamps[0] : min;
|
||||
max = helpers.isNullOrUndef(timeOpts.max) && timestamps.length ? timestamps[timestamps.length - 1] : max;
|
||||
} else {
|
||||
timestamps = model.labels;
|
||||
timestamps = me._labels;
|
||||
}
|
||||
|
||||
if (ticksOpts.bounds === 'labels' && timestamps.length) {
|
||||
min = timestamps[0];
|
||||
max = timestamps[timestamps.length - 1];
|
||||
}
|
||||
|
||||
// Enforce limits with user min/max options
|
||||
min = parse(timeOpts.min, me) || min;
|
||||
max = parse(timeOpts.max, me) || max;
|
||||
|
||||
// Remove ticks outside the min/max range
|
||||
for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
|
||||
timestamp = timestamps[i];
|
||||
@@ -477,12 +503,13 @@ module.exports = function(Chart) {
|
||||
me.ticks = ticks;
|
||||
me.min = min;
|
||||
me.max = max;
|
||||
me.unit = unit;
|
||||
me.majorUnit = majorUnit;
|
||||
me.displayFormat = formats[unit];
|
||||
me.majorDisplayFormat = formats[majorUnit];
|
||||
|
||||
model.table = buildLookupTable(ticks, min, max, ticksOpts.mode === 'linear');
|
||||
// PRIVATE
|
||||
me._unit = unit;
|
||||
me._majorUnit = majorUnit;
|
||||
me._displayFormat = formats[unit];
|
||||
me._majorDisplayFormat = formats[majorUnit];
|
||||
me._table = buildLookupTable(ticks, min, max, ticksOpts.mode === 'linear');
|
||||
},
|
||||
|
||||
getLabelForIndex: function(index, datasetIndex) {
|
||||
@@ -510,11 +537,11 @@ module.exports = function(Chart) {
|
||||
var me = this;
|
||||
var options = me.options;
|
||||
var time = tick.valueOf();
|
||||
var majorUnit = me.majorUnit;
|
||||
var majorFormat = me.majorDisplayFormat;
|
||||
var majorTime = tick.clone().startOf(me.majorUnit).valueOf();
|
||||
var majorUnit = me._majorUnit;
|
||||
var majorFormat = me._majorDisplayFormat;
|
||||
var majorTime = tick.clone().startOf(me._majorUnit).valueOf();
|
||||
var major = majorUnit && majorFormat && time === majorTime;
|
||||
var formattedTick = tick.format(major ? majorFormat : me.displayFormat);
|
||||
var formattedTick = tick.format(major ? majorFormat : me._displayFormat);
|
||||
var tickOpts = major ? options.ticks.major : options.ticks.minor;
|
||||
var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
|
||||
|
||||
@@ -543,10 +570,9 @@ module.exports = function(Chart) {
|
||||
*/
|
||||
getPixelForOffset: function(time) {
|
||||
var me = this;
|
||||
var model = me._model;
|
||||
var size = model.horizontal ? me.width : me.height;
|
||||
var start = model.horizontal ? me.left : me.top;
|
||||
var pos = interpolate(model.table, 'time', time, 'pos');
|
||||
var size = me._horizontal ? me.width : me.height;
|
||||
var start = me._horizontal ? me.left : me.top;
|
||||
var pos = interpolate(me._table, 'time', time, 'pos');
|
||||
|
||||
return start + size * pos;
|
||||
},
|
||||
@@ -556,7 +582,7 @@ module.exports = function(Chart) {
|
||||
var time = null;
|
||||
|
||||
if (index !== undefined && datasetIndex !== undefined) {
|
||||
time = me._model.datasets[datasetIndex][index];
|
||||
time = me._datasets[datasetIndex][index];
|
||||
}
|
||||
|
||||
if (time === null) {
|
||||
@@ -576,11 +602,10 @@ module.exports = function(Chart) {
|
||||
|
||||
getValueForPixel: function(pixel) {
|
||||
var me = this;
|
||||
var model = me._model;
|
||||
var size = model.horizontal ? me.width : me.height;
|
||||
var start = model.horizontal ? me.left : me.top;
|
||||
var size = me._horizontal ? me.width : me.height;
|
||||
var start = me._horizontal ? me.left : me.top;
|
||||
var pos = size ? (pixel - start) / size : 0;
|
||||
var time = interpolate(model.table, 'pos', pos, 'time');
|
||||
var time = interpolate(me._table, 'pos', pos, 'time');
|
||||
|
||||
return moment(time);
|
||||
},
|
||||
@@ -607,7 +632,7 @@ module.exports = function(Chart) {
|
||||
getLabelCapacity: function(exampleTime) {
|
||||
var me = this;
|
||||
|
||||
me.displayFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation
|
||||
me._displayFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation
|
||||
|
||||
var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []).value;
|
||||
var tickLabelWidth = me.getLabelWidth(exampleLabel);
|
||||
|
||||
@@ -268,6 +268,9 @@ describe('Deprecations', function() {
|
||||
time: {
|
||||
unit: 'hour',
|
||||
unitStepSize: 2
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ describe('Time scale tests', function() {
|
||||
mirror: false,
|
||||
mode: 'linear',
|
||||
source: 'auto',
|
||||
bounds: 'data',
|
||||
padding: 0,
|
||||
reverse: false,
|
||||
display: true,
|
||||
@@ -144,7 +145,8 @@ describe('Time scale tests', function() {
|
||||
scale.update(1000, 200);
|
||||
var ticks = getTicksValues(scale.ticks);
|
||||
|
||||
expect(ticks).toEqual(['Jan 2015', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10', 'Jan 11']);
|
||||
// `ticks.bounds === 'data'`: first and last ticks removed since outside the data range
|
||||
expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
|
||||
});
|
||||
|
||||
it('should accept labels as date objects', function() {
|
||||
@@ -155,7 +157,8 @@ describe('Time scale tests', function() {
|
||||
scale.update(1000, 200);
|
||||
var ticks = getTicksValues(scale.ticks);
|
||||
|
||||
expect(ticks).toEqual(['Jan 2015', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10', 'Jan 11']);
|
||||
// `ticks.bounds === 'data'`: first and last ticks removed since outside the data range
|
||||
expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
|
||||
});
|
||||
|
||||
it('should accept data as xy points', function() {
|
||||
@@ -203,7 +206,8 @@ describe('Time scale tests', function() {
|
||||
xScale.update(800, 200);
|
||||
var ticks = getTicksValues(xScale.ticks);
|
||||
|
||||
expect(ticks).toEqual(['Jan 2015', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10', 'Jan 11']);
|
||||
// `ticks.bounds === 'data'`: first and last ticks removed since outside the data range
|
||||
expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
|
||||
});
|
||||
|
||||
it('should accept data as ty points', function() {
|
||||
@@ -251,7 +255,8 @@ describe('Time scale tests', function() {
|
||||
tScale.update(800, 200);
|
||||
var ticks = getTicksValues(tScale.ticks);
|
||||
|
||||
expect(ticks).toEqual(['Jan 2015', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10', 'Jan 11']);
|
||||
// `ticks.bounds === 'data'`: first and last ticks removed since outside the data range
|
||||
expect(ticks).toEqual(['Jan 2', 'Jan 3', 'Jan 4', 'Jan 5', 'Jan 6', 'Jan 7', 'Jan 8', 'Jan 9', 'Jan 10']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -259,12 +264,10 @@ describe('Time scale tests', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['foo', 'bar'],
|
||||
datasets: [{
|
||||
xAxisID: 'xScale0',
|
||||
data: [{
|
||||
x: 375068900,
|
||||
y: 1
|
||||
}]
|
||||
data: [0, 1]
|
||||
}],
|
||||
},
|
||||
options: {
|
||||
@@ -277,8 +280,13 @@ describe('Time scale tests', function() {
|
||||
unit: 'day',
|
||||
round: true,
|
||||
parser: function(label) {
|
||||
return moment.unix(label);
|
||||
return label === 'foo' ?
|
||||
moment(946771200000) : // 02/01/2000 @ 12:00am (UTC)
|
||||
moment(1462665600000); // 05/08/2016 @ 12:00am (UTC)
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
source: 'labels'
|
||||
}
|
||||
}],
|
||||
}
|
||||
@@ -289,8 +297,8 @@ describe('Time scale tests', function() {
|
||||
var xScale = chart.scales.xScale0;
|
||||
|
||||
// Counts down because the lines are drawn top to bottom
|
||||
expect(xScale.ticks[0].value).toEqualOneOf(['Nov 19', 'Nov 20', 'Nov 21']); // handle time zone changes
|
||||
expect(xScale.ticks[1].value).toEqualOneOf(['Nov 19', 'Nov 20', 'Nov 21']); // handle time zone changes
|
||||
expect(xScale.ticks[0].value).toBe('Jan 2');
|
||||
expect(xScale.ticks[1].value).toBe('May 8');
|
||||
});
|
||||
|
||||
it('should build ticks using the config unit', function() {
|
||||
@@ -313,8 +321,14 @@ describe('Time scale tests', function() {
|
||||
labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00'], // days
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
|
||||
config.time.minUnit = 'day';
|
||||
var config = Chart.helpers.mergeIf({
|
||||
time: {
|
||||
minUnit: 'day'
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}, Chart.scaleService.getScaleDefaults('time'));
|
||||
|
||||
var scale = createScale(mockData, config);
|
||||
var ticks = getTicksValues(scale.ticks);
|
||||
@@ -327,9 +341,15 @@ describe('Time scale tests', function() {
|
||||
labels: ['2015-01-01T20:00:00', '2015-02-02T21:00:00', '2015-02-21T01:00:00'], // days
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
|
||||
config.time.unit = 'week';
|
||||
config.time.round = 'week';
|
||||
var config = Chart.helpers.mergeIf({
|
||||
time: {
|
||||
unit: 'week',
|
||||
round: 'week'
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}, Chart.scaleService.getScaleDefaults('time'));
|
||||
|
||||
var scale = createScale(mockData, config);
|
||||
scale.update(800, 200);
|
||||
@@ -345,9 +365,15 @@ describe('Time scale tests', function() {
|
||||
labels: ['2015-01-01T20:00:00', '2015-01-01T21:00:00'],
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
|
||||
config.time.unit = 'hour';
|
||||
config.time.stepSize = 2;
|
||||
var config = Chart.helpers.mergeIf({
|
||||
time: {
|
||||
unit: 'hour',
|
||||
stepSize: 2
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}, Chart.scaleService.getScaleDefaults('time'));
|
||||
|
||||
var scale = createScale(mockData, config);
|
||||
scale.update(2500, 200);
|
||||
@@ -394,10 +420,16 @@ describe('Time scale tests', function() {
|
||||
]
|
||||
};
|
||||
|
||||
var config = Chart.helpers.clone(Chart.scaleService.getScaleDefaults('time'));
|
||||
config.time.unit = 'week';
|
||||
// Wednesday
|
||||
config.time.isoWeekday = 3;
|
||||
var config = Chart.helpers.mergeIf({
|
||||
time: {
|
||||
unit: 'week',
|
||||
isoWeekday: 3 // Wednesday
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}, Chart.scaleService.getScaleDefaults('time'));
|
||||
|
||||
var scale = createScale(mockData, config);
|
||||
var ticks = getTicksValues(scale.ticks);
|
||||
|
||||
@@ -405,50 +437,64 @@ describe('Time scale tests', function() {
|
||||
});
|
||||
|
||||
describe('when rendering several days', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
xAxisID: 'xScale0',
|
||||
data: []
|
||||
}],
|
||||
labels: ['2015-01-01T20:00:00', '2015-01-02T21:00:00', '2015-01-03T22:00:00', '2015-01-05T23:00:00', '2015-01-07T03:00', '2015-01-08T10:00', '2015-01-10T12:00'], // days
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'xScale0',
|
||||
type: 'time',
|
||||
position: 'bottom'
|
||||
beforeEach(function() {
|
||||
this.chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
xAxisID: 'xScale0',
|
||||
data: []
|
||||
}],
|
||||
labels: [
|
||||
'2015-01-01T20:00:00',
|
||||
'2015-01-02T21:00:00',
|
||||
'2015-01-03T22:00:00',
|
||||
'2015-01-05T23:00:00',
|
||||
'2015-01-07T03:00',
|
||||
'2015-01-08T10:00',
|
||||
'2015-01-10T12:00'
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'xScale0',
|
||||
type: 'time',
|
||||
position: 'bottom'
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.scale = this.chart.scales.xScale0;
|
||||
});
|
||||
|
||||
var xScale = chart.scales.xScale0;
|
||||
|
||||
it('should be bounded by the nearest week beginnings', function() {
|
||||
expect(xScale.getValueForPixel(xScale.left)).toBeGreaterThan(moment(chart.data.labels[0]).startOf('week'));
|
||||
expect(xScale.getValueForPixel(xScale.right)).toBeLessThan(moment(chart.data.labels[chart.data.labels.length - 1]).add(1, 'week').endOf('week'));
|
||||
var chart = this.chart;
|
||||
var scale = this.scale;
|
||||
expect(scale.getValueForPixel(scale.left)).toBeGreaterThan(moment(chart.data.labels[0]).startOf('week'));
|
||||
expect(scale.getValueForPixel(scale.right)).toBeLessThan(moment(chart.data.labels[chart.data.labels.length - 1]).add(1, 'week').endOf('week'));
|
||||
});
|
||||
|
||||
it('should convert between screen coordinates and times', function() {
|
||||
var timeRange = moment(xScale.max).valueOf() - moment(xScale.min).valueOf();
|
||||
var msPerPix = timeRange / xScale.width;
|
||||
var firstPointOffsetMs = moment(chart.config.data.labels[0]).valueOf() - xScale.min;
|
||||
var firstPointPixel = xScale.left + firstPointOffsetMs / msPerPix;
|
||||
var lastPointOffsetMs = moment(chart.config.data.labels[chart.config.data.labels.length - 1]).valueOf() - xScale.min;
|
||||
var lastPointPixel = xScale.left + lastPointOffsetMs / msPerPix;
|
||||
var chart = this.chart;
|
||||
var scale = this.scale;
|
||||
var timeRange = moment(scale.max).valueOf() - moment(scale.min).valueOf();
|
||||
var msPerPix = timeRange / scale.width;
|
||||
var firstPointOffsetMs = moment(chart.config.data.labels[0]).valueOf() - scale.min;
|
||||
var firstPointPixel = scale.left + firstPointOffsetMs / msPerPix;
|
||||
var lastPointOffsetMs = moment(chart.config.data.labels[chart.config.data.labels.length - 1]).valueOf() - scale.min;
|
||||
var lastPointPixel = scale.left + lastPointOffsetMs / msPerPix;
|
||||
|
||||
expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(firstPointPixel);
|
||||
expect(xScale.getPixelForValue(chart.data.labels[0])).toBeCloseToPixel(firstPointPixel);
|
||||
expect(xScale.getValueForPixel(firstPointPixel)).toBeCloseToTime({
|
||||
expect(scale.getPixelForValue('', 0, 0)).toBeCloseToPixel(firstPointPixel);
|
||||
expect(scale.getPixelForValue(chart.data.labels[0])).toBeCloseToPixel(firstPointPixel);
|
||||
expect(scale.getValueForPixel(firstPointPixel)).toBeCloseToTime({
|
||||
value: moment(chart.data.labels[0]),
|
||||
unit: 'hour',
|
||||
});
|
||||
|
||||
expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(lastPointPixel);
|
||||
expect(xScale.getValueForPixel(lastPointPixel)).toBeCloseToTime({
|
||||
expect(scale.getPixelForValue('', 6, 0)).toBeCloseToPixel(lastPointPixel);
|
||||
expect(scale.getValueForPixel(lastPointPixel)).toBeCloseToTime({
|
||||
value: moment(chart.data.labels[6]),
|
||||
unit: 'hour'
|
||||
});
|
||||
@@ -456,51 +502,58 @@ describe('Time scale tests', function() {
|
||||
});
|
||||
|
||||
describe('when rendering several years', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['2005-07-04', '2017-01-20'],
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'xScale0',
|
||||
type: 'time',
|
||||
position: 'bottom'
|
||||
}],
|
||||
beforeEach(function() {
|
||||
this.chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['2005-07-04', '2017-01-20'],
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'xScale0',
|
||||
type: 'time',
|
||||
position: 'bottom',
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.scale = this.chart.scales.xScale0;
|
||||
this.scale.update(800, 200);
|
||||
});
|
||||
|
||||
var xScale = chart.scales.xScale0;
|
||||
xScale.update(800, 200);
|
||||
|
||||
var step = xScale.ticks[1].time - xScale.ticks[0].time;
|
||||
var stepsAmount = Math.floor((xScale.max - xScale.min) / step);
|
||||
|
||||
it('should be bounded by nearest step\'s year start and end', function() {
|
||||
expect(xScale.getValueForPixel(xScale.left)).toBeCloseToTime({
|
||||
value: moment(xScale.min).startOf('year'),
|
||||
var scale = this.scale;
|
||||
var step = scale.ticks[1].time - scale.ticks[0].time;
|
||||
var stepsAmount = Math.floor((scale.max - scale.min) / step);
|
||||
|
||||
expect(scale.getValueForPixel(scale.left)).toBeCloseToTime({
|
||||
value: moment(scale.min).startOf('year'),
|
||||
unit: 'hour',
|
||||
});
|
||||
expect(xScale.getValueForPixel(xScale.right)).toBeCloseToTime({
|
||||
value: moment(xScale.min + step * stepsAmount).endOf('year'),
|
||||
expect(scale.getValueForPixel(scale.right)).toBeCloseToTime({
|
||||
value: moment(scale.min + step * stepsAmount).endOf('year'),
|
||||
unit: 'hour',
|
||||
});
|
||||
});
|
||||
|
||||
it('should build the correct ticks', function() {
|
||||
// Where 'correct' is a two year spacing.
|
||||
expect(getTicksValues(xScale.ticks)).toEqual(['2005', '2007', '2009', '2011', '2013', '2015', '2017', '2019']);
|
||||
expect(getTicksValues(this.scale.ticks)).toEqual(['2005', '2007', '2009', '2011', '2013', '2015', '2017', '2019']);
|
||||
});
|
||||
|
||||
it('should have ticks with accurate labels', function() {
|
||||
var ticks = xScale.ticks;
|
||||
var pixelsPerYear = xScale.width / 14;
|
||||
var scale = this.scale;
|
||||
var ticks = scale.ticks;
|
||||
var pixelsPerYear = scale.width / 14;
|
||||
|
||||
for (var i = 0; i < ticks.length - 1; i++) {
|
||||
var offset = 2 * pixelsPerYear * i;
|
||||
expect(xScale.getValueForPixel(xScale.left + offset)).toBeCloseToTime({
|
||||
expect(scale.getValueForPixel(scale.left + offset)).toBeCloseToTime({
|
||||
value: moment(ticks[i].value + '-01-01'),
|
||||
unit: 'day',
|
||||
threshold: 0.5,
|
||||
@@ -638,7 +691,7 @@ describe('Time scale tests', function() {
|
||||
|
||||
expect(scale.min).toEqual(+moment('2012', 'YYYY'));
|
||||
expect(scale.max).toEqual(+moment('2051', 'YYYY'));
|
||||
expect(getTicksValues(this.chart.scales.x.ticks)).toEqual([
|
||||
expect(getTicksValues(scale.ticks)).toEqual([
|
||||
'2017', '2019', '2020', '2025', '2042']);
|
||||
});
|
||||
it ('should remove ticks that are not inside the min and max time range', function() {
|
||||
@@ -652,7 +705,7 @@ describe('Time scale tests', function() {
|
||||
|
||||
expect(scale.min).toEqual(+moment('2022', 'YYYY'));
|
||||
expect(scale.max).toEqual(+moment('2032', 'YYYY'));
|
||||
expect(getTicksValues(this.chart.scales.x.ticks)).toEqual([
|
||||
expect(getTicksValues(scale.ticks)).toEqual([
|
||||
'2025']);
|
||||
});
|
||||
it ('should not duplicate ticks if min and max are the labels limits', function() {
|
||||
@@ -666,7 +719,7 @@ describe('Time scale tests', function() {
|
||||
|
||||
expect(scale.min).toEqual(+moment('2017', 'YYYY'));
|
||||
expect(scale.max).toEqual(+moment('2042', 'YYYY'));
|
||||
expect(getTicksValues(this.chart.scales.x.ticks)).toEqual([
|
||||
expect(getTicksValues(scale.ticks)).toEqual([
|
||||
'2017', '2019', '2020', '2025', '2042']);
|
||||
});
|
||||
it ('should correctly handle empty `data.labels`', function() {
|
||||
@@ -678,7 +731,7 @@ describe('Time scale tests', function() {
|
||||
|
||||
expect(scale.min).toEqual(+moment().startOf('day'));
|
||||
expect(scale.max).toEqual(+moment().endOf('day') + 1);
|
||||
expect(getTicksValues(this.chart.scales.x.ticks)).toEqual([]);
|
||||
expect(getTicksValues(scale.ticks)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -834,4 +887,155 @@ describe('Time scale tests', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when ticks.bounds', function() {
|
||||
describe('is "data"', function() {
|
||||
it ('should preserve the data range', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
|
||||
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x',
|
||||
type: 'time',
|
||||
time: {
|
||||
parser: 'MM/DD HH:mm',
|
||||
unit: 'day'
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'data'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var scale = chart.scales.x;
|
||||
|
||||
expect(scale.min).toEqual(+moment('02/20 08:00', 'MM/DD HH:mm'));
|
||||
expect(scale.max).toEqual(+moment('02/23 11:00', 'MM/DD HH:mm'));
|
||||
expect(scale.getPixelForValue('02/20 08:00')).toBeCloseToPixel(scale.left);
|
||||
expect(scale.getPixelForValue('02/23 11:00')).toBeCloseToPixel(scale.left + scale.width);
|
||||
expect(getTicksValues(scale.ticks)).toEqual([
|
||||
'Feb 21', 'Feb 22', 'Feb 23']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('is "labels"', function() {
|
||||
it('should preserve the label range', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
|
||||
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x',
|
||||
type: 'time',
|
||||
time: {
|
||||
parser: 'MM/DD HH:mm',
|
||||
unit: 'day'
|
||||
},
|
||||
ticks: {
|
||||
bounds: 'labels'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var scale = chart.scales.x;
|
||||
var ticks = scale.ticks;
|
||||
|
||||
expect(scale.min).toEqual(ticks[0].time);
|
||||
expect(scale.max).toEqual(ticks[ticks.length - 1].time);
|
||||
expect(scale.getPixelForValue('02/20 08:00')).toBeCloseToPixel(77);
|
||||
expect(scale.getPixelForValue('02/23 11:00')).toBeCloseToPixel(412);
|
||||
expect(getTicksValues(scale.ticks)).toEqual([
|
||||
'Feb 20', 'Feb 21', 'Feb 22', 'Feb 23', 'Feb 24']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when time.min and/or time.max are defined', function() {
|
||||
['auto', 'labels'].forEach(function(source) {
|
||||
['data', 'labels'].forEach(function(bounds) {
|
||||
describe('and source is "' + source + '" and bounds "' + bounds + '"', function() {
|
||||
beforeEach(function() {
|
||||
this.chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['02/20 08:00', '02/21 09:00', '02/22 10:00', '02/23 11:00'],
|
||||
datasets: [{data: [0, 1, 2, 3, 4, 5]}]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x',
|
||||
type: 'time',
|
||||
time: {
|
||||
parser: 'MM/DD HH:mm',
|
||||
unit: 'day'
|
||||
},
|
||||
ticks: {
|
||||
source: source,
|
||||
bounds: bounds
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: false
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it ('should expand scale to the min/max range', function() {
|
||||
var chart = this.chart;
|
||||
var scale = chart.scales.x;
|
||||
var options = chart.options.scales.xAxes[0];
|
||||
var min = '02/19 07:00';
|
||||
var max = '02/24 08:00';
|
||||
|
||||
options.time.min = min;
|
||||
options.time.max = max;
|
||||
chart.update();
|
||||
|
||||
expect(scale.min).toEqual(+moment(min, 'MM/DD HH:mm'));
|
||||
expect(scale.max).toEqual(+moment(max, 'MM/DD HH:mm'));
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(scale.left);
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(scale.left + scale.width);
|
||||
});
|
||||
it ('should shrink scale to the min/max range', function() {
|
||||
var chart = this.chart;
|
||||
var scale = chart.scales.x;
|
||||
var options = chart.options.scales.xAxes[0];
|
||||
var min = '02/21 07:00';
|
||||
var max = '02/22 20:00';
|
||||
|
||||
options.time.min = min;
|
||||
options.time.max = max;
|
||||
chart.update();
|
||||
|
||||
expect(scale.min).toEqual(+moment(min, 'MM/DD HH:mm'));
|
||||
expect(scale.max).toEqual(+moment(max, 'MM/DD HH:mm'));
|
||||
expect(scale.getPixelForValue(min)).toBeCloseToPixel(scale.left);
|
||||
expect(scale.getPixelForValue(max)).toBeCloseToPixel(scale.left + scale.width);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user