Skip to content

Commit

Permalink
Per-controller options to fix mixed charts
Browse files Browse the repository at this point in the history
  • Loading branch information
benmccann committed Feb 12, 2019
1 parent 8b07cc2 commit 5c3f88f
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 30 deletions.
31 changes: 31 additions & 0 deletions docs/configuration/README.md
Expand Up @@ -31,3 +31,34 @@ var chartDifferentHoverMode = new Chart(ctx, {
}
});
```

## Dataset Configuration

Options may be configured direction on the dataset. Dataset defaults also allow for changing options globally across a dataset type avoiding the need to specify options for each dataset instance.

Chart.js merges user-specified dataset configuration with the dataset defaults appropriately. This way you can be as specific as you would like in your individual dataset configuration, while still changing the defaults for all datasets where applicable. The dataset general options are defined in `Chart.defaults.datasets.type` where `type` corresponds to the dataset type. Dataset options take precedence over element options. If you set a dataset type default, it will override all corresponding element options. The defaults for each dataset type are discussed in the documentation for that chart type.

The following example would set the `showLine` option to 'false' for all line datasets where this was not overridden by the options passed to the dataset on creation.

```javascript
// Do not show lines for all datasets by default
Chart.defaults.datasets.line.showLine = false;

// This chart would show a line only for the third dataset
var chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
data: [0, 0],
}, {
data: [0, 1]
}, {
data: [1, 0],
showLine: true // overrides the `line` dataset default
}, {
type: 'scatter', // 'line' dataset default does not affect this dataset since it's a 'scatter'
data: [1, 1]
}]
}
});
```
21 changes: 9 additions & 12 deletions src/controllers/controller.line.js
Expand Up @@ -29,10 +29,6 @@ defaults._set('line', {
}
});

function lineEnabled(dataset, options) {
return valueOrDefault(dataset.showLine, options.showLines);
}

module.exports = DatasetController.extend({

datasetElementType: elements.Line,
Expand All @@ -44,9 +40,10 @@ module.exports = DatasetController.extend({
var meta = me.getMeta();
var line = meta.dataset;
var points = meta.data || [];
var options = me.chart.options;
var scale = me.getScaleForId(meta.yAxisID);
var dataset = me.getDataset();
var showLine = lineEnabled(dataset, me.chart.options);
var showLine = me._showLine = valueOrDefault(me._cfg.showLine, options.showLines);
var i, ilen;

// Update Line
Expand Down Expand Up @@ -179,7 +176,7 @@ module.exports = DatasetController.extend({
_resolveLineOptions: function(element) {
var me = this;
var chart = me.chart;
var dataset = chart.data.datasets[me.index];
var datasetOpts = me._cfg;
var custom = element.custom || {};
var options = chart.options;
var elementOptions = options.elements.line;
Expand All @@ -202,17 +199,17 @@ module.exports = DatasetController.extend({
key = keys[i];
values[key] = resolve([
custom[key],
dataset[key],
datasetOpts[key],
elementOptions[key]
]);
}

// The default behavior of lines is to break at null values, according
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
// This option gives lines the ability to span gaps
values.spanGaps = valueOrDefault(dataset.spanGaps, options.spanGaps);
values.tension = valueOrDefault(dataset.lineTension, elementOptions.tension);
values.steppedLine = resolve([custom.steppedLine, dataset.steppedLine, elementOptions.stepped]);
values.spanGaps = resolve([datasetOpts.spanGaps, options.spanGaps]);
values.tension = resolve([datasetOpts.tension, elementOptions.tension]);
values.steppedLine = resolve([custom.steppedLine, datasetOpts.steppedLine, elementOptions.stepped]);

return values;
},
Expand Down Expand Up @@ -311,11 +308,11 @@ module.exports = DatasetController.extend({
var meta = me.getMeta();
var points = meta.data || [];
var area = chart.chartArea;
var i = 0;
var ilen = points.length;
var halfBorderWidth;
var i = 0;

if (lineEnabled(me.getDataset(), chart.options)) {
if (me._showLine) {
halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;

helpers.canvas.clipArea(chart.ctx, {
Expand Down
8 changes: 6 additions & 2 deletions src/controllers/controller.scatter.js
Expand Up @@ -21,8 +21,6 @@ defaults._set('scatter', {
}]
},

showLines: false,

tooltips: {
callbacks: {
title: function() {
Expand All @@ -35,5 +33,11 @@ defaults._set('scatter', {
}
});

defaults._set('datasets', {
scatter: {
showLine: false
}
});

// Scatter charts use line controllers
module.exports = LineController;
1 change: 1 addition & 0 deletions src/core/core.controller.js
Expand Up @@ -417,6 +417,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
meta.controller = new ControllerClass(me, datasetIndex);
newControllers.push(meta.controller);
}
meta.controller._config();
}, me);

return newControllers;
Expand Down
23 changes: 23 additions & 0 deletions src/core/core.datasetController.js
@@ -1,5 +1,6 @@
'use strict';

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

var resolve = helpers.options.resolve;
Expand Down Expand Up @@ -100,6 +101,7 @@ helpers.extend(DatasetController.prototype, {
me.index = datasetIndex;
me.linkScales();
me.addElements();
me._type = me.getMeta().type;
},

updateIndex: function(datasetIndex) {
Expand Down Expand Up @@ -234,6 +236,27 @@ helpers.extend(DatasetController.prototype, {
me.resyncElements();
},

/**
* Returns the merged user-supplied and default dataset-level options
* @private
*/
_config: function() {
var me = this;
var dataset = me.getDataset();
var datasetDefaults = defaults.datasets[me._type];
var userOpts = {};
var keys = Object.keys(dataset);
var i, ilen, key;
for (i = 0, ilen = keys.length; i < ilen; i++) {
key = keys[i];
if (key !== '_meta' && key !== 'data') {
userOpts[key] = helpers.clone(dataset[key]);
}
}
datasetDefaults = datasetDefaults !== undefined ? helpers.clone(datasetDefaults) : {};
me._cfg = helpers.merge(datasetDefaults, userOpts);
},

update: helpers.noop,

transition: function(easingValue) {
Expand Down
90 changes: 90 additions & 0 deletions test/specs/controller.line.tests.js
Expand Up @@ -573,6 +573,96 @@ describe('Chart.controllers.line', function() {
expect(meta.dataset._model.borderWidth).toBe(0.55);
});

describe('dataset defaults', function() {
beforeEach(function() {
this._defaults = Chart.helpers.clone(Chart.defaults.datasets.line);
});

afterEach(function() {
Chart.defaults.datasets.line = this._defaults;
delete this._defaults;
});

it('should utilize the dataset default options', function() {
Chart.defaults.datasets.line = Chart.defaults.datasets.line || {};
var defaults = Chart.defaults.datasets.line;
defaults.spanGaps = true;
defaults.tension = 0.231;
defaults.backgroundColor = '#add';
defaults.borderWidth = '#daa';
defaults.borderColor = '#dad';
defaults.borderCapStyle = 'round';
defaults.borderDash = [0];
defaults.borderDashOffset = 0.871;
defaults.borderJoinStyle = 'miter';
defaults.fill = 'start';
defaults.cubicInterpolationMode = 'monotone';

var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [0, 0],
label: 'dataset1'
}],
labels: ['label1', 'label2']
}
});

var model = chart.getDatasetMeta(0).dataset._model;

expect(model.spanGaps).toBe(true);
expect(model.tension).toBe(0.231);
expect(model.backgroundColor).toBe('#add');
expect(model.borderWidth).toBe('#daa');
expect(model.borderColor).toBe('#dad');
expect(model.borderCapStyle).toBe('round');
expect(model.borderDash).toEqual([0]);
expect(model.borderDashOffset).toBe(0.871);
expect(model.borderJoinStyle).toBe('miter');
expect(model.fill).toBe('start');
expect(model.cubicInterpolationMode).toBe('monotone');
});
});

it('should obey the dataset options', function() {
var chart = window.acquireChart({
type: 'line',
data: {
datasets: [{
data: [0, 0],
label: 'dataset1',
spanGaps: true,
tension: 0.231,
backgroundColor: '#add',
borderWidth: '#daa',
borderColor: '#dad',
borderCapStyle: 'round',
borderDash: [0],
borderDashOffset: 0.871,
borderJoinStyle: 'miter',
fill: 'start',
cubicInterpolationMode: 'monotone'
}],
labels: ['label1', 'label2']
}
});

var model = chart.getDatasetMeta(0).dataset._model;

expect(model.spanGaps).toBe(true);
expect(model.tension).toBe(0.231);
expect(model.backgroundColor).toBe('#add');
expect(model.borderWidth).toBe('#daa');
expect(model.borderColor).toBe('#dad');
expect(model.borderCapStyle).toBe('round');
expect(model.borderDash).toEqual([0]);
expect(model.borderDashOffset).toBe(0.871);
expect(model.borderJoinStyle).toBe('miter');
expect(model.fill).toBe('start');
expect(model.cubicInterpolationMode).toBe('monotone');
});

it('should handle number of data point changes in update', function() {
var chart = window.acquireChart({
type: 'line',
Expand Down
23 changes: 23 additions & 0 deletions test/specs/controller.scatter.test.js
Expand Up @@ -47,5 +47,28 @@ describe('Chart.controllers.scatter', function() {
expect(meta.dataset.draw.calls.count()).toBe(0);
expect(meta.data[0].draw.calls.count()).toBe(1);
});

it('should draw a line if true', function() {
var chart = window.acquireChart({
type: 'scatter',
data: {
datasets: [{
data: [{x: 10, y: 15}],
showLine: true,
label: 'dataset1'
}],
},
options: {}
});

var meta = chart.getDatasetMeta(0);
spyOn(meta.dataset, 'draw');
spyOn(meta.data[0], 'draw');

chart.update();

expect(meta.dataset.draw.calls.count()).toBe(1);
expect(meta.data[0].draw.calls.count()).toBe(1);
});
});
});
21 changes: 5 additions & 16 deletions test/specs/core.controller.tests.js
Expand Up @@ -64,33 +64,22 @@ describe('Chart', function() {
});

it('should initialize config with default options', function() {
var callback = function() {};

var defaults = Chart.defaults;
defaults.global.responsiveAnimationDuration = 42;
defaults.global.hover.onHover = callback;
defaults.line.hover.mode = 'x-axis';
defaults.line.spanGaps = true;

var chart = acquireChart({
type: 'line'
});

var options = chart.options;
expect(options.defaultFontSize).toBe(defaults.global.defaultFontSize);
expect(options.showLines).toBe(defaults.line.showLines);
expect(options.spanGaps).toBe(true);
expect(options.responsiveAnimationDuration).toBe(42);
expect(options.hover.onHover).toBe(callback);
expect(options.hover.mode).toBe('x-axis');
expect(options.showLines).toBe(defaults.global.showLines);
expect(options.spanGaps).toBe(defaults.line.spanGaps);
expect(options.responsiveAnimationDuration).toBe(defaults.global.responsiveAnimationDuration);
expect(options.hover.onHover).toBe(defaults.global.hover.onHover);
expect(options.hover.mode).toBe(defaults.line.hover.mode);
});

it('should override default options', function() {
var defaults = Chart.defaults;
defaults.global.responsiveAnimationDuration = 42;
defaults.line.hover.mode = 'x-axis';
defaults.line.spanGaps = true;

var chart = acquireChart({
type: 'line',
options: {
Expand Down

0 comments on commit 5c3f88f

Please sign in to comment.