Add new option, skipNull to bar charts that enables skipping null values (#7849)

* Add new option, `skipNull` to bar charts that enables skipping null
or undefined values.

* Address code review feedback

* Fix windows CI lint issues
This commit is contained in:
Evert Timberg
2020-10-06 07:33:24 -04:00
committed by GitHub
parent 168965fa38
commit 56a8a23d54
7 changed files with 106 additions and 12 deletions

View File

@@ -186,6 +186,14 @@ If set to `'flex'`, the base sample widths are calculated automatically based on
If not set (default), the base sample widths are calculated using the smallest interval that prevents bar overlapping, and bars are sized using `barPercentage` and `categoryPercentage`. This mode always generates bars equally sized.
## Config Options
These are the customisation options specific to Bar charts. These options are merged with the global chart configuration options, and form the options of the chart.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `skipNull` | `boolean` | `undefined` | If `true`, null or undefined values will not be drawn
## Scale Configuration
The bar chart sets unique default values for the following configuration from the associated `scale` options:

View File

@@ -30,9 +30,8 @@ function computeMinSampleSize(scale, pixels) {
* mode currently always generates bars equally sized (until we introduce scriptable options?).
* @private
*/
function computeFitCategoryTraits(index, ruler, options) {
function computeFitCategoryTraits(index, ruler, options, stackCount) {
const thickness = options.barThickness;
const count = ruler.stackCount;
let size, ratio;
if (isNullOrUndef(thickness)) {
@@ -42,12 +41,12 @@ function computeFitCategoryTraits(index, ruler, options) {
// 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;
size = thickness * stackCount;
ratio = 1;
}
return {
chunk: size / count,
chunk: size / stackCount,
ratio,
start: ruler.pixels[index] - (size / 2)
};
@@ -59,7 +58,7 @@ function computeFitCategoryTraits(index, ruler, options) {
* generates bars with different widths when data are not evenly spaced.
* @private
*/
function computeFlexCategoryTraits(index, ruler, options) {
function computeFlexCategoryTraits(index, ruler, options, stackCount) {
const pixels = ruler.pixels;
const curr = pixels[index];
let prev = index > 0 ? pixels[index - 1] : null;
@@ -81,7 +80,7 @@ function computeFlexCategoryTraits(index, ruler, options) {
const size = Math.abs(next - prev) / 2 * percent;
return {
chunk: size / ruler.stackCount,
chunk: size / stackCount,
ratio: options.barPercentage,
start
};
@@ -271,10 +270,11 @@ export default class BarController extends DatasetController {
/**
* Returns the stacks based on groups and bar visibility.
* @param {number} [last] - The dataset index
* @param {number} [dataIndex] - The data index of the ruler
* @returns {string[]} The list of stack IDs
* @private
*/
_getStacks(last) {
_getStacks(last, dataIndex) {
const me = this;
const meta = me._cachedMeta;
const iScale = meta.iScale;
@@ -286,6 +286,17 @@ export default class BarController extends DatasetController {
for (i = 0; i < ilen; ++i) {
item = metasets[i];
if (typeof dataIndex !== 'undefined') {
const val = item.controller.getParsed(dataIndex)[
item.controller._cachedMeta.vScale.axis
];
if (isNullOrUndef(val) || isNaN(val)) {
continue;
}
}
// stacked | meta.stack
// | found | not found | undefined
// false | x | x | x
@@ -314,8 +325,8 @@ export default class BarController extends DatasetController {
* Returns the effective number of stacks based on groups and bar visibility.
* @private
*/
_getStackCount() {
return this._getStacks().length;
_getStackCount(index) {
return this._getStacks(undefined, index).length;
}
/**
@@ -429,9 +440,10 @@ export default class BarController extends DatasetController {
*/
_calculateBarIndexPixels(index, ruler, options) {
const me = this;
const stackCount = me.chart.options.skipNull ? me._getStackCount(index) : ruler.stackCount;
const range = options.barThickness === 'flex'
? computeFlexCategoryTraits(index, ruler, options)
: computeFitCategoryTraits(index, ruler, options);
? computeFlexCategoryTraits(index, ruler, options, stackCount)
: computeFitCategoryTraits(index, ruler, options, stackCount);
const stackIndex = me._getStackIndex(me.index, me._cachedMeta.stack);
const center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
@@ -486,7 +498,7 @@ BarController.defaults = {
'barThickness',
'categoryPercentage',
'maxBarThickness',
'minBarLength'
'minBarLength',
],
hover: {
mode: 'index'

View File

@@ -0,0 +1,35 @@
module.exports = {
config: {
type: 'bar',
data: {
labels: [0, 1, 3, 4],
datasets: [
{
data: {0: 5, 1: 20, 2: 1, 3: 10},
backgroundColor: '#00ff00',
borderColor: '#ff0000'
},
{
data: {0: 10, 1: null, 2: 1, 3: NaN},
backgroundColor: '#ff0000',
borderColor: '#ff0000'
}
]
},
options: {
legend: false,
skipNull: true,
title: false,
scales: {
x: {display: false},
y: {display: false}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,35 @@
module.exports = {
config: {
type: 'bar',
data: {
labels: [0, 1, 3, 4],
datasets: [
{
data: [5, 20, 1, 10],
backgroundColor: '#00ff00',
borderColor: '#ff0000'
},
{
data: [10, null, 1, undefined],
backgroundColor: '#ff0000',
borderColor: '#ff0000'
}
]
},
options: {
legend: false,
skipNull: true,
title: false,
scales: {
x: {display: false},
y: {display: false}
}
}
},
options: {
canvas: {
height: 256,
width: 512
}
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -79,6 +79,10 @@ export interface IBarControllerDatasetOptions
}
export interface IBarControllerChartOptions {
/**
* Should null or undefined values be omitted from drawing
*/
skipNull?: boolean;
}
export interface BarController extends DatasetController {}