Skip to content

Commit

Permalink
Support object values for bar charts (chartjs#6323)
Browse files Browse the repository at this point in the history
* Support object values for bar charts

* Check if null or undefined

* Refactor category scale code

* Make isNullOrUndef global
  • Loading branch information
nagix authored and etimberg committed Jun 19, 2019
1 parent 870ac76 commit 1a4d1e1
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 39 deletions.
90 changes: 51 additions & 39 deletions src/scales/scale.category.js
@@ -1,7 +1,10 @@
'use strict';

var helpers = require('../helpers/index');
var Scale = require('../core/core.scale');

var isNullOrUndef = helpers.isNullOrUndef;

var defaultConfig = {
position: 'bottom'
};
Expand All @@ -10,31 +13,43 @@ module.exports = Scale.extend({
determineDataLimits: function() {
var me = this;
var labels = me._getLabels();
me.minIndex = 0;
me.maxIndex = labels.length - 1;
var ticksOpts = me.options.ticks;
var min = ticksOpts.min;
var max = ticksOpts.max;
var minIndex = 0;
var maxIndex = labels.length - 1;
var findIndex;

if (me.options.ticks.min !== undefined) {
if (min !== undefined) {
// user specified min value
findIndex = labels.indexOf(me.options.ticks.min);
me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
findIndex = labels.indexOf(min);
if (findIndex >= 0) {
minIndex = findIndex;
}
}

if (me.options.ticks.max !== undefined) {
if (max !== undefined) {
// user specified max value
findIndex = labels.indexOf(me.options.ticks.max);
me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
findIndex = labels.indexOf(max);
if (findIndex >= 0) {
maxIndex = findIndex;
}
}

me.min = labels[me.minIndex];
me.max = labels[me.maxIndex];
me.minIndex = minIndex;
me.maxIndex = maxIndex;
me.min = labels[minIndex];
me.max = labels[maxIndex];
},

buildTicks: function() {
var me = this;
var labels = me._getLabels();
var minIndex = me.minIndex;
var maxIndex = me.maxIndex;

// If we are viewing some subset of labels, slice the original array
me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1);
},

getLabelForIndex: function(index, datasetIndex) {
Expand All @@ -49,61 +64,58 @@ module.exports = Scale.extend({
},

// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index) {
getPixelForValue: function(value, index, datasetIndex) {
var me = this;
var offset = me.options.offset;

// 1 is added because we need the length but we have the indexes
var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
var offsetAmt = Math.max(me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1), 1);

var isHorizontal = me.isHorizontal();
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
var valueCategory, labels, idx, pixel;

if (!isNullOrUndef(index) && !isNullOrUndef(datasetIndex)) {
value = me.chart.data.datasets[datasetIndex].data[index];
}

// If value is a data object, then index is the index in the data array,
// not the index of the scale. We need to change that.
var valueCategory;
if (value !== undefined && value !== null) {
valueCategory = me.isHorizontal() ? value.x : value.y;
if (!isNullOrUndef(value)) {
valueCategory = isHorizontal ? value.x : value.y;
}
if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
var labels = me._getLabels();
value = valueCategory || value;
var idx = labels.indexOf(value);
labels = me._getLabels();
value = helpers.valueOrDefault(valueCategory, value);
idx = labels.indexOf(value);
index = idx !== -1 ? idx : index;
}

if (me.isHorizontal()) {
var valueWidth = me.width / offsetAmt;
var widthOffset = (valueWidth * (index - me.minIndex));

if (offset) {
widthOffset += (valueWidth / 2);
}

return me.left + widthOffset;
}
var valueHeight = me.height / offsetAmt;
var heightOffset = (valueHeight * (index - me.minIndex));
pixel = valueDimension * (index - me.minIndex);

if (offset) {
heightOffset += (valueHeight / 2);
pixel += valueDimension / 2;
}

return me.top + heightOffset;
return (isHorizontal ? me.left : me.top) + pixel;
},

getPixelForTick: function(index) {
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
return this.getPixelForValue(this.ticks[index], index + this.minIndex);
},

getValueForPixel: function(pixel) {
var me = this;
var offset = me.options.offset;
var offsetAmt = Math.max(me._ticks.length - (offset ? 0 : 1), 1);
var isHorizontal = me.isHorizontal();
var valueDimension = (isHorizontal ? me.width : me.height) / offsetAmt;
var value;
var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
var horz = me.isHorizontal();
var valueDimension = (horz ? me.width : me.height) / offsetAmt;

pixel -= horz ? me.left : me.top;
pixel -= isHorizontal ? me.left : me.top;

if (offset) {
pixel -= (valueDimension / 2);
pixel -= valueDimension / 2;
}

if (pixel <= 0) {
Expand Down
153 changes: 153 additions & 0 deletions test/specs/scale.category.tests.js
Expand Up @@ -390,4 +390,157 @@ describe('Category scale tests', function() {
expect(yScale.getPixelForValue(0, 1, 0)).toBeCloseToPixel(107);
expect(yScale.getPixelForValue(0, 3, 0)).toBeCloseToPixel(407);
});

it('Should get the correct pixel for an object value when horizontal', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [
{x: 0, y: 10},
{x: 1, y: 5},
{x: 2, y: 0},
{x: 3, y: 25},
{x: 0, y: 78}
]
}],
labels: [0, 1, 2, 3]
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'category',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});

var xScale = chart.scales.xScale0;
expect(xScale.getPixelForValue({x: 0, y: 10}, 0, 0)).toBeCloseToPixel(29);
expect(xScale.getPixelForValue({x: 3, y: 25}, 3, 0)).toBeCloseToPixel(506);
expect(xScale.getPixelForValue({x: 0, y: 78}, 4, 0)).toBeCloseToPixel(29);
});

it('Should get the correct pixel for an object value when vertical', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [
{x: 0, y: 2},
{x: 1, y: 4},
{x: 2, y: 0},
{x: 3, y: 3},
{x: 0, y: 1}
]
}],
labels: [0, 1, 2, 3],
yLabels: [0, 1, 2, 3, 4]
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'category',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'category',
position: 'left'
}]
}
}
});

var yScale = chart.scales.yScale0;
expect(yScale.getPixelForValue({x: 0, y: 2}, 0, 0)).toBeCloseToPixel(257);
expect(yScale.getPixelForValue({x: 0, y: 1}, 4, 0)).toBeCloseToPixel(144);
});

it('Should get the correct pixel for an object value in a bar chart', function() {
var chart = window.acquireChart({
type: 'bar',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [
{x: 0, y: 10},
{x: 1, y: 5},
{x: 2, y: 0},
{x: 3, y: 25},
{x: 0, y: 78}
]
}],
labels: [0, 1, 2, 3]
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'category',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'linear'
}]
}
}
});

var xScale = chart.scales.xScale0;
expect(xScale.getPixelForValue(null, 0, 0)).toBeCloseToPixel(89);
expect(xScale.getPixelForValue(null, 3, 0)).toBeCloseToPixel(449);
expect(xScale.getPixelForValue(null, 4, 0)).toBeCloseToPixel(89);
});

it('Should get the correct pixel for an object value in a horizontal bar chart', function() {
var chart = window.acquireChart({
type: 'horizontalBar',
data: {
datasets: [{
xAxisID: 'xScale0',
yAxisID: 'yScale0',
data: [
{x: 10, y: 0},
{x: 5, y: 1},
{x: 0, y: 2},
{x: 25, y: 3},
{x: 78, y: 0}
]
}],
labels: [0, 1, 2, 3]
},
options: {
scales: {
xAxes: [{
id: 'xScale0',
type: 'linear',
position: 'bottom'
}],
yAxes: [{
id: 'yScale0',
type: 'category'
}]
}
}
});

var yScale = chart.scales.yScale0;
expect(yScale.getPixelForValue(null, 0, 0)).toBeCloseToPixel(88);
expect(yScale.getPixelForValue(null, 3, 0)).toBeCloseToPixel(426);
expect(yScale.getPixelForValue(null, 4, 0)).toBeCloseToPixel(88);
});
});

0 comments on commit 1a4d1e1

Please sign in to comment.