From: Jukka Kurkela Date: Mon, 13 Jul 2020 21:43:30 +0000 (+0300) Subject: Registry fixes (#7617) X-Git-Tag: v3.0.0-beta.2~47 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6deafdb4d4256407b3da93864ffd5da322e88e80;p=thirdparty%2FChart.js.git Registry fixes (#7617) * Fix documentation for classical extensions * Tests and fixes for registry --- diff --git a/docs/docs/developers/axes.md b/docs/docs/developers/axes.md index 9af289ad3..474dbd089 100644 --- a/docs/docs/developers/axes.md +++ b/docs/docs/developers/axes.md @@ -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); diff --git a/docs/docs/developers/charts.md b/docs/docs/developers/charts.md index c415c52a2..05ab8025a 100644 --- a/docs/docs/developers/charts.md +++ b/docs/docs/developers/charts.md @@ -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, { diff --git a/src/core/core.typedRegistry.js b/src/core/core.typedRegistry.js index e84126ba5..c7b72e53c 100644 --- a/src/core/core.typedRegistry.js +++ b/src/core/core.typedRegistry.js @@ -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.type, type); + 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 index 000000000..6ed0b5dad --- /dev/null +++ b/test/specs/core.registry.tests.js @@ -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 {}')); + }); +});