]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Registry fixes (#7617)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Mon, 13 Jul 2020 21:43:30 +0000 (00:43 +0300)
committerGitHub <noreply@github.com>
Mon, 13 Jul 2020 21:43:30 +0000 (17:43 -0400)
* Fix documentation for classical extensions
* Tests and fixes for registry

docs/docs/developers/axes.md
docs/docs/developers/charts.md
src/core/core.typedRegistry.js
test/specs/core.registry.tests.js [new file with mode: 0644]

index 9af289ad38815b7ce36e70f555af4f54aa16dc83..474dbd089956491b8691d6dd48e3d08749364257 100644 (file)
@@ -17,6 +17,8 @@ function MyScale() {
   Chart.Scale.call(this, arguments);
   // constructor stuff
 }
+MyScale.prototype = Object.create(Chart.Scale.prototype);
+MyScale.prototype.constructor = MyScale;
 
 MyScale.prototype.draw = function(ctx) {
   Chart.Scale.prototype.draw.call(this, arguments);
index c415c52a27db35a2f9d4abc7c8f9847ff6f9c890..05ab8025a37df55f48ddf7f45fde3e934fa2e006 100644 (file)
@@ -112,12 +112,14 @@ Same example in classic style
 
 ```javascript
 function Custom() {
-  Chart.controllers.bubble.call(this, arguments);
+  Chart.controllers.bubble.apply(this, arguments);
   // constructor stuff
 }
+Custom.prototype = Object.create(Chart.controllers.bubble.prototype);
+Custom.prototype.constructor = Custom;
 
 Custom.prototype.draw = function(ctx) {
-    Chart.controllers.bubble.prototype.draw.call(this, arguments);
+    Chart.controllers.bubble.prototype.draw.apply(this, arguments);
 
     var meta = this.getMeta();
     var pt0 = meta.data[0];
@@ -134,9 +136,7 @@ Custom.prototype.draw = function(ctx) {
 Custom.id = 'derivedBubble';
 Custom.defaults = Chart.defaults.bubble;
 
-// Prototype chain can not be used to detect we are trying to register a controller, so we need
-// to be explicit
-Chart.registry.addControllers(Custom);
+Chart.register(Custom);
 
 // Now we can create and use our new chart type
 new Chart(ctx, {
index e84126ba54facb7c027f5910c3717ddc28e553f9..c7b72e53c505d5304a3e51a11e4f750e24433984 100644 (file)
@@ -1,5 +1,4 @@
 import defaults from './core.defaults';
-import {valueOrDefault} from '../helpers/helpers.core';
 
 /**
  * @typedef {{id: string, defaults: any, defaultRoutes: any}} IChartComponent
@@ -13,15 +12,14 @@ export default class TypedRegistry {
        }
 
        isForType(type) {
-               return Object.prototype.isPrototypeOf.call(this.typetype);
+               return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
        }
 
        /**
         * @param {IChartComponent} item
-        * @param {string} [scopeOverride]
         * @returns {string} The scope where items defaults were registered to.
         */
-       register(item, scopeOverride) {
+       register(item) {
                const proto = Object.getPrototypeOf(item);
                let parentScope;
 
@@ -32,11 +30,11 @@ export default class TypedRegistry {
 
                const items = this.items;
                const id = item.id;
-               const baseScope = valueOrDefault(scopeOverride, this.scope);
+               const baseScope = this.scope;
                const scope = baseScope ? baseScope + '.' + id : id;
 
                if (!id) {
-                       throw new Error('class does not have id: ' + Object.getPrototypeOf(item));
+                       throw new Error('class does not have id: ' + item);
                }
 
                if (id in items) {
@@ -64,13 +62,14 @@ export default class TypedRegistry {
        unregister(item) {
                const items = this.items;
                const id = item.id;
+               const scope = this.scope;
 
                if (id in items) {
                        delete items[id];
                }
 
-               if (id in defaults[this.scope]) {
-                       delete defaults[this.scope][id];
+               if (scope && id in defaults[scope]) {
+                       delete defaults[scope][id];
                } else if (id in defaults) {
                        delete defaults[id];
                }
diff --git a/test/specs/core.registry.tests.js b/test/specs/core.registry.tests.js
new file mode 100644 (file)
index 0000000..6ed0b5d
--- /dev/null
@@ -0,0 +1,175 @@
+describe('Chart.registry', function() {
+       it('should handle a classic controller extension', function() {
+               function CustomController() {
+                       Chart.controllers.line.apply(this, arguments);
+               }
+               CustomController.prototype = Object.create(Chart.controllers.line.prototype);
+               CustomController.prototype.constructor = CustomController;
+               CustomController.id = 'myline';
+               CustomController.defaults = Chart.defaults.line;
+
+               Chart.register(CustomController);
+
+               expect(Chart.registry.getController('myline')).toEqual(CustomController);
+               expect(Chart.defaults.myline).toEqual(CustomController.defaults);
+
+               Chart.unregister(CustomController);
+       });
+
+       it('should handle a classic scale extension', function() {
+               function CustomScale() {
+                       Chart.Scale.apply(this, arguments);
+               }
+               CustomScale.prototype = Object.create(Chart.Scale.prototype);
+               CustomScale.prototype.constructor = CustomScale;
+               CustomScale.id = 'myScale';
+               CustomScale.defaults = {
+                       foo: 'bar'
+               };
+
+               Chart.register(CustomScale);
+
+               expect(Chart.registry.getScale('myScale')).toEqual(CustomScale);
+               expect(Chart.defaults.scales.myScale).toEqual(CustomScale.defaults);
+
+               Chart.unregister(CustomScale);
+
+               expect(function() {
+                       Chart.registry.getScale('myScale');
+               }).toThrow(new Error('"myScale" is not a registered scale.'));
+               expect(Chart.defaults.scales.myScale).not.toBeDefined();
+       });
+
+       it('should handle a classic element extension', function() {
+               function CustomElement() {
+                       Chart.Element.apply(this, arguments);
+               }
+               CustomElement.prototype = Object.create(Chart.Element.prototype);
+               CustomElement.prototype.constructor = CustomElement;
+               CustomElement.id = 'myElement';
+               CustomElement.defaults = {
+                       foo: 'baz'
+               };
+
+               Chart.register(CustomElement);
+
+               expect(Chart.registry.getElement('myElement')).toEqual(CustomElement);
+               expect(Chart.defaults.elements.myElement).toEqual(CustomElement.defaults);
+
+               Chart.unregister(CustomElement);
+
+               expect(function() {
+                       Chart.registry.getElement('myElement');
+               }).toThrow(new Error('"myElement" is not a registered element.'));
+               expect(Chart.defaults.elements.myElement).not.toBeDefined();
+       });
+
+       it('should handle a classig plugin', function() {
+               const CustomPlugin = {
+                       id: 'customPlugin',
+                       defaults: {
+                               custom: 'plugin'
+                       }
+               };
+
+               Chart.register(CustomPlugin);
+
+               expect(Chart.registry.getPlugin('customPlugin')).toEqual(CustomPlugin);
+               expect(Chart.defaults.plugins.customPlugin).toEqual(CustomPlugin.defaults);
+
+               Chart.unregister(CustomPlugin);
+
+               expect(function() {
+                       Chart.registry.getPlugin('customPlugin');
+               }).toThrow(new Error('"customPlugin" is not a registered plugin.'));
+               expect(Chart.defaults.plugins.customPlugin).not.toBeDefined();
+       });
+
+       it('should handle an ES6 controller extension', function() {
+               class CustomController extends Chart.DatasetController {}
+               CustomController.id = 'custom';
+               CustomController.defaults = {
+                       foo: 'bar'
+               };
+               Chart.register(CustomController);
+
+               expect(Chart.registry.getController('custom')).toEqual(CustomController);
+               expect(Chart.defaults.custom).toEqual(CustomController.defaults);
+
+               Chart.unregister(CustomController);
+
+               expect(function() {
+                       Chart.registry.getController('custom');
+               }).toThrow(new Error('"custom" is not a registered controller.'));
+               expect(Chart.defaults.custom).not.toBeDefined();
+       });
+
+       it('should handle an ES6 scale extension', function() {
+               class CustomScale extends Chart.Scale {}
+               CustomScale.id = 'es6Scale';
+               CustomScale.defaults = {
+                       foo: 'bar'
+               };
+               Chart.register(CustomScale);
+
+               expect(Chart.registry.getScale('es6Scale')).toEqual(CustomScale);
+               expect(Chart.defaults.scales.es6Scale).toEqual(CustomScale.defaults);
+
+               Chart.unregister(CustomScale);
+
+               expect(function() {
+                       Chart.registry.getScale('es6Scale');
+               }).toThrow(new Error('"es6Scale" is not a registered scale.'));
+               expect(Chart.defaults.custom).not.toBeDefined();
+       });
+
+       it('should handle an ES6 element extension', function() {
+               class CustomElement extends Chart.Element {}
+               CustomElement.id = 'es6element';
+               CustomElement.defaults = {
+                       foo: 'bar'
+               };
+               Chart.register(CustomElement);
+
+               expect(Chart.registry.getElement('es6element')).toEqual(CustomElement);
+               expect(Chart.defaults.elements.es6element).toEqual(CustomElement.defaults);
+
+               Chart.unregister(CustomElement);
+
+               expect(function() {
+                       Chart.registry.getElement('es6element');
+               }).toThrow(new Error('"es6element" is not a registered element.'));
+               expect(Chart.defaults.elements.es6element).not.toBeDefined();
+       });
+
+       it('should handle an ES6 plugin', function() {
+               class CustomPlugin {}
+               CustomPlugin.id = 'es6plugin';
+               CustomPlugin.defaults = {
+                       foo: 'bar'
+               };
+               Chart.register(CustomPlugin);
+
+               expect(Chart.registry.getPlugin('es6plugin')).toEqual(CustomPlugin);
+               expect(Chart.defaults.plugins.es6plugin).toEqual(CustomPlugin.defaults);
+
+               Chart.unregister(CustomPlugin);
+
+               expect(function() {
+                       Chart.registry.getPlugin('es6plugin');
+               }).toThrow(new Error('"es6plugin" is not a registered plugin.'));
+               expect(Chart.defaults.plugins.es6plugin).not.toBeDefined();
+       });
+
+       it('should not accept an object without id', function() {
+               expect(function() {
+                       Chart.register({foo: 'bar'});
+               }).toThrow(new Error('class does not have id: bar'));
+
+               class FaultyPlugin {}
+
+               expect(function() {
+                       Chart.register(FaultyPlugin);
+               }).toThrow(new Error('class does not have id: class FaultyPlugin {}'));
+       });
+});