From bb5f12ad2abea9b92d1d2d1bae991a26a95c3cfb Mon Sep 17 00:00:00 2001 From: Akihiko Kusanagi Date: Wed, 19 Jun 2019 19:10:04 +0800 Subject: [PATCH] Support object values for bar charts (#6323) * Support object values for bar charts * Check if null or undefined * Refactor category scale code * Make isNullOrUndef global --- src/scales/scale.category.js | 90 +++++++++-------- test/specs/scale.category.tests.js | 153 +++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 39 deletions(-) diff --git a/src/scales/scale.category.js b/src/scales/scale.category.js index 1d8949d4df6..bd01587369c 100644 --- a/src/scales/scale.category.js +++ b/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' }; @@ -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) { @@ -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) { diff --git a/test/specs/scale.category.tests.js b/test/specs/scale.category.tests.js index d6529324ec3..96883e31c9c 100644 --- a/test/specs/scale.category.tests.js +++ b/test/specs/scale.category.tests.js @@ -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); + }); });