Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('AngularRenderer', () => {

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TestComponent],
declarations: [TestComponent, TestUpdateComponent],
}).compileComponents();

injector = TestBed.inject(Injector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
import { Component, Injector, EnvironmentInjector } from '@angular/core';
import { AngularFrameworkComponentFactory } from '../lib/utils/component-factory';
import { CreateComponentOptions } from 'dockview-core';
import { ComponentRegistryService } from '../lib/utils/component-registry.service';

@Component({
selector: 'test-dockview-component',
Expand Down Expand Up @@ -53,6 +54,7 @@ describe('AngularFrameworkComponentFactory', () => {
let injector: Injector;
let environmentInjector: EnvironmentInjector;
let factory: AngularFrameworkComponentFactory;
let resolver: ComponentRegistryService;

const components = {
'dockview-test': TestDockviewComponent,
Expand Down Expand Up @@ -84,9 +86,11 @@ describe('AngularFrameworkComponentFactory', () => {

injector = TestBed.inject(Injector);
environmentInjector = TestBed.inject(EnvironmentInjector);
resolver = TestBed.inject(ComponentRegistryService);
resolver.registerComponents(components);

factory = new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector,
tabComponents,
Expand Down Expand Up @@ -237,7 +241,7 @@ describe('AngularFrameworkComponentFactory', () => {

it('should return undefined when no component and no default', () => {
const factoryWithoutDefault = new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector,
{}
Expand Down Expand Up @@ -266,7 +270,7 @@ describe('AngularFrameworkComponentFactory', () => {
it('should throw error when no watermark component provided', () => {
const factoryWithoutWatermark =
new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector
);
Expand Down Expand Up @@ -299,7 +303,7 @@ describe('AngularFrameworkComponentFactory', () => {
it('should return undefined when no header actions components provided', () => {
const factoryWithoutHeaderActions =
new AngularFrameworkComponentFactory(
components,
resolver,
injector,
environmentInjector
);
Expand Down
121 changes: 121 additions & 0 deletions packages/dockview-angular/src/__tests__/component-registry.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { TestBed } from '@angular/core/testing';
import { Type } from '@angular/core';
import {
ComponentRegistryService,
ComponentResolver,
} from '../lib/utils/component-registry.service';

describe('ComponentRegistryService', () => {
let service: ComponentRegistryService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [ComponentRegistryService],
});
service = TestBed.inject(ComponentRegistryService);
});

describe('registerComponent', () => {
it('should register a component with a valid name and reference', () => {
const mockComponent = {} as Type<unknown>;
service.registerComponent('testComponent', mockComponent);

expect(service.resolveComponent('testComponent')).toBe(
mockComponent
);
});

it('should throw an error if component name or reference is not provided', () => {
expect(() =>
service.registerComponent('', {} as Type<unknown>)
).toThrow('Component and reference must be provided');
});
});

describe('registerComponents', () => {
it('should register multiple components from a record', () => {
const components = {
componentA: {} as Type<unknown>,
componentB: {} as Type<unknown>,
};

service.registerComponents(components);

expect(service.resolveComponent('componentA')).toBe(
components.componentA
);
expect(service.resolveComponent('componentB')).toBe(
components.componentB
);
});
});

describe('resolveComponent', () => {
it('should return a registered component reference', () => {
const mockComponent = {} as Type<unknown>;
service.registerComponent('testComponent', mockComponent);

const resolved = service.resolveComponent('testComponent');
expect(resolved).toBe(mockComponent);
});

it('should throw an error if component name is not provided', () => {
expect(() => service.resolveComponent('')).toThrow(
'Component must be provided'
);
});

it('should resolve a component dynamically through a resolver', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);

expect(service.resolveComponent('dynamicComponent')).toBe(
dynamicComponent
);
});

it('should fallback to static registration if no resolver matches', () => {
const staticComponent = {} as Type<unknown>;
service.registerComponent('staticComponent', staticComponent);

const resolver: ComponentResolver = () => undefined;
service.registerResolver(resolver);

expect(service.resolveComponent('staticComponent')).toBe(
staticComponent
);
});
});

describe('registerResolver', () => {
it('should register a new resolver for dynamic component resolution', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);

expect(service.resolveComponent('dynamicComponent')).toBe(
dynamicComponent
);
});
});

describe('unregisterResolver', () => {
it('should unregister a resolver', () => {
const dynamicComponent = {} as Type<unknown>;
const resolver: ComponentResolver = (component) =>
component === 'dynamicComponent' ? dynamicComponent : undefined;

service.registerResolver(resolver);
service.unregisterResolver(resolver);

expect(
service.resolveComponent('dynamicComponent')
).toBeUndefined();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('DockviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('DockviewAngularComponent: components input is required');
});

it('should initialize dockview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('GridviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('GridviewAngularComponent: components input is required');
});

it('should initialize gridview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('PaneviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('PaneviewAngularComponent: components input is required');
});

it('should initialize paneview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ describe('SplitviewAngularComponent', () => {
expect(component).toBeTruthy();
});

it('should throw error if components input is not provided', () => {
component.components = undefined as any;

expect(() => {
component.ngOnInit();
}).toThrow('SplitviewAngularComponent: components input is required');
});

it('should initialize splitview api on ngOnInit', () => {
component.ngOnInit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from 'dockview-core';
import { AngularFrameworkComponentFactory } from '../utils/component-factory';
import { AngularLifecycleManager } from '../utils/lifecycle-utils';
import { ComponentRegistryService } from '../utils/component-registry.service';

export interface DockviewAngularOptions extends DockviewOptions {
components: Record<string, Type<any>>;
Expand Down Expand Up @@ -60,10 +61,14 @@ export interface DockviewAngularOptions extends DockviewOptions {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DockviewAngularComponent implements OnInit, OnDestroy, OnChanges {
private readonly componentRegistry: ComponentRegistryService = inject(
ComponentRegistryService
);

@ViewChild('dockviewContainer', { static: true })
private containerRef!: ElementRef<HTMLDivElement>;

@Input() components!: Record<string, Type<any>>;
@Input() components?: Record<string, Type<any>>;
@Input() tabComponents?: Record<string, Type<any>>;
@Input() watermarkComponent?: Type<any>;
@Input() defaultTabComponent?: Type<any>;
Expand Down Expand Up @@ -130,10 +135,8 @@ export class DockviewAngularComponent implements OnInit, OnDestroy, OnChanges {
}

private initializeDockview(): void {
if (!this.components) {
throw new Error(
'DockviewAngularComponent: components input is required'
);
if (this.components) {
this.componentRegistry.registerComponents(this.components);
}

const coreOptions = this.extractCoreOptions();
Expand Down Expand Up @@ -178,7 +181,7 @@ export class DockviewAngularComponent implements OnInit, OnDestroy, OnChanges {
}

const componentFactory = new AngularFrameworkComponentFactory(
this.components,
this.componentRegistry,
this.injector,
this.environmentInjector,
this.tabComponents,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { AngularFrameworkComponentFactory } from '../utils/component-factory';
import { AngularLifecycleManager } from '../utils/lifecycle-utils';
import { GridviewAngularReadyEvent } from './types';
import { ComponentRegistryService } from '../utils/component-registry.service';

export interface GridviewAngularOptions extends GridviewOptions {
components: Record<string, Type<any>>;
Expand All @@ -52,10 +53,14 @@ export interface GridviewAngularOptions extends GridviewOptions {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GridviewAngularComponent implements OnInit, OnDestroy, OnChanges {
private readonly componentRegistry: ComponentRegistryService = inject(
ComponentRegistryService
);

@ViewChild('gridviewContainer', { static: true })
private containerRef!: ElementRef<HTMLDivElement>;

@Input() components!: Record<string, Type<any>>;
@Input() components?: Record<string, Type<any>>;

// Core gridview options as inputs
@Input() className?: string;
Expand Down Expand Up @@ -107,10 +112,8 @@ export class GridviewAngularComponent implements OnInit, OnDestroy, OnChanges {
}

private initializeGridview(): void {
if (!this.components) {
throw new Error(
'GridviewAngularComponent: components input is required'
);
if (this.components) {
this.componentRegistry.registerComponents(this.components);
}

const coreOptions = this.extractCoreOptions();
Expand Down Expand Up @@ -140,7 +143,7 @@ export class GridviewAngularComponent implements OnInit, OnDestroy, OnChanges {

private createFrameworkOptions(): GridviewFrameworkOptions {
const componentFactory = new AngularFrameworkComponentFactory(
this.components,
this.componentRegistry,
this.injector,
this.environmentInjector
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { AngularFrameworkComponentFactory } from '../utils/component-factory';
import { AngularLifecycleManager } from '../utils/lifecycle-utils';
import { PaneviewAngularReadyEvent } from './types';
import { AngularPanePart } from './angular-pane-part';
import { ComponentRegistryService } from '../utils/component-registry.service';

export interface PaneviewAngularOptions extends PaneviewOptions {
components: Record<string, Type<any>>;
Expand Down Expand Up @@ -55,10 +56,14 @@ export interface PaneviewAngularOptions extends PaneviewOptions {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PaneviewAngularComponent implements OnInit, OnDestroy, OnChanges {
private readonly componentRegistry: ComponentRegistryService = inject(
ComponentRegistryService
);

@ViewChild('paneviewContainer', { static: true })
private containerRef!: ElementRef<HTMLDivElement>;

@Input() components!: Record<string, Type<any>>;
@Input() components?: Record<string, Type<any>>;
@Input() headerComponents?: Record<string, Type<any>>;

// Core paneview options as inputs
Expand Down Expand Up @@ -111,10 +116,8 @@ export class PaneviewAngularComponent implements OnInit, OnDestroy, OnChanges {
}

private initializePaneview(): void {
if (!this.components) {
throw new Error(
'PaneviewAngularComponent: components input is required'
);
if (this.components) {
this.componentRegistry.registerComponents(this.components);
}

const coreOptions = this.extractCoreOptions();
Expand Down Expand Up @@ -147,7 +150,7 @@ export class PaneviewAngularComponent implements OnInit, OnDestroy, OnChanges {

private createFrameworkOptions(): PaneviewFrameworkOptions {
const componentFactory = new AngularFrameworkComponentFactory(
this.components,
this.componentRegistry,
this.injector,
this.environmentInjector,
this.headerComponents
Expand Down
Loading
Loading