From e1020fefe2090727157b242a46d5edc8ecf3b023 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sun, 10 Feb 2019 16:16:42 -0800 Subject: [PATCH 01/11] Per-controller options to fix mixed charts --- docs/configuration/README.md | 31 +++++++++ src/controllers/controller.line.js | 20 +++--- src/controllers/controller.scatter.js | 8 ++- src/core/core.controller.js | 1 + src/core/core.datasetController.js | 23 +++++++ test/specs/controller.line.tests.js | 90 +++++++++++++++++++++++++++ test/specs/controller.scatter.test.js | 23 +++++++ test/specs/core.controller.tests.js | 4 ++ 8 files changed, 187 insertions(+), 13 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 0a4dee450c9..db25736f1d2 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -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] + }] + } +}); +``` diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 8bc14c3decd..6c18562d45a 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -29,10 +29,6 @@ defaults._set('line', { } }); -function lineEnabled(dataset, options) { - return valueOrDefault(dataset.showLine, options.showLines); -} - module.exports = DatasetController.extend({ datasetElementType: elements.Line, @@ -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 @@ -181,6 +178,7 @@ module.exports = DatasetController.extend({ var chart = me.chart; var datasetIndex = me.index; var dataset = chart.data.datasets[datasetIndex]; + var datasetOpts = me._cfg; var custom = element.custom || {}; var options = chart.options; var elementOptions = options.elements.line; @@ -210,7 +208,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], elementOptions[key] ], context); } @@ -218,9 +216,9 @@ module.exports = DatasetController.extend({ // 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; }, @@ -319,11 +317,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, { diff --git a/src/controllers/controller.scatter.js b/src/controllers/controller.scatter.js index 319296d33ba..7cdc088490a 100644 --- a/src/controllers/controller.scatter.js +++ b/src/controllers/controller.scatter.js @@ -21,8 +21,6 @@ defaults._set('scatter', { }] }, - showLines: false, - tooltips: { callbacks: { title: function() { @@ -35,5 +33,11 @@ defaults._set('scatter', { } }); +defaults._set('datasets', { + scatter: { + showLine: false + } +}); + // Scatter charts use line controllers module.exports = LineController; diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 92cfcd01794..139c68c8df1 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -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; diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index afd021c1db1..4486755c830 100644 --- a/src/core/core.datasetController.js +++ b/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; @@ -100,6 +101,7 @@ helpers.extend(DatasetController.prototype, { me.index = datasetIndex; me.linkScales(); me.addElements(); + me._type = me.getMeta().type; }, updateIndex: function(datasetIndex) { @@ -236,6 +238,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) { diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index 285f5c988a2..6599abf59fb 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -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', diff --git a/test/specs/controller.scatter.test.js b/test/specs/controller.scatter.test.js index bc1ff18bf49..a2744b9e437 100644 --- a/test/specs/controller.scatter.test.js +++ b/test/specs/controller.scatter.test.js @@ -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); + }); }); }); diff --git a/test/specs/core.controller.tests.js b/test/specs/core.controller.tests.js index 14be7c0b1c6..9820d4f3aca 100644 --- a/test/specs/core.controller.tests.js +++ b/test/specs/core.controller.tests.js @@ -91,9 +91,11 @@ describe('Chart', function() { }); it('should override 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; @@ -113,11 +115,13 @@ describe('Chart', function() { var options = chart.options; expect(options.responsiveAnimationDuration).toBe(4242); + expect(options.showLines).toBe(defaults.global.showLines); expect(options.spanGaps).toBe(false); expect(options.hover.mode).toBe('dataset'); expect(options.title.position).toBe('bottom'); defaults.global.responsiveAnimationDuration = 0; + defaults.global.hover.onHover = null; defaults.line.hover.mode = 'label'; defaults.line.spanGaps = false; }); From 642183f97727460f64daed1a28d2dadbb37e36bf Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 11 Feb 2019 20:45:15 -0800 Subject: [PATCH 02/11] Move dataset defaults under global --- docs/configuration/README.md | 4 ++-- src/controllers/controller.scatter.js | 8 +++++--- src/core/core.datasetController.js | 2 +- test/specs/controller.line.tests.js | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index db25736f1d2..65a784ec482 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -36,13 +36,13 @@ var chartDifferentHoverMode = new Chart(ctx, { 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. +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.global.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; +Chart.defaults.global.datasets.line.showLine = false; // This chart would show a line only for the third dataset var chart = new Chart(ctx, { diff --git a/src/controllers/controller.scatter.js b/src/controllers/controller.scatter.js index 7cdc088490a..177e563203f 100644 --- a/src/controllers/controller.scatter.js +++ b/src/controllers/controller.scatter.js @@ -33,9 +33,11 @@ defaults._set('scatter', { } }); -defaults._set('datasets', { - scatter: { - showLine: false +defaults._set('global', { + datasets: { + scatter: { + showLine: false + } } }); diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 4486755c830..0c214ad883b 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -245,7 +245,7 @@ helpers.extend(DatasetController.prototype, { _config: function() { var me = this; var dataset = me.getDataset(); - var datasetDefaults = defaults.datasets[me._type]; + var datasetDefaults = defaults.global.datasets[me._type]; var userOpts = {}; var keys = Object.keys(dataset); var i, ilen, key; diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index 6599abf59fb..9c4d5f687f4 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -575,17 +575,17 @@ describe('Chart.controllers.line', function() { describe('dataset defaults', function() { beforeEach(function() { - this._defaults = Chart.helpers.clone(Chart.defaults.datasets.line); + this._defaults = Chart.helpers.clone(Chart.defaults.global.datasets.line); }); afterEach(function() { - Chart.defaults.datasets.line = this._defaults; + Chart.defaults.global.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; + Chart.defaults.global.datasets.line = Chart.defaults.global.datasets.line || {}; + var defaults = Chart.defaults.global.datasets.line; defaults.spanGaps = true; defaults.tension = 0.231; defaults.backgroundColor = '#add'; From 82f948bc434c9528116b90853313a824d08d7e02 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 12 Feb 2019 09:07:46 -0800 Subject: [PATCH 03/11] Improve calling configure method --- src/core/core.controller.js | 3 +-- src/core/core.datasetController.js | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 139c68c8df1..8abe3978e1a 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -417,7 +417,6 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { meta.controller = new ControllerClass(me, datasetIndex); newControllers.push(meta.controller); } - meta.controller._config(); }, me); return newControllers; @@ -568,7 +567,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ { return; } - meta.controller.update(); + meta.controller._update(); plugins.notify(me, 'afterDatasetUpdate', [args]); }, diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 0c214ad883b..45f3536ba5d 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -242,7 +242,7 @@ helpers.extend(DatasetController.prototype, { * Returns the merged user-supplied and default dataset-level options * @private */ - _config: function() { + _configure: function() { var me = this; var dataset = me.getDataset(); var datasetDefaults = defaults.global.datasets[me._type]; @@ -259,6 +259,12 @@ helpers.extend(DatasetController.prototype, { me._cfg = helpers.merge(datasetDefaults, userOpts); }, + _update: function() { + var me = this; + me._configure(); + me.update(); + }, + update: helpers.noop, transition: function(easingValue) { From 5ace65a74e207174d50705bc164a05d24d746b94 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 13 Feb 2019 05:59:47 -0800 Subject: [PATCH 04/11] Add TODO --- src/core/core.defaults.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/core.defaults.js b/src/core/core.defaults.js index 279491debae..54aa6c93ad1 100644 --- a/src/core/core.defaults.js +++ b/src/core/core.defaults.js @@ -11,6 +11,8 @@ var defaults = { } }; +// TODO(v3): remove 'global' from namespace. all default are global and +// there's inconsistency around whih options are under 'global' defaults._set('global', { defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', From d8e33f0f6bd9bf559d85e79f8741af062dca2800 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sun, 24 Feb 2019 06:51:58 -0800 Subject: [PATCH 05/11] Update documentation --- docs/configuration/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 65a784ec482..42dbb67cd9f 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -34,9 +34,9 @@ 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. +Options may be configured directly on the dataset. The default option values can be changed globally across a dataset type. E.g. an option may be given a default value for all `line` datasets as demonstrated below. -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.global.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. +Chart.js merges user-specified dataset configuration with the dataset defaults. 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.global.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. From bc8ebb6f2cb8202e7694fef9061595e22d77859b Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 27 Mar 2019 08:13:07 -0700 Subject: [PATCH 06/11] Address minor review comments --- src/controllers/controller.line.js | 4 ++-- src/core/core.datasetController.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 6c18562d45a..036da7d165d 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -216,8 +216,8 @@ module.exports = DatasetController.extend({ // 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 = resolve([datasetOpts.spanGaps, options.spanGaps]); - values.tension = resolve([datasetOpts.tension, elementOptions.tension]); + values.spanGaps = valueOrDefault(datasetOpts.spanGaps, options.spanGaps); + values.tension = valueOrDefault(datasetOpts.tension, elementOptions.tension); values.steppedLine = resolve([custom.steppedLine, datasetOpts.steppedLine, elementOptions.stepped]); return values; diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 45f3536ba5d..4871847612f 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -245,7 +245,7 @@ helpers.extend(DatasetController.prototype, { _configure: function() { var me = this; var dataset = me.getDataset(); - var datasetDefaults = defaults.global.datasets[me._type]; + var datasetDefaults = helpers.clone(defaults.global.datasets[me._type]) || {}; var userOpts = {}; var keys = Object.keys(dataset); var i, ilen, key; @@ -255,7 +255,6 @@ helpers.extend(DatasetController.prototype, { userOpts[key] = helpers.clone(dataset[key]); } } - datasetDefaults = datasetDefaults !== undefined ? helpers.clone(datasetDefaults) : {}; me._cfg = helpers.merge(datasetDefaults, userOpts); }, From f0898c4e2a0b6a09fb42603a088ea8b3558e332e Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 2 Apr 2019 09:19:56 -0700 Subject: [PATCH 07/11] Add ability to specify options in options.datasets.type --- docs/configuration/README.md | 2 +- src/core/core.datasetController.js | 3 +- test/specs/controller.line.tests.js | 85 ++++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 42dbb67cd9f..c4635f4c7bc 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -36,7 +36,7 @@ var chartDifferentHoverMode = new Chart(ctx, { Options may be configured directly on the dataset. The default option values can be changed globally across a dataset type. E.g. an option may be given a default value for all `line` datasets as demonstrated below. -Chart.js merges user-specified dataset configuration with the dataset defaults. 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.global.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. +Chart.js merges user-specified dataset configuration with the dataset defaults. 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.global.datasets.type` where `type` corresponds to the dataset type. You may also set `chart.options.datasets.type` to set the default at the chart level instead of globally. Dataset options take precedence over element options. If you set a dataset type default option, 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. diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 4871847612f..d814924f6cb 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -245,7 +245,8 @@ helpers.extend(DatasetController.prototype, { _configure: function() { var me = this; var dataset = me.getDataset(); - var datasetDefaults = helpers.clone(defaults.global.datasets[me._type]) || {}; + var chartDefaults = me.chart.options.datasets; + var datasetDefaults = helpers.merge({}, [defaults.global.datasets[me._type], chartDefaults ? chartDefaults[me._type] : {}]); var userOpts = {}; var keys = Object.keys(dataset); var i, ilen, key; diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index 9c4d5f687f4..9f29e47f3f0 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -573,7 +573,7 @@ describe('Chart.controllers.line', function() { expect(meta.dataset._model.borderWidth).toBe(0.55); }); - describe('dataset defaults', function() { + describe('dataset global defaults', function() { beforeEach(function() { this._defaults = Chart.helpers.clone(Chart.defaults.global.datasets.line); }); @@ -583,7 +583,7 @@ describe('Chart.controllers.line', function() { delete this._defaults; }); - it('should utilize the dataset default options', function() { + it('should utilize the dataset global default options', function() { Chart.defaults.global.datasets.line = Chart.defaults.global.datasets.line || {}; var defaults = Chart.defaults.global.datasets.line; defaults.spanGaps = true; @@ -623,6 +623,87 @@ describe('Chart.controllers.line', function() { expect(model.fill).toBe('start'); expect(model.cubicInterpolationMode).toBe('monotone'); }); + + it('should be overriden by user-supplied values', function() { + Chart.defaults.global.datasets.line = Chart.defaults.global.datasets.line || {}; + var defaults = Chart.defaults.global.datasets.line; + defaults.spanGaps = true; + defaults.tension = 0.231; + + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + data: [0, 0], + label: 'dataset1', + spanGaps: true, + backgroundColor: '#dad' + }], + labels: ['label1', 'label2'] + }, + options: { + datasets: { + line: { + tension: 0.345, + backgroundColor: '#add' + } + } + } + }); + + var model = chart.getDatasetMeta(0).dataset._model; + + // dataset-level option overrides global default + expect(model.spanGaps).toBe(true); + // chart-level default overrides global default + expect(model.tension).toBe(0.345); + // dataset-level option overrides chart-level default + expect(model.backgroundColor).toBe('#dad'); + }); + }); + + it('should obey the chart-level dataset options', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + data: [0, 0], + label: 'dataset1' + }], + labels: ['label1', 'label2'] + }, + options: { + datasets: { + line: { + spanGaps: true, + tension: 0.231, + backgroundColor: '#add', + borderWidth: '#daa', + borderColor: '#dad', + borderCapStyle: 'round', + borderDash: [0], + borderDashOffset: 0.871, + borderJoinStyle: 'miter', + fill: 'start', + cubicInterpolationMode: 'monotone' + } + } + } + }); + + 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() { From 306d96e7435ee255d7b5dc51f3c98e2e26fd46a5 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 2 Apr 2019 19:44:40 -0700 Subject: [PATCH 08/11] Address review comments --- docs/configuration/README.md | 12 +++++++--- src/controllers/controller.line.js | 4 ++-- src/core/core.datasetController.js | 22 ++++++++----------- src/core/core.defaults.js | 2 +- test/specs/controller.line.tests.js | 34 ++++++++++++++++------------- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index c4635f4c7bc..aa21f1f696e 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -34,11 +34,17 @@ var chartDifferentHoverMode = new Chart(ctx, { ## Dataset Configuration -Options may be configured directly on the dataset. The default option values can be changed globally across a dataset type. E.g. an option may be given a default value for all `line` datasets as demonstrated below. +Options may be configured directly on the dataset. The dataset options can be changed at 3 different levels and are evaluated with the following priority: -Chart.js merges user-specified dataset configuration with the dataset defaults. 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.global.datasets.type` where `type` corresponds to the dataset type. You may also set `chart.options.datasets.type` to set the default at the chart level instead of globally. Dataset options take precedence over element options. If you set a dataset type default option, it will override all corresponding element options. The defaults for each dataset type are discussed in the documentation for that chart type. +- per dataset: dataset.* +- per chart: options.datasets[type].* +- or globally: Chart.defaults.global.datalabels[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. +where type corresponds to the dataset type. + +*Note:* dataset options take precedence over element options. + +The following example would set the `showLine` option to 'false' for all line datasets except for those overridden by options passed to the dataset on creation. ```javascript // Do not show lines for all datasets by default diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 036da7d165d..0b7a2775aee 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -43,7 +43,7 @@ module.exports = DatasetController.extend({ var options = me.chart.options; var scale = me.getScaleForId(meta.yAxisID); var dataset = me.getDataset(); - var showLine = me._showLine = valueOrDefault(me._cfg.showLine, options.showLines); + var showLine = me._showLine = valueOrDefault(me._config.showLine, options.showLines); var i, ilen; // Update Line @@ -178,7 +178,7 @@ module.exports = DatasetController.extend({ var chart = me.chart; var datasetIndex = me.index; var dataset = chart.data.datasets[datasetIndex]; - var datasetOpts = me._cfg; + var datasetOpts = me._config; var custom = element.custom || {}; var options = chart.options; var elementOptions = options.elements.line; diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index d814924f6cb..6b29e88ed39 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -1,6 +1,5 @@ 'use strict'; -var defaults = require('./core.defaults'); var helpers = require('../helpers/index'); var resolve = helpers.options.resolve; @@ -244,19 +243,16 @@ helpers.extend(DatasetController.prototype, { */ _configure: function() { var me = this; - var dataset = me.getDataset(); - var chartDefaults = me.chart.options.datasets; - var datasetDefaults = helpers.merge({}, [defaults.global.datasets[me._type], chartDefaults ? chartDefaults[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]); + me._config = helpers.merge({}, [ + me.chart.options.datasets[me._type], + me.getDataset(), + ], { + merger: function(key, target, source) { + if (key !== '_meta' && key !== 'data') { + helpers._merger(key, target, source); + } } - } - me._cfg = helpers.merge(datasetDefaults, userOpts); + }); }, _update: function() { diff --git a/src/core/core.defaults.js b/src/core/core.defaults.js index 54aa6c93ad1..99e7f0e5dc7 100644 --- a/src/core/core.defaults.js +++ b/src/core/core.defaults.js @@ -12,7 +12,7 @@ var defaults = { }; // TODO(v3): remove 'global' from namespace. all default are global and -// there's inconsistency around whih options are under 'global' +// there's inconsistency around whih options are under 'global' defaults._set('global', { defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index 9f29e47f3f0..f58acb5ab17 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -585,18 +585,20 @@ describe('Chart.controllers.line', function() { it('should utilize the dataset global default options', function() { Chart.defaults.global.datasets.line = Chart.defaults.global.datasets.line || {}; - var defaults = Chart.defaults.global.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'; + + Chart.helpers.merge(Chart.defaults.global.datasets.line, { + spanGaps: true, + tension: 0.231, + backgroundColor: '#add', + borderWidth: '#daa', + borderColor: '#dad', + borderCapStyle: 'round', + borderDash: [0], + borderDashOffset: 0.871, + borderJoinStyle: 'miter', + fill: 'start', + cubicInterpolationMode: 'monotone' + }); var chart = window.acquireChart({ type: 'line', @@ -626,9 +628,11 @@ describe('Chart.controllers.line', function() { it('should be overriden by user-supplied values', function() { Chart.defaults.global.datasets.line = Chart.defaults.global.datasets.line || {}; - var defaults = Chart.defaults.global.datasets.line; - defaults.spanGaps = true; - defaults.tension = 0.231; + + Chart.helpers.merge(Chart.defaults.global.datasets.line, { + spanGaps: true, + tension: 0.231 + }); var chart = window.acquireChart({ type: 'line', From 11c4ea297b6b4bff7e6d7d7e54a828133aac36c5 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 24 Apr 2019 09:05:01 -0700 Subject: [PATCH 09/11] fix documentation typos --- docs/configuration/README.md | 2 +- src/core/core.defaults.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index aa21f1f696e..9139d5f88e1 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -38,7 +38,7 @@ Options may be configured directly on the dataset. The dataset options can be ch - per dataset: dataset.* - per chart: options.datasets[type].* -- or globally: Chart.defaults.global.datalabels[type].* +- or globally: Chart.defaults.global.datasets[type].* where type corresponds to the dataset type. diff --git a/src/core/core.defaults.js b/src/core/core.defaults.js index 99e7f0e5dc7..6c8e996a1a8 100644 --- a/src/core/core.defaults.js +++ b/src/core/core.defaults.js @@ -12,7 +12,7 @@ var defaults = { }; // TODO(v3): remove 'global' from namespace. all default are global and -// there's inconsistency around whih options are under 'global' +// there's inconsistency around which options are under 'global' defaults._set('global', { defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', From bda257d9ddae73c6cf76db6b8f0ea6d252406bbd Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 26 Apr 2019 09:02:50 -0700 Subject: [PATCH 10/11] Fix accidental renaming of lineTension option --- src/controllers/controller.line.js | 2 +- test/specs/controller.line.tests.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 0b7a2775aee..c6458c7a32b 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -217,7 +217,7 @@ module.exports = DatasetController.extend({ // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps values.spanGaps = valueOrDefault(datasetOpts.spanGaps, options.spanGaps); - values.tension = valueOrDefault(datasetOpts.tension, elementOptions.tension); + values.tension = valueOrDefault(datasetOpts.lineTension, elementOptions.tension); values.steppedLine = resolve([custom.steppedLine, datasetOpts.steppedLine, elementOptions.stepped]); return values; diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index f58acb5ab17..953594deac1 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -588,7 +588,7 @@ describe('Chart.controllers.line', function() { Chart.helpers.merge(Chart.defaults.global.datasets.line, { spanGaps: true, - tension: 0.231, + lineTension: 0.231, backgroundColor: '#add', borderWidth: '#daa', borderColor: '#dad', @@ -631,7 +631,7 @@ describe('Chart.controllers.line', function() { Chart.helpers.merge(Chart.defaults.global.datasets.line, { spanGaps: true, - tension: 0.231 + lineTension: 0.231 }); var chart = window.acquireChart({ @@ -648,7 +648,7 @@ describe('Chart.controllers.line', function() { options: { datasets: { line: { - tension: 0.345, + lineTension: 0.345, backgroundColor: '#add' } } @@ -680,7 +680,7 @@ describe('Chart.controllers.line', function() { datasets: { line: { spanGaps: true, - tension: 0.231, + lineTension: 0.231, backgroundColor: '#add', borderWidth: '#daa', borderColor: '#dad', @@ -718,7 +718,7 @@ describe('Chart.controllers.line', function() { data: [0, 0], label: 'dataset1', spanGaps: true, - tension: 0.231, + lineTension: 0.231, backgroundColor: '#add', borderWidth: '#daa', borderColor: '#dad', From 5bb98a8c950ae9c811dfbd6b564ef7ece4a5162a Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 26 Apr 2019 21:07:36 -0700 Subject: [PATCH 11/11] Use dataset defaults in all controllers --- src/controllers/controller.bar.js | 3 ++- src/controllers/controller.bubble.js | 3 ++- src/controllers/controller.doughnut.js | 10 ++++++++-- src/controllers/controller.line.js | 5 +++-- src/controllers/controller.polarArea.js | 3 ++- src/controllers/controller.radar.js | 8 +++++--- src/core/core.datasetController.js | 6 +++--- test/specs/controller.radar.tests.js | 6 +++--- 8 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 992333a25c2..7130a9d0905 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -382,6 +382,7 @@ module.exports = DatasetController.extend({ var chart = me.chart; var datasets = chart.data.datasets; var dataset = datasets[me.index]; + var datasetOpts = me._config; var custom = rectangle.custom || {}; var options = chart.options.elements.rectangle; var values = {}; @@ -406,7 +407,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], options[key] ], context, index); } diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index d40b4393b51..d724432f446 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -127,6 +127,7 @@ module.exports = DatasetController.extend({ var chart = me.chart; var datasets = chart.data.datasets; var dataset = datasets[me.index]; + var datasetOpts = me._config; var custom = point.custom || {}; var options = chart.options.elements.point; var data = dataset.data[index]; @@ -158,7 +159,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], options[key] ], context, index); } diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index a6d4a63775a..bcb989c8589 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -305,7 +305,12 @@ module.exports = DatasetController.extend({ for (i = 0, ilen = arcs.length; i < ilen; ++i) { arc = arcs[i]; - options = controller ? controller._resolveElementOptions(arc, i) : arc._options; + if (controller) { + controller._configure(); + options = controller._resolveElementOptions(arc, i); + } else { + options = arc._options; + } if (options.borderAlign !== 'inner') { borderWidth = options.borderWidth; hoverWidth = options.hoverBorderWidth; @@ -343,6 +348,7 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var dataset = me.getDataset(); + var datasetOpts = me._config; var custom = arc.custom || {}; var options = chart.options.elements.arc; var values = {}; @@ -370,7 +376,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], options[key] ], context, index); } diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index c6458c7a32b..c671d657933 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -129,6 +129,7 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var dataset = chart.data.datasets[me.index]; + var datasetOpts = me._config; var custom = element.custom || {}; var options = chart.options.elements.point; var values = {}; @@ -161,8 +162,8 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[ELEMENT_OPTIONS[key]], - dataset[key], + datasetOpts[ELEMENT_OPTIONS[key]], + datasetOpts[key], options[key] ], context, index); } diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 19ceefddab7..582a0a78bf7 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -248,6 +248,7 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var dataset = me.getDataset(); + var datasetOpts = me._config; var custom = arc.custom || {}; var options = chart.options.elements.arc; var values = {}; @@ -275,7 +276,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], options[key] ], context, index); } diff --git a/src/controllers/controller.radar.js b/src/controllers/controller.radar.js index 770ddc51afe..8322a630f81 100644 --- a/src/controllers/controller.radar.js +++ b/src/controllers/controller.radar.js @@ -109,6 +109,7 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var dataset = chart.data.datasets[me.index]; + var datasetOpts = me._config; var custom = element.custom || {}; var options = chart.options.elements.point; var values = {}; @@ -141,8 +142,8 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[ELEMENT_OPTIONS[key]], - dataset[key], + datasetOpts[ELEMENT_OPTIONS[key]], + datasetOpts[key], options[key] ], context, index); } @@ -157,6 +158,7 @@ module.exports = DatasetController.extend({ var me = this; var chart = me.chart; var dataset = chart.data.datasets[me.index]; + var datasetOpts = me._config; var custom = element.custom || {}; var options = chart.options.elements.line; var values = {}; @@ -177,7 +179,7 @@ module.exports = DatasetController.extend({ key = keys[i]; values[key] = resolve([ custom[key], - dataset[key], + datasetOpts[key], options[key] ]); } diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 6b29e88ed39..45a2e894236 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -161,7 +161,7 @@ helpers.extend(DatasetController.prototype, { }, reset: function() { - this.update(true); + this._update(true); }, /** @@ -255,10 +255,10 @@ helpers.extend(DatasetController.prototype, { }); }, - _update: function() { + _update: function(reset) { var me = this; me._configure(); - me.update(); + me.update(reset); }, update: helpers.noop, diff --git a/test/specs/controller.radar.tests.js b/test/specs/controller.radar.tests.js index 6b2c754547c..41141a8cd1d 100644 --- a/test/specs/controller.radar.tests.js +++ b/test/specs/controller.radar.tests.js @@ -154,7 +154,7 @@ describe('Chart.controllers.radar', function() { }); // Now update controller and ensure proper updates - meta.controller.update(); + meta.controller._update(); [ {x: 256, y: 120, cppx: 246, cppy: 120, cpnx: 272, cpny: 120}, @@ -198,7 +198,7 @@ describe('Chart.controllers.radar', function() { chart.data.datasets[0].pointBorderColor = 'rgb(56, 57, 58)'; chart.data.datasets[0].pointBorderWidth = 1.123; - meta.controller.update(); + meta.controller._update(); expect(meta.dataset._model).toEqual(jasmine.objectContaining({ backgroundColor: 'rgb(98, 98, 98)', @@ -256,7 +256,7 @@ describe('Chart.controllers.radar', function() { hitRadius: 5, }; - meta.controller.update(); + meta.controller._update(); expect(meta.dataset._model).toEqual(jasmine.objectContaining({ backgroundColor: 'rgb(55, 55, 54)',