diff --git a/CHANGELOG.md b/CHANGELOG.md index 946efff04b5..a7831f8c61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [next] +- feat(): Add has method to classRegistry to allow to check if a class exists. (fixes #10001) + ## [6.1.0] - fix(): Avoid errors on restoring custom properties that pass the lazy detection of shadow,gradient,pattern and clipPath. [#10001](https://github.com/fabricjs/fabric.js/issues/10001) diff --git a/src/ClassRegistry.spec.ts b/src/ClassRegistry.spec.ts new file mode 100644 index 00000000000..253040e8281 --- /dev/null +++ b/src/ClassRegistry.spec.ts @@ -0,0 +1,39 @@ +import { ClassRegistry } from './ClassRegistry'; + +describe('ClassRegistry', () => { + let classRegistry: ClassRegistry; + beforeEach(() => { + classRegistry = new ClassRegistry(); + }); + it('will error if a class is request that is not registered', () => { + expect(() => classRegistry.getClass('any')).toThrow( + 'No class registered for any' + ); + }); + it('will return a class previously registered', () => { + classRegistry.setClass(Set, 'any'); + expect(classRegistry.getClass('any')).toBe(Set); + }); + it('will check if a class was previously registered', () => { + expect(classRegistry.has('any')).toBe(false); + classRegistry.setClass(Set, 'any'); + expect(classRegistry.has('any')).toBe(true); + }); + it('not specified will register the class using the type static prop', () => { + class Set2 extends Set { + static type = 'SETABC'; + } + classRegistry.setClass(Set2); + expect(classRegistry.has('SETABC')).toBe(true); + expect(classRegistry.getClass('SETABC')).toBe(Set2); + expect(classRegistry.getClass('setabc')).toBe(Set2); + }); + it('has a method for SVG parsing classes', () => { + class Set2 extends Set { + static type = 'SETABC'; + } + classRegistry.setSVGClass(Set2); + expect(classRegistry.getSVGClass('SETABC')).toBe(undefined); + expect(classRegistry.getSVGClass('setabc')).toBe(Set2); + }); +}); diff --git a/src/ClassRegistry.ts b/src/ClassRegistry.ts index 8b673c40686..fb2611c8b89 100644 --- a/src/ClassRegistry.ts +++ b/src/ClassRegistry.ts @@ -24,6 +24,10 @@ export class ClassRegistry { this[SVG] = new Map(); } + has(classType: string): boolean { + return this[JSON].has(classType); + } + getClass(classType: string): T { const constructor = this[JSON].get(classType); if (!constructor) { diff --git a/src/util/misc/objectEnlive.spec.ts b/src/util/misc/objectEnlive.spec.ts new file mode 100644 index 00000000000..b37ab91c301 --- /dev/null +++ b/src/util/misc/objectEnlive.spec.ts @@ -0,0 +1,68 @@ +import { enlivenObjects } from './objectEnlive'; +import { Rect, type RectProps } from '../../shapes/Rect'; +import { Shadow } from '../../Shadow'; +import { classRegistry } from '../../ClassRegistry'; + +const mockedRectWithCustomProperty = { + type: 'rect', + width: 100, + // will become a shadow + shadow: { + type: 'shadow', + blur: 5, + }, + // will become a rect + custom1: { + type: 'rect', + width: 50, + }, + custom2: { + type: 'nothing', + value: 3, + }, + // will become a set + custom3: { + type: 'registered', + }, +}; + +describe('enlivenObjects', () => { + it('will enlive correctly', async () => { + const [rect] = await enlivenObjects>([ + mockedRectWithCustomProperty, + ]); + expect(rect).toBeInstanceOf(Rect); + expect(rect.shadow).toBeInstanceOf(Shadow); + expect(rect.custom1).toBeInstanceOf(Rect); + expect(rect.custom2).toEqual({ + type: 'nothing', + value: 3, + }); + expect(rect.custom3).toEqual({ + type: 'registered', + }); + }); + it('will enlive correctly newly registered props', async () => { + class Test { + declare opts: any; + constructor(opts: any) { + this.opts = opts; + } + static async fromObject(opts: any) { + return new this(opts); + } + } + classRegistry.setClass(Test, 'registered'); + const [rect] = await enlivenObjects>([ + mockedRectWithCustomProperty, + ]); + expect(rect).toBeInstanceOf(Rect); + expect(rect.shadow).toBeInstanceOf(Shadow); + expect(rect.custom1).toBeInstanceOf(Rect); + expect(rect.custom2).toEqual({ + type: 'nothing', + value: 3, + }); + expect(rect.custom3).toBeInstanceOf(Test); + }); +}); diff --git a/src/util/misc/objectEnlive.ts b/src/util/misc/objectEnlive.ts index 479fe5aa7e9..68723a30bd4 100644 --- a/src/util/misc/objectEnlive.ts +++ b/src/util/misc/objectEnlive.ts @@ -160,7 +160,7 @@ export const enlivenObjectEnlivables = < * If we have a type and there is a classe registered for it, we enlive it. * If there is no class registered for it we return the value as is * */ - if (value.type && classRegistry.getClass(value.type)) { + if (value.type && classRegistry.has(value.type)) { return enlivenObjects([value], { signal, }).then(([enlived]) => {