]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Relocate chart type and dataset type defaults (#8563)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Sat, 6 Mar 2021 15:34:52 +0000 (17:34 +0200)
committerGitHub <noreply@github.com>
Sat, 6 Mar 2021 15:34:52 +0000 (10:34 -0500)
* Relocate chart type and dataset type defaults

* Update types

* Separate overrides and descriptors

* Update derived sample, use merge for inherit

* Don't merge overrides

* Review update

36 files changed:
docs/docs/charts/bar.mdx
docs/docs/charts/bubble.mdx
docs/docs/charts/doughnut.mdx
docs/docs/charts/line.mdx
docs/docs/charts/polar.mdx
docs/docs/charts/radar.mdx
docs/docs/configuration/animations.mdx
docs/docs/configuration/index.md
docs/docs/general/options.md
docs/docs/getting-started/v3-migration.md
samples/advanced/derived-chart-type.html
samples/charts/multi-series-pie.html
src/controllers/controller.bar.js
src/controllers/controller.bubble.js
src/controllers/controller.doughnut.js
src/controllers/controller.line.js
src/controllers/controller.pie.js
src/controllers/controller.polarArea.js
src/controllers/controller.radar.js
src/controllers/controller.scatter.js
src/core/core.config.js
src/core/core.controller.js
src/core/core.defaults.js
src/core/core.registry.js
src/core/core.typedRegistry.js
test/specs/controller.bar.tests.js
test/specs/controller.line.tests.js
test/specs/core.controller.tests.js
test/specs/core.registry.tests.js
test/specs/global.defaults.tests.js
test/specs/platform.dom.tests.js
types/index.esm.d.ts
types/tests/defaults.ts [new file with mode: 0644]
types/tests/options.ts [new file with mode: 0644]
types/tests/overrides.ts [new file with mode: 0644]
types/tests/plugins/plugin.tooltip/tooltip_parsed_data_chart_defaults.ts

index 22ea1936187cd7a9b089011efe5a423be87547ca..cce21feb2f1bfed2a7c50884d6d9eed9b5cb1f37 100644 (file)
@@ -235,7 +235,7 @@ If true, the bars for a particular data point fall between the grid lines. The g
 
 ## Default Options
 
-It is common to want to apply a configuration setting to all created bar charts. The global bar chart settings are stored in `Chart.defaults.controllers.bar`. Changing the global options only affects charts created after the change. Existing charts are not changed.
+It is common to want to apply a configuration setting to all created bar charts. The global bar chart settings are stored in `Chart.overrides.bar`. Changing the global options only affects charts created after the change. Existing charts are not changed.
 
 ## barPercentage vs categoryPercentage
 
index 9c42ab33a2c0eefddfa0500162e802290d1e81e5..fff1413b721853f2a0e6f39d2d2300e7386a6518 100644 (file)
@@ -106,7 +106,7 @@ All these values, if `undefined`, fallback to the associated [`elements.point.*`
 
 ## Default Options
 
-We can also change the default values for the Bubble chart type. Doing so will give all bubble charts created after this point the new defaults. The default configuration for the bubble chart can be accessed at `Chart.defaults.controllers.bubble`.
+We can also change the default values for the Bubble chart type. Doing so will give all bubble charts created after this point the new defaults. The default configuration for the bubble chart can be accessed at `Chart.overrides.bubble`.
 
 ## Data Structure
 
index 02838865dd0d4268bfc18d6839bee6842e82161e..6dc8948ef1c0c32134dbc5cfd07f8ece2829f33a 100644 (file)
@@ -172,7 +172,7 @@ These are the customisation options specific to Pie & Doughnut charts. These opt
 
 ## Default Options
 
-We can also change these default values for each Doughnut type that is created, this object is available at `Chart.defaults.controllers.doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.defaults.controllers.pie`, with the only difference being `cutout` being set to 0.
+We can also change these default values for each Doughnut type that is created, this object is available at `Chart.overrides.doughnut`. Pie charts also have a clone of these defaults available to change at `Chart.overrides.pie`, with the only difference being `cutout` being set to 0.
 
 ## Data Structure
 
index 2a265c0a3211d8d14b1fd6831e59655ca39c2fdb..7a40305228882774c3f5a2c9881381759252ccd5 100644 (file)
@@ -188,12 +188,12 @@ The line chart defines the following configuration options. These options are lo
 
 ## Default Options
 
-It is common to want to apply a configuration setting to all created line charts. The global line chart settings are stored in `Chart.defaults.controllers.line`. Changing the global options only affects charts created after the change. Existing charts are not changed.
+It is common to want to apply a configuration setting to all created line charts. The global line chart settings are stored in `Chart.overrides.line`. Changing the global options only affects charts created after the change. Existing charts are not changed.
 
 For example, to configure all line charts with `spanGaps = true` you would do:
 
 ```javascript
-Chart.defaults.controllers.line.spanGaps = true;
+Chart.overrides.line.spanGaps = true;
 ```
 
 ## Data Structure
index 17c5d4fa32a49ddeb6374a5fddcfbcea15b34697..e1644fbd8e37c82ebf58eebf6dea7c13dcca5857 100644 (file)
@@ -120,12 +120,12 @@ The polar area chart uses the [radialLinear](../axes/radial/linear.mdx) scale. A
 
 ## Default Options
 
-We can also change these default values for each PolarArea type that is created, this object is available at `Chart.defaults.controllers.polarArea`. Changing the global options only affects charts created after the change. Existing charts are not changed.
+We can also change these default values for each PolarArea type that is created, this object is available at `Chart.overrides.polarArea`. Changing the global options only affects charts created after the change. Existing charts are not changed.
 
 For example, to configure all new polar area charts with `animateScale = false` you would do:
 
 ```javascript
-Chart.defaults.controllers.polarArea.animation.animateScale = false;
+Chart.overrides.polarArea.animation.animateScale = false;
 ```
 
 ## Data Structure
index 23ee58d7aacc99fc46203448f90a7ea156b2b1ab..316eadb1430b0664b920d62b286e5d70a680fafa 100644 (file)
@@ -193,7 +193,7 @@ options = {
 
 ## Default Options
 
-It is common to want to apply a configuration setting to all created radar charts. The global radar chart settings are stored in `Chart.defaults.controllers.radar`. Changing the global options only affects charts created after the change. Existing charts are not changed.
+It is common to want to apply a configuration setting to all created radar charts. The global radar chart settings are stored in `Chart.overrides.radar`. Changing the global options only affects charts created after the change. Existing charts are not changed.
 
 ## Data Structure
 
index 65ea5f67f1ec0cb9c059b2659ff08d1dfc012727..16826616bb1f31f39bfab0321b5dc5ae20fabb4a 100644 (file)
@@ -86,7 +86,7 @@ function example() {
               y: {
                   from: 0
               }
-            }  
+            }
           },
           hide: {
             animations: {
@@ -124,9 +124,8 @@ Animation configuration consists of 3 keys.
 These keys can be configured in following paths:
 
 * `` - chart options
-* `controllers[type]` - controller type options
-* `controllers[type].datasets` - dataset type options
 * `datasets[type]` - dataset type options
+* `overrides[type]` - chart type options
 
 These paths are valid under `defaults` for global confuguration and `options` for instance configuration.
 
index ce8074f5c37442ee09912269461eb22f3fbcff88..6c429ed951d7f76d450be71d3e62d5308ac42625 100644 (file)
@@ -42,7 +42,7 @@ The following example would set the `showLine` option to 'false' for all line da
 
 ```javascript
 // Do not show lines for all datasets by default
-Chart.defaults.controllers.line.showLine = false;
+Chart.defaults.datasets.line.showLine = false;
 
 // This chart would show a line only for the third dataset
 var chart = new Chart(ctx, {
index e5906a2fede244801b783ef91f3665f6c0dc1f59..092d8c8351d90e7b83409d93a90f7d5f3f8d2c16 100644 (file)
@@ -9,7 +9,7 @@ Options are resolved from top to bottom, using a context dependent route.
 ### Chart level options
 
 * options
-* defaults.controllers[`config.type`]
+* overrides[`config.type`]
 * defaults
 
 ### Dataset level options
@@ -18,18 +18,18 @@ Options are resolved from top to bottom, using a context dependent route.
 
 * dataset
 * options.datasets[`dataset.type`]
-* options.controllers[`dataset.type`].datasets
 * options
+* overrides[`config.type`].datasets[`dataset.type`]
 * defaults.datasets[`dataset.type`]
-* defaults.controllers[`dataset.type`].datasets
 * defaults
 
 ### Dataset animation options
 
 * dataset.animation
-* options.controllers[`dataset.type`].datasets.animation
+* options.datasets[`dataset.type`].animation
 * options.animation
-* defaults.controllers[`dataset.type`].datasets.animation
+* overrides[`config.type`].datasets[`dataset.type`].animation
+* defaults.datasets[`dataset.type`].animation
 * defaults.animation
 
 ### Dataset element level options
@@ -38,31 +38,30 @@ Each scope is looked up with `elementType` prefix in the option name first, then
 
 * dataset
 * options.datasets[`dataset.type`]
-* options.controllers[`dataset.type`].datasets
-* options.controllers[`dataset.type`].elements[`elementType`]
+* options.datasets[`dataset.type`].elements[`elementType`]
 * options.elements[`elementType`]
 * options
+* overrides[`config.type`].datasets[`dataset.type`]
+* overrides[`config.type`].datasets[`dataset.type`].elements[`elementType`]
 * defaults.datasets[`dataset.type`]
-* defaults.controllers[`dataset.type`].datasets
-* defaults.controllers[`dataset.type`].elements[`elementType`]
+* defaults.datasets[`dataset.type`].elements[`elementType`]
 * defaults.elements[`elementType`]
 * defaults
 
 ### Scale options
 
 * options.scales
-* defaults.controllers[`config.type`].scales
-* defaults.controllers[`dataset.type`].scales
+* overrides[`config.type`].scales
 * defaults.scales
+* defaults.scale
 
 ### Plugin options
 
 A plugin can provide `additionalOptionScopes` array of paths to additionally look for its options in. For root scope, use empty string: `''`. Most core plugins also take options from root scope.
 
 * options.plugins[`plugin.id`]
-* options.controllers[`config.type`].plugins[`plugin.id`]
 * (options.[`...plugin.additionalOptionScopes`])
-* defaults.controllers[`config.type`].plugins[`plugin.id`]
+* overrides[`config.type`].plugins[`plugin.id`]
 * defaults.plugins[`plugin.id`]
 * (defaults.[`...plugin.additionalOptionScopes`])
 
index fc03f38645f5c45cd12b383461cd122c3f24ab21..1aa12040f6d69d0daf057ba5dd5f79e50238cdf8 100644 (file)
@@ -111,7 +111,7 @@ A number of changes were made to the configuration options passed to the `Chart`
 #### Defaults
 
 * `global` namespace was removed from `defaults`. So `Chart.defaults.global` is now `Chart.defaults`
-* Dataset controller defaults were relocate to `controllers`. For example `Chart.defaults.line` is now `Chart.defaults.controllers.line`
+* Dataset controller defaults were relocate to `overrides`. For example `Chart.defaults.line` is now `Chart.overrides.line`
 * `default` prefix was removed from defaults. For example `Chart.defaults.global.defaultColor` is now `Chart.defaults.color`
 * `defaultColor` was split to `color`, `borderColor` and `backgroundColor`
 * `defaultFontColor` was renamed to `color`
index 3bb7a1dbb2f872e48527ccfb92dc401ac0f9aab9..9f6cb205b23ac119e834011e00957c12d4c4333f 100644 (file)
@@ -24,7 +24,7 @@
                                // Call bubble controller method to draw all the points
                                super.draw(arguments);
 
-                               // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
+                               // Now we can do some custom drawing for this dataset. Here we'll draw a box around the first point in each dataset, using `boxStrokeStyle` dataset option for color
                                var meta = this.getMeta();
                                var pt0 = meta.data[0];
 
 
                                var ctx = this.chart.ctx;
                                ctx.save();
-                               ctx.strokeStyle = 'red';
+                               ctx.strokeStyle = this.options.boxStrokeStyle;
                                ctx.lineWidth = 1;
                                ctx.strokeRect(x - radius, y - radius, 2 * radius, 2 * radius);
                                ctx.restore();
                        }
                }
                Custom.id = 'derivedBubble';
-               Custom.defaults = Chart.controllers.bubble.defaults;
+               Custom.defaults = {
+                       // Custom defaults. Bubble defaults are inherited.
+                       boxStrokeStyle: 'red'
+               };
+               // Overrides are only inherited, but not merged if defined
+               // Custom.overrides = Chart.overrides.bubble;
 
                // Stores the controller so that the chart initialization routine can look it up
                Chart.register(Custom);
index c4952b112786e94590fffbc67081ad76d91c1e9c..13adb98f18fa6880c13cdaf8a695b52a98388306 100644 (file)
@@ -48,7 +48,7 @@
                                                labels: {
                                                        generateLabels: function(chart) {
                                                                // Get the default label list
-                                                               var original = Chart.defaults.controllers.pie.plugins.legend.labels.generateLabels;
+                                                               var original = Chart.overrides.pie.plugins.legend.labels.generateLabels;
                                                                var labels = original.call(this, chart);
 
                                                                // Build an array of colors used in the datasets of the chart
index 94d7a5313eecf247a1ccaa234963466866be32ba..9bf5ef32e4c3adbb39d2e0907be2e3c8ad5412ab 100644 (file)
@@ -511,23 +511,27 @@ BarController.defaults = {
   datasetElementType: false,
   dataElementType: 'bar',
 
+  categoryPercentage: 0.8,
+  barPercentage: 0.9,
+
+  animations: {
+    numbers: {
+      type: 'number',
+      properties: ['x', 'y', 'base', 'width', 'height']
+    }
+  }
+};
+
+/**
+ * @type {any}
+ */
+BarController.overrides = {
   interaction: {
     mode: 'index'
   },
 
   hover: {},
 
-  datasets: {
-    categoryPercentage: 0.8,
-    barPercentage: 0.9,
-    animations: {
-      numbers: {
-        type: 'number',
-        properties: ['x', 'y', 'base', 'width', 'height']
-      }
-    }
-  },
-
   scales: {
     _index_: {
       type: 'category',
index 348ea8866d7f1f9f3270d05b713d17cebe519e71..08632e72bb17b2b196be7dd7b3c55097aeb72930 100644 (file)
@@ -133,12 +133,19 @@ BubbleController.id = 'bubble';
 BubbleController.defaults = {
   datasetElementType: false,
   dataElementType: 'point',
+
   animations: {
     numbers: {
       type: 'number',
       properties: ['x', 'y', 'borderWidth', 'radius']
     }
-  },
+  }
+};
+
+/**
+ * @type {any}
+ */
+BubbleController.overrides = {
   scales: {
     x: {
       type: 'linear'
index ef38f02250edf6ea5e43f22e42e3b7d1696b1fe3..a8a3fbcbdf1ddb71b937ebe85ee062735e7c61b2 100644 (file)
@@ -328,23 +328,26 @@ DoughnutController.defaults = {
       properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth']
     },
   },
-  aspectRatio: 1,
-
-  datasets: {
-    // The percentage of the chart that we cut out of the middle.
-    cutout: '50%',
+  // The percentage of the chart that we cut out of the middle.
+  cutout: '50%',
 
-    // The rotation of the chart, where the first data arc begins.
-    rotation: 0,
+  // The rotation of the chart, where the first data arc begins.
+  rotation: 0,
 
-    // The total circumference of the chart.
-    circumference: 360,
+  // The total circumference of the chart.
+  circumference: 360,
 
-    // The outr radius of the chart
-    radius: '100%'
-  },
+  // The outr radius of the chart
+  radius: '100%',
 
   indexAxis: 'r',
+};
+
+/**
+ * @type {any}
+ */
+DoughnutController.overrides = {
+  aspectRatio: 1,
 
   // Need to override these to give a nice default
   plugins: {
index e6245295cd66180becaf994d14d60ffeaa99d3c1..ca56046e802199fe13f603676e9e539fba59f3fb 100644 (file)
@@ -111,11 +111,14 @@ LineController.defaults = {
   datasetElementType: 'line',
   dataElementType: 'point',
 
-  datasets: {
-    showLine: true,
-    spanGaps: false,
-  },
+  showLine: true,
+  spanGaps: false,
+};
 
+/**
+ * @type {any}
+ */
+LineController.overrides = {
   interaction: {
     mode: 'index'
   },
index a53b8d5f9eda297507dceb00ec08e24094886b44..f0ba83e15a1f2b34a227df34adbe049dd0751482 100644 (file)
@@ -11,17 +11,15 @@ PieController.id = 'pie';
  * @type {any}
  */
 PieController.defaults = {
-  datasets: {
-    // The percentage of the chart that we cut out of the middle.
-    cutout: 0,
+  // The percentage of the chart that we cut out of the middle.
+  cutout: 0,
 
-    // The rotation of the chart, where the first data arc begins.
-    rotation: 0,
+  // The rotation of the chart, where the first data arc begins.
+  rotation: 0,
 
-    // The total circumference of the chart.
-    circumference: 360,
+  // The total circumference of the chart.
+  circumference: 360,
 
-    // The outr radius of the chart
-    radius: '100%'
-  }
+  // The outr radius of the chart
+  radius: '100%'
 };
index 49ac298987c19d038310cd0856ec19ff032f247b..ec09716a8304cbfc12f6670842ccfcc9155a6fc2 100644 (file)
@@ -131,25 +131,16 @@ PolarAreaController.defaults = {
       properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius']
     },
   },
-  aspectRatio: 1,
   indexAxis: 'r',
-  scales: {
-    r: {
-      type: 'radialLinear',
-      angleLines: {
-        display: false
-      },
-      beginAtZero: true,
-      gridLines: {
-        circular: true
-      },
-      pointLabels: {
-        display: false
-      }
-    }
-  },
-
   startAngle: 0,
+};
+
+/**
+ * @type {any}
+ */
+PolarAreaController.overrides = {
+  aspectRatio: 1,
+
   plugins: {
     legend: {
       labels: {
@@ -193,6 +184,21 @@ PolarAreaController.defaults = {
         }
       }
     }
-  }
+  },
 
+  scales: {
+    r: {
+      type: 'radialLinear',
+      angleLines: {
+        display: false
+      },
+      beginAtZero: true,
+      gridLines: {
+        circular: true
+      },
+      pointLabels: {
+        display: false
+      }
+    }
+  }
 };
index 36473f600791546d766fcc60ac9c827c67990d20..e38c0778376291f926278a127c54f791d0ee7d0d 100644 (file)
@@ -80,16 +80,21 @@ RadarController.id = 'radar';
 RadarController.defaults = {
   datasetElementType: 'line',
   dataElementType: 'point',
-  aspectRatio: 1,
-  datasets: {
-    showLine: true,
-  },
+  indexAxis: 'r',
+  showLine: true,
   elements: {
     line: {
       fill: 'start'
     }
   },
-  indexAxis: 'r',
+};
+
+/**
+ * @type {any}
+ */
+RadarController.overrides = {
+  aspectRatio: 1,
+
   scales: {
     r: {
       type: 'radialLinear',
index cc9823f45852c38dd53b685df3f188aa5ba2aeb3..1d1ccb73e5a95664dd2123319d1fd7620de61edf 100644 (file)
@@ -10,19 +10,14 @@ ScatterController.id = 'scatter';
  * @type {any}
  */
 ScatterController.defaults = {
-  scales: {
-    x: {
-      type: 'linear'
-    },
-    y: {
-      type: 'linear'
-    }
-  },
+  showLine: false,
+  fill: false
+};
 
-  datasets: {
-    showLine: false,
-    fill: false
-  },
+/**
+ * @type {any}
+ */
+ScatterController.overrides = {
 
   interaction: {
     mode: 'point'
@@ -39,5 +34,14 @@ ScatterController.defaults = {
         }
       }
     }
+  },
+
+  scales: {
+    x: {
+      type: 'linear'
+    },
+    y: {
+      type: 'linear'
+    }
   }
 };
index d41240c685add0558b626e15c734092957cbaf40..a9e937ce9c78017780b66eb994de3f6f2ef90c71 100644 (file)
@@ -1,13 +1,11 @@
-import defaults from './core.defaults';
+import defaults, {overrides, descriptors} from './core.defaults';
 import {mergeIf, resolveObjectKey, isArray, isFunction, valueOrDefault, isObject} from '../helpers/helpers.core';
 import {_attachContext, _createResolver, _descriptors} from '../helpers/helpers.config';
 
 export function getIndexAxis(type, options) {
-  const typeDefaults = defaults.controllers[type] || {};
-  const datasetDefaults = typeDefaults.datasets || {};
-  const datasetOptions = options.datasets || {};
-  const typeOptions = datasetOptions[type] || {};
-  return typeOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
+  const datasetDefaults = defaults.datasets[type] || {};
+  const datasetOptions = (options.datasets || {})[type] || {};
+  return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
 }
 
 function getAxisFromDefaultScaleID(id, indexAxis) {
@@ -41,7 +39,7 @@ export function determineAxis(id, scaleOptions) {
 }
 
 function mergeScaleConfig(config, options) {
-  const chartDefaults = defaults.controllers[config.type] || {scales: {}};
+  const chartDefaults = overrides[config.type] || {scales: {}};
   const configScales = options.scales || {};
   const chartIndexAxis = getIndexAxis(config.type, options);
   const firstIDs = Object.create(null);
@@ -61,7 +59,7 @@ function mergeScaleConfig(config, options) {
   config.data.datasets.forEach(dataset => {
     const type = dataset.type || config.type;
     const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
-    const datasetDefaults = defaults.controllers[type] || {};
+    const datasetDefaults = overrides[type] || {};
     const defaultScaleOptions = datasetDefaults.scales || {};
     Object.keys(defaultScaleOptions).forEach(defaultID => {
       const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
@@ -175,8 +173,6 @@ export default class Config {
     return cachedKeys(datasetType,
       () => [
         `datasets.${datasetType}`,
-        `controllers.${datasetType}`,
-        `controllers.${datasetType}.datasets`,
         ''
       ]);
   }
@@ -192,12 +188,9 @@ export default class Config {
     return cachedKeys(`${datasetType}.transition.${transition}`,
       () => [
         `datasets.${datasetType}.transitions.${transition}`,
-        `controllers.${datasetType}.transitions.${transition}`,
-        `controllers.${datasetType}.datasets.transitions.${transition}`,
         `transitions.${transition}`,
+        // The following are used for looking up the `animations` and `animation` keys
         `datasets.${datasetType}`,
-        `controllers.${datasetType}`,
-        `controllers.${datasetType}.datasets`,
         ''
       ]);
   }
@@ -213,9 +206,8 @@ export default class Config {
   datasetElementScopeKeys(datasetType, elementType) {
     return cachedKeys(`${datasetType}-${elementType}`,
       () => [
+        `datasets.${datasetType}.elements.${elementType}`,
         `datasets.${datasetType}`,
-        `controllers.${datasetType}.datasets`,
-        `controllers.${datasetType}.elements.${elementType}`,
         `elements.${elementType}`,
         ''
       ]);
@@ -231,7 +223,6 @@ export default class Config {
     const type = this.type;
     return cachedKeys(`${type}-plugin-${id}`,
       () => [
-        `controllers.${type}.plugins.${id}`,
         `plugins.${id}`,
         ...plugin.additionalOptionScopes || [],
       ]);
@@ -244,10 +235,11 @@ export default class Config {
    * @param {boolean} [resetCache] - reset the cache for this mainScope
    */
   getOptionScopes(mainScope, scopeKeys, resetCache) {
-    let cache = this._scopeCache.get(mainScope);
+    const {_scopeCache, options, type} = this;
+    let cache = _scopeCache.get(mainScope);
     if (!cache || resetCache) {
       cache = new Map();
-      this._scopeCache.set(mainScope, cache);
+      _scopeCache.set(mainScope, cache);
     }
     const cached = cache.get(scopeKeys);
     if (cached) {
@@ -260,9 +252,10 @@ export default class Config {
       scopes.add(mainScope);
       scopeKeys.forEach(key => addIfFound(scopes, mainScope, key));
     }
-    scopeKeys.forEach(key => addIfFound(scopes, this.options, key));
+    scopeKeys.forEach(key => addIfFound(scopes, options, key));
+    scopeKeys.forEach(key => addIfFound(scopes, overrides[type] || {}, key));
     scopeKeys.forEach(key => addIfFound(scopes, defaults, key));
-    scopeKeys.forEach(key => addIfFound(scopes, defaults.descriptors, key));
+    scopeKeys.forEach(key => addIfFound(scopes, descriptors, key));
 
     const array = [...scopes];
     if (keysCached.has(scopeKeys)) {
@@ -276,14 +269,15 @@ export default class Config {
    * @return {object[]}
    */
   chartOptionScopes() {
-    const controllerDefaults = defaults.controllers[this.type] || {};
+    const {options, type} = this;
+
     return [
-      this.options,
-      controllerDefaults,
-      controllerDefaults.datasets || {},
-      {type: this.type},
+      options,
+      overrides[type] || {},
+      defaults.datasets[type] || {}, // https://github.com/chartjs/Chart.js/issues/8531
+      {type},
       defaults,
-      defaults.descriptors
+      descriptors
     ];
   }
 
index 6d196d03243e85745a1c1c67b46f4cb811db2294..cf2c195b7914b30dfbce1ac8cb75d393a322df6d 100644 (file)
@@ -1,5 +1,5 @@
 import animator from './core.animator';
-import defaults from './core.defaults';
+import defaults, {overrides} from './core.defaults';
 import Interaction from './core.interaction';
 import layouts from './core.layouts';
 import {BasicPlatform, DomPlatform} from '../platform';
@@ -410,11 +410,11 @@ class Chart {
         meta.controller.updateIndex(i);
         meta.controller.linkScales();
       } else {
-        const controllerDefaults = defaults.controllers[type];
         const ControllerClass = registry.getController(type);
+        const {datasetElementType, dataElementType} = defaults.datasets[type];
         Object.assign(ControllerClass.prototype, {
-          dataElementType: registry.getElement(controllerDefaults.dataElementType),
-          datasetElementType: controllerDefaults.datasetElementType && registry.getElement(controllerDefaults.datasetElementType)
+          dataElementType: registry.getElement(dataElementType),
+          datasetElementType: datasetElementType && registry.getElement(datasetElementType)
         });
         meta.controller = new ControllerClass(me, i);
         newControllers.push(meta.controller);
@@ -1117,6 +1117,10 @@ Object.defineProperties(Chart, {
     enumerable,
     value: instances
   },
+  overrides: {
+    enumerable,
+    value: overrides
+  },
   registry: {
     enumerable,
     value: registry
index c99ab78f03935bfe9d6e1f9ddb6df07b6460f52f..4d94bbfd2c1ccd940dada00394c9368a81342f7b 100644 (file)
@@ -1,7 +1,8 @@
 import {getHoverColor} from '../helpers/helpers.color';
 import {isObject, merge, valueOrDefault} from '../helpers/helpers.core';
 
-const privateSymbol = Symbol('private');
+export const overrides = Object.create(null);
+export const descriptors = Object.create(null);
 
 /**
  * @param {object} node
@@ -20,17 +21,24 @@ function getScope(node, key) {
   return node;
 }
 
+function set(root, scope, values) {
+  if (typeof scope === 'string') {
+    return merge(getScope(root, scope), values);
+  }
+  return merge(getScope(root, ''), scope);
+}
+
 /**
  * Please use the module's default export which provides a singleton instance
  * Note: class is exported for typedoc
  */
 export class Defaults {
-  constructor(descriptors) {
+  constructor(_descriptors) {
     this.animation = undefined;
     this.backgroundColor = 'rgba(0,0,0,0.1)';
     this.borderColor = 'rgba(0,0,0,0.1)';
     this.color = '#666';
-    this.controllers = {};
+    this.datasets = {};
     this.devicePixelRatio = (context) => context.chart.platform.getDevicePixelRatio();
     this.elements = {};
     this.events = [
@@ -68,12 +76,7 @@ export class Defaults {
     this.scales = {};
     this.showLine = true;
 
-    Object.defineProperty(this, privateSymbol, {
-      value: Object.create(null),
-      writable: false
-    });
-
-    this.describe(descriptors);
+    this.describe(_descriptors);
   }
 
   /**
@@ -81,10 +84,7 @@ export class Defaults {
         * @param {object} [values]
         */
   set(scope, values) {
-    if (typeof scope === 'string') {
-      return merge(getScope(this, scope), values);
-    }
-    return merge(getScope(this, ''), scope);
+    return set(this, scope, values);
   }
 
   /**
@@ -99,15 +99,11 @@ export class Defaults {
         * @param {object} [values]
         */
   describe(scope, values) {
-    const root = this[privateSymbol];
-    if (typeof scope === 'string') {
-      return merge(getScope(root, scope), values);
-    }
-    return merge(getScope(root, ''), scope);
+    return set(descriptors, scope, values);
   }
 
-  get descriptors() {
-    return this[privateSymbol];
+  override(scope, values) {
+    return set(overrides, scope, values);
   }
 
   /**
index e46d2dc2fddfdd0cdbe388d20c2ec0b4372620ab..6547da2dfb7bc545eaed97643ba078c0e449933a 100644 (file)
@@ -10,7 +10,7 @@ import {each, callback as call, _capitalize} from '../helpers/helpers.core';
  */
 export class Registry {
   constructor() {
-    this.controllers = new TypedRegistry(DatasetController, 'controllers');
+    this.controllers = new TypedRegistry(DatasetController, 'datasets', true);
     this.elements = new TypedRegistry(Element, 'elements');
     this.plugins = new TypedRegistry(Object, 'plugins');
     this.scales = new TypedRegistry(Scale, 'scales');
index 76306cbe0e71c1756ae01fae8392ea858fdd0c93..0439faf2d7f6c2d1857654ab70c1119c068f79e4 100644 (file)
@@ -1,13 +1,15 @@
-import defaults from './core.defaults';
+import {merge} from '../helpers';
+import defaults, {overrides} from './core.defaults';
 
 /**
- * @typedef {{id: string, defaults: any, defaultRoutes: any}} IChartComponent
+ * @typedef {{id: string, defaults: any, overrides?: any, defaultRoutes: any}} IChartComponent
  */
 
 export default class TypedRegistry {
-  constructor(type, scope) {
+  constructor(type, scope, override) {
     this.type = type;
     this.scope = scope;
+    this.override = override;
     this.items = Object.create(null);
   }
 
@@ -20,18 +22,18 @@ export default class TypedRegistry {
         * @returns {string} The scope where items defaults were registered to.
         */
   register(item) {
+    const me = this;
     const proto = Object.getPrototypeOf(item);
     let parentScope;
 
     if (isIChartComponent(proto)) {
       // Make sure the parent is registered and note the scope where its defaults are.
-      parentScope = this.register(proto);
+      parentScope = me.register(proto);
     }
 
-    const items = this.items;
+    const items = me.items;
     const id = item.id;
-    const baseScope = this.scope;
-    const scope = baseScope ? baseScope + '.' + id : id;
+    const scope = me.scope + '.' + id;
 
     if (!id) {
       throw new Error('class does not have id: ' + item);
@@ -44,6 +46,9 @@ export default class TypedRegistry {
 
     items[id] = item;
     registerDefaults(item, scope, parentScope);
+    if (me.override) {
+      defaults.override(item.id, item.overrides);
+    }
 
     return scope;
   }
@@ -70,18 +75,20 @@ export default class TypedRegistry {
 
     if (scope && id in defaults[scope]) {
       delete defaults[scope][id];
+      if (this.override) {
+        delete overrides[id];
+      }
     }
   }
 }
 
 function registerDefaults(item, scope, parentScope) {
   // Inherit the parent's defaults and keep existing defaults
-  const itemDefaults = Object.assign(
-    Object.create(null),
-    parentScope && defaults.get(parentScope),
-    item.defaults,
-    defaults.get(scope)
-  );
+  const itemDefaults = merge(Object.create(null), [
+    parentScope ? defaults.get(parentScope) : {},
+    defaults.get(scope),
+    item.defaults
+  ]);
 
   defaults.set(scope, itemDefaults);
 
index b69a5819b940eef991a9bcc26f92c1ec0c81c360..883a9ed58e17b60fd2a97b0b8633c7534d7e69ee 100644 (file)
@@ -1306,7 +1306,7 @@ describe('Chart.controllers.bar', function() {
       var chart = window.acquireChart(this.config);
       var meta = chart.getDatasetMeta(0);
       var xScale = chart.scales[meta.xAxisID];
-      var options = Chart.defaults.controllers.bar.datasets;
+      var options = Chart.defaults.datasets.bar;
 
       var categoryPercentage = options.categoryPercentage;
       var barPercentage = options.barPercentage;
@@ -1482,7 +1482,7 @@ describe('Chart.controllers.bar', function() {
             expected = barThickness;
           } else {
             var scale = chart.scales.x;
-            var options = Chart.defaults.controllers.bar.datasets;
+            var options = Chart.defaults.datasets.bar;
             var categoryPercentage = options.categoryPercentage;
             var barPercentage = options.barPercentage;
             var tickInterval = scale.getPixelForTick(1) - scale.getPixelForTick(0);
index 187d4d1d654c9b4786bfc3b616b756e58ccfbb76..9878eca185ea00fd45414cba622f37aa8ed91d61 100644 (file)
@@ -511,18 +511,16 @@ describe('Chart.controllers.line', function() {
 
   describe('dataset global defaults', function() {
     beforeEach(function() {
-      this._defaults = Chart.helpers.clone(Chart.defaults.controllers.line.datasets);
+      this._defaults = Chart.helpers.clone(Chart.defaults.datasets.line);
     });
 
     afterEach(function() {
-      Chart.defaults.controllers.line.datasets = this._defaults;
+      Chart.defaults.datasets.line = this._defaults;
       delete this._defaults;
     });
 
     it('should utilize the dataset global default options', function() {
-      Chart.defaults.controllers.line.datasets = Chart.defaults.controllers.line.datasets || {};
-
-      Chart.helpers.merge(Chart.defaults.controllers.line.datasets, {
+      Chart.helpers.merge(Chart.defaults.datasets.line, {
         spanGaps: true,
         tension: 0.231,
         backgroundColor: '#add',
@@ -563,9 +561,7 @@ describe('Chart.controllers.line', function() {
     });
 
     it('should be overriden by user-supplied values', function() {
-      Chart.defaults.controllers.line.datasets = Chart.defaults.controllers.line.datasets || {};
-
-      Chart.helpers.merge(Chart.defaults.controllers.line.datasets, {
+      Chart.helpers.merge(Chart.defaults.datasets.line, {
         spanGaps: true,
         tension: 0.231
       });
index fc379d9ef520d8479f61841c5eaf12fa9acbaaa8..8d95f90ac2b40a0bfa2695d5a9c77e8376c6f0d5 100644 (file)
@@ -1,5 +1,7 @@
 describe('Chart', function() {
 
+  const overrides = Chart.overrides;
+
   // https://github.com/chartjs/Chart.js/issues/2481
   // See global.deprecations.tests.js for backward compatibility
   it('should be defined and prototype of chart instances', function() {
@@ -92,11 +94,10 @@ describe('Chart', function() {
     it('should initialize config with default interaction options', function() {
       var callback = function() {};
       var defaults = Chart.defaults;
-      var defaultMode = defaults.controllers.line.interaction.mode;
+      var defaultMode = overrides.line.interaction.mode;
 
       defaults.hover.onHover = callback;
-      defaults.controllers.line.spanGaps = true;
-      defaults.controllers.line.interaction.mode = 'test';
+      overrides.line.interaction.mode = 'test';
 
       var chart = acquireChart({
         type: 'line'
@@ -104,14 +105,11 @@ describe('Chart', function() {
 
       var options = chart.options;
       expect(options.font.size).toBe(defaults.font.size);
-      expect(options.showLine).toBe(defaults.controllers.line.datasets.showLine);
-      expect(options.spanGaps).toBe(true);
       expect(options.hover.onHover).toBe(callback);
       expect(options.hover.mode).toBe('test');
 
       defaults.hover.onHover = null;
-      defaults.controllers.line.spanGaps = false;
-      defaults.controllers.line.interaction.mode = defaultMode;
+      overrides.line.interaction.mode = defaultMode;
     });
 
     it('should initialize config with default hover options', function() {
@@ -119,8 +117,7 @@ describe('Chart', function() {
       var defaults = Chart.defaults;
 
       defaults.hover.onHover = callback;
-      defaults.controllers.line.spanGaps = true;
-      defaults.controllers.line.hover.mode = 'test';
+      overrides.line.hover.mode = 'test';
 
       var chart = acquireChart({
         type: 'line'
@@ -128,23 +125,21 @@ describe('Chart', function() {
 
       var options = chart.options;
       expect(options.font.size).toBe(defaults.font.size);
-      expect(options.showLine).toBe(defaults.controllers.line.datasets.showLine);
-      expect(options.spanGaps).toBe(true);
       expect(options.hover.onHover).toBe(callback);
       expect(options.hover.mode).toBe('test');
 
       defaults.hover.onHover = null;
-      defaults.controllers.line.spanGaps = false;
-      delete defaults.controllers.line.hover.mode;
+      delete overrides.line.hover.mode;
     });
 
     it('should override default options', function() {
       var callback = function() {};
       var defaults = Chart.defaults;
+      var defaultSpanGaps = defaults.datasets.line.spanGaps;
 
       defaults.hover.onHover = callback;
-      defaults.controllers.line.hover.mode = 'x-axis';
-      defaults.controllers.line.spanGaps = true;
+      overrides.line.hover.mode = 'x-axis';
+      defaults.datasets.line.spanGaps = true;
 
       var chart = acquireChart({
         type: 'line',
@@ -167,12 +162,12 @@ describe('Chart', function() {
       expect(options.plugins.title.position).toBe('bottom');
 
       defaults.hover.onHover = null;
-      delete defaults.controllers.line.hover.mode;
-      defaults.controllers.line.spanGaps = false;
+      delete overrides.line.hover.mode;
+      defaults.datasets.line.spanGaps = defaultSpanGaps;
     });
 
     it('should initialize config with default dataset options', function() {
-      var defaults = Chart.defaults.controllers.pie.datasets;
+      var defaults = Chart.defaults.datasets.pie;
 
       var chart = acquireChart({
         type: 'pie'
@@ -431,7 +426,7 @@ describe('Chart', function() {
       expect(chart.scales.x.options._jasmineCheck).toBeDefined();
       expect(chart.scales.y.options._jasmineCheck).toBeDefined();
 
-      expect(Chart.defaults.controllers.line._jasmineCheck).not.toBeDefined();
+      expect(Chart.overrides.line._jasmineCheck).not.toBeDefined();
       expect(Chart.defaults._jasmineCheck).not.toBeDefined();
       expect(Chart.defaults.scales.linear._jasmineCheck).not.toBeDefined();
       expect(Chart.defaults.scales.category._jasmineCheck).not.toBeDefined();
index ade0ffdb33a77dd3cb8fad7885c963aa4a4e8ab5..2d94105f14b09077ca5903e2f9198f3eca4c7875 100644 (file)
@@ -5,17 +5,22 @@ describe('Chart.registry', function() {
     CustomController.defaults = {
       foo: 'bar'
     };
+    CustomController.overrides = {
+      bar: 'foo'
+    };
     Chart.register(CustomController);
 
     expect(Chart.registry.getController('custom')).toEqual(CustomController);
-    expect(Chart.defaults.controllers.custom).toEqual(CustomController.defaults);
+    expect(Chart.defaults.datasets.custom).toEqual(CustomController.defaults);
+    expect(Chart.overrides.custom).toEqual(CustomController.overrides);
 
     Chart.unregister(CustomController);
 
     expect(function() {
       Chart.registry.getController('custom');
     }).toThrow(new Error('"custom" is not a registered controller.'));
-    expect(Chart.defaults.controllers.custom).not.toBeDefined();
+    expect(Chart.overrides.custom).not.toBeDefined();
+    expect(Chart.defaults.datasets.custom).not.toBeDefined();
   });
 
   it('should handle an ES6 scale extension', function() {
@@ -34,7 +39,7 @@ describe('Chart.registry', function() {
     expect(function() {
       Chart.registry.getScale('es6Scale');
     }).toThrow(new Error('"es6Scale" is not a registered scale.'));
-    expect(Chart.defaults.controllers.custom).not.toBeDefined();
+    expect(Chart.defaults.scales.es6Scale).not.toBeDefined();
   });
 
   it('should handle an ES6 element extension', function() {
@@ -104,14 +109,14 @@ describe('Chart.registry', function() {
       Chart.registry.addControllers(customExtension);
 
       expect(Chart.registry.getController('custom')).toEqual(customExtension);
-      expect(Chart.defaults.controllers.custom).toEqual(customExtension.defaults);
+      expect(Chart.defaults.datasets.custom).toEqual(customExtension.defaults);
 
       Chart.registry.removeControllers(customExtension);
 
       expect(function() {
         Chart.registry.getController('custom');
       }).toThrow(new Error('"custom" is not a registered controller.'));
-      expect(Chart.defaults.controllers.custom).not.toBeDefined();
+      expect(Chart.defaults.datasets.custom).not.toBeDefined();
     });
 
     it('as scale', function() {
@@ -199,17 +204,21 @@ describe('Chart.registry', function() {
   });
 
   it('should preserve existing defaults', function() {
-    Chart.defaults.controllers.test = {test1: true};
+    Chart.defaults.datasets.test = {test1: true, test3: false};
+    Chart.overrides.test = {testA: true, testC: false};
 
     class testController extends Chart.DatasetController {}
     testController.id = 'test';
     testController.defaults = {test1: false, test2: true};
+    testController.overrides = {testA: false, testB: true};
 
     Chart.register(testController);
-    expect(Chart.defaults.controllers.test).toEqual({test1: true, test2: true});
+    expect(Chart.defaults.datasets.test).toEqual({test1: false, test2: true, test3: false});
+    expect(Chart.overrides.test).toEqual({testA: false, testB: true, testC: false});
 
     Chart.unregister(testController);
-    expect(Chart.defaults.controllers.test).not.toBeDefined();
+    expect(Chart.defaults.datasets.test).not.toBeDefined();
+    expect(Chart.overrides.test).not.toBeDefined();
   });
 
   describe('should handle multiple items', function() {
index a1fcf4996d1132f0672f2bfd509f81851ac3bf59..ddf21e78755ca79959cea7c4602a6d7d4b824629 100644 (file)
@@ -1,7 +1,6 @@
 describe('Default Configs', function() {
   describe('Bubble Chart', function() {
     it('should return correct tooltip strings', function() {
-      var config = Chart.defaults.controllers.bubble;
       var chart = window.acquireChart({
         type: 'bubble',
         data: {
@@ -14,7 +13,6 @@ describe('Default Configs', function() {
             }]
           }]
         },
-        options: config
       });
 
       // fake out the tooltip hover and force the tooltip to update
@@ -33,7 +31,6 @@ describe('Default Configs', function() {
 
   describe('Doughnut Chart', function() {
     it('should return correct tooltip strings', function() {
-      var config = Chart.defaults.controllers.doughnut;
       var chart = window.acquireChart({
         type: 'doughnut',
         data: {
@@ -42,7 +39,6 @@ describe('Default Configs', function() {
             data: [10, 20, 30],
           }]
         },
-        options: config
       });
 
       // fake out the tooltip hover and force the tooltip to update
@@ -59,7 +55,6 @@ describe('Default Configs', function() {
     });
 
     it('should return correct tooltip string for a multiline label', function() {
-      var config = Chart.defaults.controllers.doughnut;
       var chart = window.acquireChart({
         type: 'doughnut',
         data: {
@@ -68,7 +63,6 @@ describe('Default Configs', function() {
             data: [10, 20, 30],
           }]
         },
-        options: config
       });
 
       // fake out the tooltip hover and force the tooltip to update
@@ -89,7 +83,6 @@ describe('Default Configs', function() {
     });
 
     it('should return correct legend label objects', function() {
-      var config = Chart.defaults.controllers.doughnut;
       var chart = window.acquireChart({
         type: 'doughnut',
         data: {
@@ -101,7 +94,6 @@ describe('Default Configs', function() {
             borderColor: '#000'
           }]
         },
-        options: config
       });
 
       var expected = [{
@@ -130,7 +122,7 @@ describe('Default Configs', function() {
     });
 
     it('should hide the correct arc when a legend item is clicked', function() {
-      var config = Chart.defaults.controllers.doughnut;
+      var config = Chart.overrides.doughnut;
       var chart = window.acquireChart({
         type: 'doughnut',
         data: {
@@ -142,7 +134,6 @@ describe('Default Configs', function() {
             borderColor: '#000'
           }]
         },
-        options: config
       });
       spyOn(chart, 'update').and.callThrough();
 
@@ -159,7 +150,6 @@ describe('Default Configs', function() {
 
   describe('Polar Area Chart', function() {
     it('should return correct tooltip strings', function() {
-      var config = Chart.defaults.controllers.polarArea;
       var chart = window.acquireChart({
         type: 'polarArea',
         data: {
@@ -168,7 +158,6 @@ describe('Default Configs', function() {
             data: [10, 20, 30],
           }]
         },
-        options: config
       });
 
       // fake out the tooltip hover and force the tooltip to update
@@ -185,7 +174,6 @@ describe('Default Configs', function() {
     });
 
     it('should return correct legend label objects', function() {
-      var config = Chart.defaults.controllers.polarArea;
       var chart = window.acquireChart({
         type: 'polarArea',
         data: {
@@ -197,7 +185,6 @@ describe('Default Configs', function() {
             borderColor: '#000'
           }]
         },
-        options: config
       });
 
       var expected = [{
@@ -226,7 +213,7 @@ describe('Default Configs', function() {
     });
 
     it('should hide the correct arc when a legend item is clicked', function() {
-      var config = Chart.defaults.controllers.polarArea;
+      var config = Chart.overrides.polarArea;
       var chart = window.acquireChart({
         type: 'polarArea',
         data: {
@@ -238,7 +225,6 @@ describe('Default Configs', function() {
             borderColor: '#000'
           }]
         },
-        options: config
       });
       spyOn(chart, 'update').and.callThrough();
 
index 683f3712fe56a9894b6bbbce01b8964b09a8f77c..7e6ab03e54f52411c811ea7fab328563ca4f2316 100644 (file)
@@ -119,8 +119,8 @@ describe('Platform.dom', function() {
     });
 
     it('should use default "chart" aspect ratio for render and display sizes', function() {
-      var ratio = Chart.defaults.controllers.doughnut.aspectRatio;
-      Chart.defaults.controllers.doughnut.aspectRatio = 1;
+      var ratio = Chart.overrides.doughnut.aspectRatio;
+      Chart.overrides.doughnut.aspectRatio = 1;
 
       var chart = acquireChart({
         type: 'doughnut',
@@ -133,7 +133,7 @@ describe('Platform.dom', function() {
         }
       });
 
-      Chart.defaults.controllers.doughnut.aspectRatio = ratio;
+      Chart.overrides.doughnut.aspectRatio = ratio;
 
       expect(chart).toBeChartOfSize({
         dw: 425, dh: 425,
index 9ae5fc70a6622ef7548f12bf975149c74c0a8259..4052e0d1f43f88e210217894bf3f7f3035b7ec98 100644 (file)
@@ -93,7 +93,8 @@ export interface ControllerDatasetOptions extends ParsingOptions {
 export interface BarControllerDatasetOptions
   extends ControllerDatasetOptions,
     ScriptableAndArrayOptions<BarOptions, ScriptableContext<'bar'>>,
-    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'bar'>> {
+    ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'bar'>>,
+    AnimationOptions<'bar'> {
   /**
    * The ID of the x axis to plot this dataset on.
    */
@@ -182,7 +183,8 @@ export interface LineControllerDatasetOptions
     ScriptableAndArrayOptions<PointPrefixedOptions, ScriptableContext<'line'>>,
     ScriptableAndArrayOptions<PointPrefixedHoverOptions, ScriptableContext<'line'>>,
     ScriptableOptions<LineOptions, ScriptableContext<'line'>>,
-    ScriptableOptions<LineHoverOptions, ScriptableContext<'line'>> {
+    ScriptableOptions<LineHoverOptions, ScriptableContext<'line'>>,
+    AnimationOptions<'line'> {
   /**
    * The ID of the x axis to plot this dataset on.
    */
@@ -238,7 +240,8 @@ export const ScatterController: ChartComponent & {
 export interface DoughnutControllerDatasetOptions
   extends ControllerDatasetOptions,
     ScriptableAndArrayOptions<ArcOptions, ScriptableContext<'doughnut'>>,
-    ScriptableAndArrayOptions<ArcHoverOptions, ScriptableContext<'doughnut'>> {
+    ScriptableAndArrayOptions<ArcHoverOptions, ScriptableContext<'doughnut'>>,
+    AnimationOptions<'doughnut'> {
 
   /**
    * Sweep to allow arcs to cover.
@@ -364,8 +367,9 @@ export interface RadarControllerDatasetOptions
     ScriptableOptions<PointPrefixedOptions, ScriptableContext<'radar'>>,
     ScriptableOptions<PointPrefixedHoverOptions, ScriptableContext<'radar'>>,
     ScriptableOptions<LineOptions, ScriptableContext<'radar'>>,
-    ScriptableOptions<LineHoverOptions, ScriptableContext<'radar'>> {
-  /**
+    ScriptableOptions<LineHoverOptions, ScriptableContext<'radar'>>,
+    AnimationOptions<'radar'> {
+        /**
    * The ID of the x axis to plot this dataset on.
    */
   xAxisID: string;
@@ -496,6 +500,7 @@ export declare class Chart<
   notifyPlugins(hook: string, args?: AnyObject): boolean | void;
 
   static readonly defaults: Defaults;
+  static readonly overrides: Overrides;
   static readonly version: string;
   static readonly instances: { [key: string]: Chart };
   static readonly registry: Registry;
@@ -608,16 +613,6 @@ export interface DatasetControllerChartComponent extends ChartComponent {
 }
 
 export interface Defaults extends CoreChartOptions<ChartType>, ElementChartOptions, PluginChartOptions<ChartType> {
-  controllers: {
-    [key in ChartType]: DeepPartial<
-      CoreChartOptions<key> &
-      ElementChartOptions &
-      PluginChartOptions<key> &
-      DatasetChartOptions<key>[key] &
-      ScaleChartOptions<key> &
-      ChartTypeRegistry[key]['chartOptions']
-      >;
-  };
 
   scale: ScaleOptionsByType;
   scales: {
@@ -648,6 +643,17 @@ export interface Defaults extends CoreChartOptions<ChartType>, ElementChartOptio
   route(scope: string, name: string, targetScope: string, targetName: string): void;
 }
 
+export type Overrides = {
+  [key in ChartType]: DeepPartial<
+    CoreChartOptions<key> &
+    ElementChartOptions &
+    PluginChartOptions<key> &
+    DatasetChartOptions<ChartType> &
+    ScaleChartOptions<key> &
+    ChartTypeRegistry[key]['chartOptions']
+    >;
+}
+
 export const defaults: Defaults;
 export interface InteractionOptions {
   axis?: string;
@@ -1349,7 +1355,9 @@ export interface HoverInteractionOptions extends CoreInteractionOptions {
 
 export interface CoreChartOptions<TType extends ChartType> extends ParsingOptions, AnimationOptions<TType> {
 
-  datasets: AnimationOptions<TType>;
+  datasets: {
+    [key in ChartType]: ChartTypeRegistry[key]['datasetOptions']
+  }
 
   /**
    * The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts.
@@ -1474,30 +1482,24 @@ export type AnimationSpec<TType extends ChartType> = {
    * The number of milliseconds an animation takes.
    * @default 1000
    */
-  duration: Scriptable<number, ScriptableContext<TType>>;
+  duration?: Scriptable<number, ScriptableContext<TType>>;
   /**
    * Easing function to use
    * @default 'easeOutQuart'
    */
-  easing: Scriptable<EasingFunction, ScriptableContext<TType>>;
-
-  /**
-   * Running animation count + FPS display in upper left corner of the chart.
-   * @default false
-   */
-  debug: Scriptable<boolean, ScriptableContext<TType>>;
+  easing?: Scriptable<EasingFunction, ScriptableContext<TType>>;
 
   /**
    * Delay before starting the animations.
    * @default 0
    */
-  delay: Scriptable<number, ScriptableContext<TType>>;
+  delay?: Scriptable<number, ScriptableContext<TType>>;
 
   /**
    *   If set to true, the animations loop endlessly.
    * @default false
    */
-  loop: Scriptable<boolean, ScriptableContext<TType>>;
+  loop?: Scriptable<boolean, ScriptableContext<TType>>;
 }
 
 export type AnimationsSpec<TType extends ChartType> = {
@@ -1536,11 +1538,11 @@ export type AnimationOptions<TType extends ChartType> = {
     /**
      * Callback called on each step of an animation.
      */
-    onProgress: (this: Chart, event: AnimationEvent) => void;
+    onProgress?: (this: Chart, event: AnimationEvent) => void;
     /**
      * Callback called when all animations are completed.
      */
-    onComplete: (this: Chart, event: AnimationEvent) => void;
+    onComplete?: (this: Chart, event: AnimationEvent) => void;
   };
   animations: AnimationsSpec<TType>;
   transitions: TransitionsSpec<TType>;
diff --git a/types/tests/defaults.ts b/types/tests/defaults.ts
new file mode 100644 (file)
index 0000000..d6dba5a
--- /dev/null
@@ -0,0 +1,9 @@
+import { Chart } from '../index.esm';
+
+Chart.defaults.scales.time.time.minUnit = 'day';
+
+Chart.defaults.plugins.title.display = false;
+
+Chart.defaults.datasets.bar.backgroundColor = 'red';
+
+Chart.defaults.animation = { duration: 500 };
diff --git a/types/tests/options.ts b/types/tests/options.ts
new file mode 100644 (file)
index 0000000..d771a88
--- /dev/null
@@ -0,0 +1,33 @@
+import { Chart } from '../index.esm';
+
+export const chart = new Chart('test', {
+  type: 'bar',
+  data: {
+    labels: ['a'],
+    datasets: [{
+      data: [1],
+    }, {
+      type: 'line',
+      data: [{ x: 1, y: 1 }]
+    }]
+  },
+  options: {
+    animation: {
+      duration: 500
+    },
+    backgroundColor: 'red',
+    datasets: {
+      line: {
+        animation: {
+          duration: 600
+        },
+        backgroundColor: 'blue',
+      }
+    },
+    elements: {
+      point: {
+        backgroundColor: 'red'
+      }
+    }
+  }
+});
diff --git a/types/tests/overrides.ts b/types/tests/overrides.ts
new file mode 100644 (file)
index 0000000..8c2e02d
--- /dev/null
@@ -0,0 +1,10 @@
+import { Chart } from '../index.esm';
+
+Chart.overrides.bar.scales.x.type = 'time';
+
+Chart.overrides.bar.plugins.title.display = false;
+
+Chart.overrides.line.datasets.bar.backgroundColor = 'red';
+
+Chart.overrides.line.animation = false;
+Chart.overrides.line.datasets.bar.animation = { duration: 100 };
index 119301c44cfcf26cf8b34609bfefa2849794df56..694943e60943f9d98daa24083b3274dc82a6e947 100644 (file)
@@ -1,6 +1,6 @@
 import { Chart } from '../../../index.esm';
 
-Chart.defaults.controllers.bubble.plugins.tooltip.callbacks.label = (item) => {
+Chart.overrides.bubble.plugins.tooltip.callbacks.label = (item) => {
   const { x, y, _custom: r } = item.parsed;
   return `${item.label}: (${x}, ${y}, ${r})`;
 };