Implement equally sized bars (#4994)
When `barThickness: undefined|null` (default), we compute an optimal sample size based on the smallest tick interval reduced to prevent any bar to overlap (bar equally sized). Also added support for a special `barThickness: 'flex'` value (previous default) that globally arranges bars side by side to prevent any gap when percentage options are 1 (variable bar sizes).
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
||||
bower.json
|
||||
*.log
|
||||
*.swp
|
||||
*.stackdump
|
||||
|
||||
@ -95,6 +95,93 @@ defaults._set('horizontalBar', {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
|
||||
* @private
|
||||
*/
|
||||
function computeMinSampleSize(scale, pixels) {
|
||||
var min = scale.isHorizontal() ? scale.width : scale.height;
|
||||
var ticks = scale.getTicks();
|
||||
var prev, curr, i, ilen;
|
||||
|
||||
for (i = 1, ilen = pixels.length; i < ilen; ++i) {
|
||||
min = Math.min(min, pixels[i] - pixels[i - 1]);
|
||||
}
|
||||
|
||||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||||
curr = scale.getPixelForTick(i);
|
||||
min = i > 0 ? Math.min(min, curr - prev) : min;
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
|
||||
* uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
|
||||
* mode currently always generates bars equally sized (until we introduce scriptable options?).
|
||||
* @private
|
||||
*/
|
||||
function computeFitCategoryTraits(index, ruler, options) {
|
||||
var thickness = options.barThickness;
|
||||
var count = ruler.stackCount;
|
||||
var curr = ruler.pixels[index];
|
||||
var size, ratio;
|
||||
|
||||
if (helpers.isNullOrUndef(thickness)) {
|
||||
size = ruler.min * options.categoryPercentage;
|
||||
ratio = options.barPercentage;
|
||||
} else {
|
||||
// When bar thickness is enforced, category and bar percentages are ignored.
|
||||
// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
|
||||
// and deprecate barPercentage since this value is ignored when thickness is absolute.
|
||||
size = thickness * count;
|
||||
ratio = 1;
|
||||
}
|
||||
|
||||
return {
|
||||
chunk: size / count,
|
||||
ratio: ratio,
|
||||
start: curr - (size / 2)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an "optimal" category that globally arranges bars side by side (no gap when
|
||||
* percentage options are 1), based on the previous and following categories. This mode
|
||||
* generates bars with different widths when data are not evenly spaced.
|
||||
* @private
|
||||
*/
|
||||
function computeFlexCategoryTraits(index, ruler, options) {
|
||||
var pixels = ruler.pixels;
|
||||
var curr = pixels[index];
|
||||
var prev = index > 0 ? pixels[index - 1] : null;
|
||||
var next = index < pixels.length - 1 ? pixels[index + 1] : null;
|
||||
var percent = options.categoryPercentage;
|
||||
var start, size;
|
||||
|
||||
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 (next === null) {
|
||||
// last data: its size is also double based on the previous point.
|
||||
next = curr + curr - prev;
|
||||
}
|
||||
|
||||
start = curr - ((curr - prev) / 2) * percent;
|
||||
size = ((next - prev) / 2) * percent;
|
||||
|
||||
return {
|
||||
chunk: size / ruler.stackCount,
|
||||
ratio: options.barPercentage,
|
||||
start: start
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function(Chart) {
|
||||
|
||||
Chart.controllers.bar = Chart.DatasetController.extend({
|
||||
@ -262,17 +349,22 @@ module.exports = function(Chart) {
|
||||
var scale = me.getIndexScale();
|
||||
var stackCount = me.getStackCount();
|
||||
var datasetIndex = me.index;
|
||||
var pixels = [];
|
||||
var isHorizontal = scale.isHorizontal();
|
||||
var start = isHorizontal ? scale.left : scale.top;
|
||||
var end = start + (isHorizontal ? scale.width : scale.height);
|
||||
var i, ilen;
|
||||
var pixels = [];
|
||||
var i, ilen, min;
|
||||
|
||||
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
|
||||
pixels.push(scale.getPixelForValue(null, i, datasetIndex));
|
||||
}
|
||||
|
||||
min = helpers.isNullOrUndef(scale.options.barThickness)
|
||||
? computeMinSampleSize(scale, pixels)
|
||||
: -1;
|
||||
|
||||
return {
|
||||
min: min,
|
||||
pixels: pixels,
|
||||
start: start,
|
||||
end: end,
|
||||
@ -332,51 +424,21 @@ module.exports = function(Chart) {
|
||||
calculateBarIndexPixels: function(datasetIndex, index, ruler) {
|
||||
var me = this;
|
||||
var options = ruler.scale.options;
|
||||
var meta = me.getMeta();
|
||||
var stackIndex = me.getStackIndex(datasetIndex, meta.stack);
|
||||
var pixels = ruler.pixels;
|
||||
var base = pixels[index];
|
||||
var length = pixels.length;
|
||||
var start = ruler.start;
|
||||
var end = ruler.end;
|
||||
var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size;
|
||||
var range = options.barThickness === 'flex'
|
||||
? computeFlexCategoryTraits(index, ruler, options)
|
||||
: computeFitCategoryTraits(index, ruler, options);
|
||||
|
||||
if (length === 1) {
|
||||
leftSampleSize = base > start ? base - start : end - base;
|
||||
rightSampleSize = base < end ? end - base : base - start;
|
||||
} else {
|
||||
if (index > 0) {
|
||||
leftSampleSize = (base - pixels[index - 1]) / 2;
|
||||
if (index === length - 1) {
|
||||
rightSampleSize = leftSampleSize;
|
||||
}
|
||||
}
|
||||
if (index < length - 1) {
|
||||
rightSampleSize = (pixels[index + 1] - base) / 2;
|
||||
if (index === 0) {
|
||||
leftSampleSize = rightSampleSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leftCategorySize = leftSampleSize * options.categoryPercentage;
|
||||
rightCategorySize = rightSampleSize * options.categoryPercentage;
|
||||
fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount;
|
||||
size = fullBarSize * options.barPercentage;
|
||||
|
||||
size = Math.min(
|
||||
helpers.valueOrDefault(options.barThickness, size),
|
||||
helpers.valueOrDefault(options.maxBarThickness, Infinity));
|
||||
|
||||
base -= leftCategorySize;
|
||||
base += fullBarSize * stackIndex;
|
||||
base += (fullBarSize - size) / 2;
|
||||
var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
|
||||
var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
|
||||
var size = Math.min(
|
||||
helpers.valueOrDefault(options.maxBarThickness, Infinity),
|
||||
range.chunk * range.ratio);
|
||||
|
||||
return {
|
||||
size: size,
|
||||
base: base,
|
||||
head: base + size,
|
||||
center: base + size / 2
|
||||
base: center - size / 2,
|
||||
head: center + size / 2,
|
||||
center: center,
|
||||
size: size
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@ -11,3 +11,4 @@ globals:
|
||||
rules:
|
||||
# Best Practices
|
||||
complexity: 0
|
||||
max-statements: 0
|
||||
|
||||
42
test/fixtures/controller.bar/bar-thickness-absolute.json
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2017", "2018", "2019", "2024", "2025"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "rgba(255, 99, 132, 0.5)",
|
||||
"data": [1, null, 3, 4, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"offset": true,
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"barThickness": 128,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-absolute.png
vendored
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
42
test/fixtures/controller.bar/bar-thickness-flex-offset.json
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2017", "2018", "2020", "2024", "2038"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [1, null, 3, 4, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"offset": true,
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"barThickness": "flex",
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-flex-offset.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
41
test/fixtures/controller.bar/bar-thickness-flex.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2017", "2018", "2020", "2024", "2038"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [1, null, 3, 4, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"barThickness": "flex",
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-flex.png
vendored
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
41
test/fixtures/controller.bar/bar-thickness-max.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2016", "2018", "2020", "2024", "2030"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [1, null, 3, 4, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"maxBarThickness": 8,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-max.png
vendored
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
40
test/fixtures/controller.bar/bar-thickness-min-interval.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2016", "2018", "2020", "2024", "2030"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [1, null, 3, 4, 5]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-min-interval.png
vendored
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
46
test/fixtures/controller.bar/bar-thickness-multiple.json
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-multiple.png
vendored
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
46
test/fixtures/controller.bar/bar-thickness-no-overlap.json
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2016", "2018", "2020", "2024", "2030"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [
|
||||
{"y": "1", "t": "2016"},
|
||||
{"y": "2", "t": "2017"},
|
||||
{"y": "3", "t": "2017-08"},
|
||||
{"y": "4", "t": "2024"},
|
||||
{"y": "5", "t": "2030"}
|
||||
]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-no-overlap.png
vendored
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
47
test/fixtures/controller.bar/bar-thickness-offset.json
vendored
Normal file
@ -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",
|
||||
"offset": true,
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-offset.png
vendored
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
40
test/fixtures/controller.bar/bar-thickness-single-xy.json
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": ["2016", "2018", "2020", "2024", "2030"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "#FF6384",
|
||||
"data": [{"x": "2022", "y": 42}]
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"xAxes": [{
|
||||
"type": "time",
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-single-xy.png
vendored
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
43
test/fixtures/controller.bar/bar-thickness-single.json
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"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,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
},
|
||||
"time": {
|
||||
"min": "2013"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-single.png
vendored
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
48
test/fixtures/controller.bar/bar-thickness-stacked.json
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"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",
|
||||
"stacked": true,
|
||||
"display": false,
|
||||
"barPercentage": 1,
|
||||
"categoryPercentage": 1,
|
||||
"ticks": {
|
||||
"source": "labels"
|
||||
}
|
||||
}],
|
||||
"yAxes": [{
|
||||
"display": false,
|
||||
"stacked": true,
|
||||
"ticks": {
|
||||
"beginAtZero": true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/controller.bar/bar-thickness-stacked.png
vendored
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
@ -118,9 +118,6 @@ function specFromFixture(description, inputs) {
|
||||
var chart = acquireChart(json.config, json.options);
|
||||
if (!inputs.png) {
|
||||
fail('Missing PNG comparison file for ' + inputs.json);
|
||||
if (!json.debug) {
|
||||
releaseChart(chart);
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
describe('Chart.controllers.bar', function() {
|
||||
describe('auto', jasmine.specsFromFixtures('controller.bar'));
|
||||
|
||||
it('should be constructed', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'bar',
|
||||
@ -1630,84 +1632,4 @@ describe('Chart.controllers.bar', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bar thickness with a time scale', function() {
|
||||
['auto', 'data', 'labels'].forEach(function(source) {
|
||||
['series', 'linear'].forEach(function(distribution) {
|
||||
describe('When ticks.source is "' + source + '", distribution is "' + distribution + '"', function() {
|
||||
beforeEach(function() {
|
||||
this.chart = window.acquireChart({
|
||||
type: 'bar',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [1, 2, 3]
|
||||
}, {
|
||||
data: [1, 2, 3]
|
||||
}],
|
||||
labels: ['2017', '2018', '2020']
|
||||
},
|
||||
options: {
|
||||
legend: false,
|
||||
title: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x',
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'year',
|
||||
parser: 'YYYY'
|
||||
},
|
||||
ticks: {
|
||||
source: source
|
||||
},
|
||||
offset: true,
|
||||
distribution: distribution
|
||||
}],
|
||||
yAxes: [{
|
||||
type: 'linear'
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should correctly set bar width', function() {
|
||||
var chart = this.chart;
|
||||
var scale = chart.scales.x;
|
||||
var options = chart.options.scales.xAxes[0];
|
||||
var categoryPercentage = options.categoryPercentage;
|
||||
var barPercentage = options.barPercentage;
|
||||
var firstInterval = scale.getPixelForValue('2018') - scale.getPixelForValue('2017');
|
||||
var firstExpected = firstInterval * categoryPercentage / 2 * barPercentage;
|
||||
var lastInterval = scale.getPixelForValue('2020') - scale.getPixelForValue('2018');
|
||||
var lastExpected = lastInterval * categoryPercentage / 2 * barPercentage;
|
||||
var i, ilen, meta;
|
||||
|
||||
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
expect(meta.data[0]._model.width).toBeCloseToPixel(firstExpected);
|
||||
expect(meta.data[1]._model.width).toBeCloseToPixel((firstExpected + lastExpected) / 2);
|
||||
expect(meta.data[2]._model.width).toBeCloseToPixel(lastExpected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should correctly set bar width if maxBarThickness is specified', function() {
|
||||
var chart = this.chart;
|
||||
var options = chart.options.scales.xAxes[0];
|
||||
var i, ilen, meta;
|
||||
|
||||
options.maxBarThickness = 10;
|
||||
chart.update();
|
||||
|
||||
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
|
||||
meta = chart.getDatasetMeta(i);
|
||||
expect(meta.data[0]._model.width).toBeCloseToPixel(10);
|
||||
expect(meta.data[1]._model.width).toBeCloseToPixel(10);
|
||||
expect(meta.data[2]._model.width).toBeCloseToPixel(10);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||