migrate @umbraco-cms/controller to libs

This commit is contained in:
Jacob Overgaard
2023-01-20 13:28:41 +01:00
parent 9815303c90
commit e53ee08093
10 changed files with 23 additions and 37 deletions

View File

@@ -0,0 +1,112 @@
import type { HTMLElementConstructor } from '../../src/core/models';
import { UmbControllerInterface } from './controller.interface';
export declare class UmbControllerHostInterface extends HTMLElement {
//#controllers:UmbController[];
//#attached:boolean;
hasController(controller: UmbControllerInterface): boolean;
getControllers(filterMethod: (ctrl: UmbControllerInterface) => boolean): UmbControllerInterface[];
addController(controller: UmbControllerInterface): void;
removeController(controller: UmbControllerInterface): void;
}
/**
* This mixin enables the component to host controllers.
* This is done by calling the `consumeContext` method.
*
* @param {Object} superClass - superclass to be extended.
* @mixin
*/
export const UmbControllerHostMixin = <T extends HTMLElementConstructor>(superClass: T) => {
class UmbContextConsumerClass extends superClass {
#controllers: UmbControllerInterface[] = [];
#attached = false;
/**
* Tests if a controller is assigned to this element.
* @param {UmbControllerInterface} ctrl
*/
hasController(ctrl: UmbControllerInterface): boolean {
return this.#controllers.indexOf(ctrl) !== -1;
}
/**
* Retrieve controllers matching a filter of this element.
* @param {method} filterMethod
*/
getControllers(filterMethod: (ctrl: UmbControllerInterface) => boolean): UmbControllerInterface[] {
return this.#controllers.filter(filterMethod);
}
/**
* Append a controller to this element.
* @param {UmbControllerInterface} ctrl
*/
addController(ctrl: UmbControllerInterface): void {
// Check if there is one already with same unique
if (ctrl.unique) {
this.#controllers.forEach((x) => {
if (x.unique === ctrl.unique) {
this.removeController(x);
}
});
}
this.#controllers.push(ctrl);
if (this.#attached) {
ctrl.hostConnected();
}
}
/**
* Remove a controller from this element.
* Notice this will also destroy the controller.
* @param {UmbControllerInterface} ctrl
*/
removeController(ctrl: UmbControllerInterface): void {
const index = this.#controllers.indexOf(ctrl);
if (index !== -1) {
this.#controllers.splice(index, 1);
if (this.#attached) {
ctrl.hostDisconnected();
}
ctrl.destroy();
}
}
/**
* Remove a controller from this element by its alias.
* Notice this will also destroy the controller.
* @param {string} unique
*/
removeControllerByAlias(unique: string): void {
this.#controllers.forEach((x) => {
if (x.unique === unique) {
this.removeController(x);
}
});
}
connectedCallback() {
super.connectedCallback?.();
this.#attached = true;
this.#controllers.forEach((ctrl: UmbControllerInterface) => ctrl.hostConnected());
}
disconnectedCallback() {
super.disconnectedCallback?.();
this.#attached = false;
this.#controllers.forEach((ctrl: UmbControllerInterface) => ctrl.hostDisconnected());
}
}
return UmbContextConsumerClass as unknown as HTMLElementConstructor<UmbControllerHostInterface> & T;
};
declare global {
interface HTMLElement {
connectedCallback(): void;
disconnectedCallback(): void;
}
}

View File

@@ -0,0 +1,27 @@
import { UmbControllerHostInterface } from './controller-host.mixin';
import { UmbControllerInterface } from './controller.interface';
export abstract class UmbController implements UmbControllerInterface {
protected host?: UmbControllerHostInterface;
private _alias?: string;
public get unique() {
return this._alias;
}
constructor(host: UmbControllerHostInterface, alias?: string) {
this.host = host;
this._alias = alias;
this.host.addController(this);
}
abstract hostConnected(): void;
abstract hostDisconnected(): void;
public destroy() {
if (this.host) {
this.host.removeController(this);
}
delete this.host;
}
}

View File

@@ -0,0 +1,6 @@
export interface UmbControllerInterface {
get unique(): string | undefined;
hostConnected(): void;
hostDisconnected(): void;
destroy(): void;
}

View File

@@ -0,0 +1,44 @@
import { expect } from '@open-wc/testing';
import { customElement } from 'lit/decorators.js';
import { UmbControllerHostInterface, UmbControllerHostMixin } from './controller-host.mixin';
import { UmbContextProviderController } from '@umbraco-cms/context-api';
class MyClass {
prop = 'value from provider';
}
@customElement('test-my-controller-host')
export class MyHostElement extends UmbControllerHostMixin(HTMLElement) {}
describe('UmbContextProvider', () => {
type NewType = UmbControllerHostInterface;
let hostElement: NewType;
const contextInstance = new MyClass();
beforeEach(() => {
hostElement = document.createElement('test-my-controller-host') as UmbControllerHostInterface;
});
describe('Destroyed controllers is gone from host', () => {
it('has a host property', () => {
const ctrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
expect(hostElement.hasController(ctrl)).to.be.true;
ctrl.destroy();
expect(hostElement.hasController(ctrl)).to.be.false;
});
});
describe('Unique controllers replace each other', () => {
it('has a host property', () => {
const firstCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
const secondCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
expect(hostElement.hasController(firstCtrl)).to.be.false;
expect(hostElement.hasController(secondCtrl)).to.be.true;
});
});
});

View File

@@ -0,0 +1,3 @@
export * from './controller-host.mixin';
export * from './controller.class';
export * from './controller.interface';