Skip to content

Commit 59bfd3f

Browse files
committed
fix null cornercases
1 parent 8b28243 commit 59bfd3f

File tree

5 files changed

+37
-82
lines changed

5 files changed

+37
-82
lines changed

README.md

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ four new types: `boxplot`, `horizontalBoxplot`, `violin`, and `horizontalViolin`
2727

2828
## Config
2929

30+
// TODO
31+
3032
```typescript
3133
interface IChartJSOptions {
3234
boxplot: {
@@ -127,15 +129,15 @@ interface IBaseStyling {
127129
* @scriptable
128130
* @indexable
129131
*/
130-
outlierColor: string;
132+
outlierBackgroundColor: string;
131133

132134
/**
133135
* to fill color below the median line of the box
134-
* @default see rectangle.lowerColor
136+
* @default transparent
135137
* @scriptable
136138
* @indexable
137139
*/
138-
lowerColor: string;
140+
lowerBackgroundColor: string;
139141

140142
/**
141143
* radius used to render items
@@ -207,19 +209,6 @@ interface IViolinStyling extends IBaseStyling {
207209
}
208210
```
209211

210-
In addition, two new scales were created `arrayLinear` and `arrayLogarithmic`. They were needed to adapt to the required data structure.
211-
212-
## Scale Options
213-
214-
Both `arrayLinear` and `arrayLogarithmic` support the two additional options to their regular counterparts:
215-
216-
```typescript
217-
interface IArrayLinearScale {
218-
219-
};
220-
}
221-
```
222-
223212
## Data structure
224213

225214
Both types support that the data is given as an array of numbers `number[]`. The statistics will be automatically computed. In addition, specific summary data structures are supported:
@@ -265,38 +254,17 @@ interface IViolinItem extends IBaseItem {
265254
}
266255
```
267256

268-
**Note**: The statistics will be cached within the array. Thus, if you manipulate the array content without creating a new instance the changes won't be reflected in the stats. See also [![Open in CodePen][codepen]](https://codepen.io/sgratzl/pen/JxQVaZ?editors=0010) for a comparison.
269-
270257
## Tooltips
271258

272-
In order to simplify the customization of the tooltips, two additional tooltip callback methods are available. Internally the `label` callback will call the correspondig callback depending on the type.
259+
In order to simplify the customization of the tooltips,
260+
// TODO
273261

274262
```js
275263
arr = {
276264
options: {
277265
tooltips: {
278266
callbacks: {
279-
/**
280-
* custom callback for boxplot tooltips
281-
* @param item see label callback
282-
* @param data see label callback
283-
* @param stats {IBoxPlotItem} the stats of the hovered element
284-
* @param hoveredOutlierIndex {number} the hovered outlier index or -1 if none
285-
* @return {string} see label callback
286-
*/
287-
boxplotLabel: function (item, data, stats, hoveredOutlierIndex) {
288-
return 'Custom tooltip';
289-
},
290-
/**
291-
* custom callback for violin tooltips
292-
* @param item see label callback
293-
* @param data see label callback
294-
* @param stats {IViolinItem} the stats of the hovered element
295-
* @return {string} see label callback
296-
*/
297-
violinLabel: function (item, data, stats) {
298-
return 'Custom tooltip';
299-
},
267+
// TODO
300268
},
301269
},
302270
},

src/controllers/base.js

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,8 @@
22
import { outlierPositioner, patchInHoveredOutlier } from '../tooltip';
33
import { controllers } from 'chart.js';
44

5-
export const configKeys = [
6-
'outlierRadius',
7-
'itemRadius',
8-
'itemStyle',
9-
'itemBackgroundColor',
10-
'itemBorderColor',
11-
'outlierColor',
12-
'medianColor',
13-
'hitPadding',
14-
'outlierHitRadius',
15-
'lowerColor',
16-
];
17-
export const colorStyleKeys = ['borderColor', 'backgroundColor'].concat(configKeys.filter((c) => c.endsWith('Color')));
18-
19-
export function baseDefaults() {
5+
export function baseDefaults(keys) {
6+
const colorKeys = ['borderColor', 'backgroundColor'].concat(keys.filter((c) => c.endsWith('Color')));
207
return {
218
datasets: {
229
animation: {
@@ -26,19 +13,19 @@ export function baseDefaults() {
2613
},
2714
colors: {
2815
type: 'color',
29-
properties: colorStyleKeys,
16+
properties: colorKeys,
3017
},
3118
show: {
3219
colors: {
3320
type: 'color',
34-
properties: colorStyleKeys,
21+
properties: colorKeys,
3522
from: 'transparent',
3623
},
3724
},
3825
hide: {
3926
colors: {
4027
type: 'color',
41-
properties: colorStyleKeys,
28+
properties: colorKeys,
4229
to: 'transparent',
4330
},
4431
},
@@ -66,8 +53,7 @@ export class StatsBase extends controllers.bar {
6653
scale.axis = bak;
6754
return { min, max };
6855
}
69-
70-
parseArrayData(meta, data, start, count) {
56+
parsePrimitiveData(meta, data, start, count) {
7157
const vScale = meta.vScale;
7258
const iScale = meta.iScale;
7359
const labels = iScale.getLabels();
@@ -76,7 +62,7 @@ export class StatsBase extends controllers.bar {
7662
const index = i + start;
7763
const parsed = {};
7864
parsed[iScale.axis] = iScale.parse(labels[index], index);
79-
const stats = this._parseStats(data[index], this._config);
65+
const stats = this._parseStats(data == null ? null : data[index], this._config);
8066
if (stats) {
8167
Object.assign(parsed, stats);
8268
parsed[vScale.axis] = stats.median;
@@ -86,20 +72,24 @@ export class StatsBase extends controllers.bar {
8672
return r;
8773
}
8874

89-
_parseStats(_value, _config) {
90-
// abstract
91-
return {};
75+
parseArrayData(meta, data, start, count) {
76+
return this.parsePrimitiveData(meta, data, start, count);
9277
}
9378

9479
parseObjectData(meta, data, start, count) {
95-
return this.parseArrayData(meta, data, start, count);
80+
return this.parsePrimitiveData(meta, data, start, count);
81+
}
82+
83+
_parseStats(_value, _config) {
84+
// abstract
85+
return {};
9686
}
9787

9888
getLabelAndValue(index) {
9989
const r = super.getLabelAndValue(index);
10090
const vScale = this._cachedMeta.vScale;
10191
const parsed = this.getParsed(index);
102-
if (!vScale || !parsed) {
92+
if (!vScale || !parsed || r.value === 'NaN') {
10393
return r;
10494
}
10595
r.value = {

src/controllers/boxplot.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { asBoxPlotStats, defaultStatsOptions } from '../data';
22
import { controllers, helpers, defaults } from 'chart.js';
3-
import { baseDefaults, configKeys, StatsBase } from './base';
3+
import { baseDefaults, StatsBase } from './base';
44
import { BoxAndWiskers, boxOptionsKeys } from '../elements';
55

66
export class BoxPlot extends StatsBase {
@@ -33,7 +33,7 @@ BoxPlot.register = () => {
3333
BoxPlot.id,
3434
helpers.merge({}, [
3535
defaults.bar,
36-
baseDefaults(),
36+
baseDefaults(boxOptionsKeys),
3737
{
3838
datasets: Object.assign(
3939
{
@@ -42,7 +42,7 @@ BoxPlot.register = () => {
4242
type: 'number',
4343
properties: defaults.bar.datasets.animation.numbers.properties.concat(
4444
['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax'],
45-
configKeys.filter((c) => !c.endsWith('Color'))
45+
boxOptionsKeys.filter((c) => !c.endsWith('Color'))
4646
),
4747
},
4848
},
@@ -76,7 +76,7 @@ HorizontalBoxPlot.register = () => {
7676
HorizontalBoxPlot.id,
7777
helpers.merge({}, [
7878
defaults.horizontalBar,
79-
baseDefaults(),
79+
baseDefaults(boxOptionsKeys),
8080
{
8181
datasets: Object.assign(
8282
{
@@ -85,7 +85,7 @@ HorizontalBoxPlot.register = () => {
8585
type: 'number',
8686
properties: defaults.bar.datasets.animation.numbers.properties.concat(
8787
['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax'],
88-
configKeys.filter((c) => !c.endsWith('Color'))
88+
boxOptionsKeys.filter((c) => !c.endsWith('Color'))
8989
),
9090
},
9191
},

src/elements/boxandwhiskers.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ export class BoxAndWiskers extends StatsBase {
5555

5656
ctx.save();
5757
// fill the part below the median with lowerColor
58-
if (options.lowerColor && options.lowerColor !== 'none') {
59-
ctx.fillStyle = options.lowerColor;
58+
if (options.lowerBackgroundColor && options.lowerBackgroundColor !== 'none') {
59+
ctx.fillStyle = options.lowerBackgroundColor;
6060
if (props.q3 > props.q1) {
6161
ctx.fillRect(x0, props.median, width, props.q3 - props.median);
6262
} else {
@@ -115,8 +115,8 @@ export class BoxAndWiskers extends StatsBase {
115115

116116
ctx.save();
117117
// fill the part below the median with lowerColor
118-
if (options.lowerColor && options.lowerColor !== 'transparent') {
119-
ctx.fillStyle = options.lowerColor;
118+
if (options.lowerBackgroundColor && options.lowerBackgroundColor !== 'transparent') {
119+
ctx.fillStyle = options.lowerBackgroundColor;
120120
if (props.q3 > props.q1) {
121121
ctx.fillRect(props.median, y0, props.q3 - props.median, height);
122122
} else {
@@ -203,7 +203,7 @@ BoxAndWiskers.register = () => {
203203
defaults.set('elements', {
204204
[BoxAndWiskers._type]: Object.assign({}, baseDefaults, {
205205
medianColor: 'transparent',
206-
lowerColor: 'transparent',
206+
lowerBackgroundColor: 'transparent',
207207
}),
208208
});
209209
return BoxAndWiskers;

src/tooltip.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ import { plugins } from 'chart.js';
22

33
export function patchInHoveredOutlier(item) {
44
const value = item.value;
5-
const hoveredOutlierIndex =
6-
this._tooltipOutlier == null || item.datasetIndex !== this._tooltipOutlier.datasetIndex
7-
? -1
8-
: this._tooltipOutlier.index;
9-
// patch in the hovered outlier index
10-
value.hoveredOutlierIndex = hoveredOutlierIndex;
5+
if (value && this._tooltipOutlier != null && item.datasetIndex === this._tooltipOutlier.datasetIndex) {
6+
value.hoveredOutlierIndex = this._tooltipOutlier.index;
7+
}
118
}
129

1310
// based on average positioner but allow access to the tooltip instance

0 commit comments

Comments
 (0)