mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 16:26:52 +01:00
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:
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
35
test/fixtures/controller.bar/bar-skip-null-object-data.js
vendored
Normal file
35
test/fixtures/controller.bar/bar-skip-null-object-data.js
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
};
|
||||
BIN
test/fixtures/controller.bar/bar-skip-null-object-data.png
vendored
Normal file
BIN
test/fixtures/controller.bar/bar-skip-null-object-data.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
35
test/fixtures/controller.bar/bar-skip-null.js
vendored
Normal file
35
test/fixtures/controller.bar/bar-skip-null.js
vendored
Normal 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
|
||||
}
|
||||
}
|
||||
};
|
||||
BIN
test/fixtures/controller.bar/bar-skip-null.png
vendored
Normal file
BIN
test/fixtures/controller.bar/bar-skip-null.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
4
types/controllers/index.d.ts
vendored
4
types/controllers/index.d.ts
vendored
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user