Repository: exadel-inc/esl
Pull request #1551 (2023-03-15 00:42:27)

Revert "feat(esl-core): make this.constructor strictly typed for `ESLBaseElement` and `ESLMixinElement`"
Reverts exadel-inc/esl#1536


Patch:
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index 4395ed68b..e709f3b45 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -62,8 +62,7 @@ export class ESLAlert extends ESLToggleable {
   public static init(options?: Partial<ESLAlert>): ESLAlert {
     let alert: ESLAlert = document.querySelector(`body > ${ESLAlert.is}`)!;
     if (!alert) {
-      alert = document.createElement(ESLAlert.is) as ESLAlert;
-      options && Object.assign(alert, options);
+      alert = Object.assign(ESLAlert.create(), options || {});
       document.body.appendChild(alert);
     }
     return alert;
diff --git a/src/modules/esl-base-element/core/esl-base-element.ts b/src/modules/esl-base-element/core/esl-base-element.ts
index 2bd53b81b..a22ad8c23 100644
--- a/src/modules/esl-base-element/core/esl-base-element.ts
+++ b/src/modules/esl-base-element/core/esl-base-element.ts
@@ -19,18 +19,19 @@ export abstract class ESLBaseElement extends HTMLElement {
   /** Custom element tag name */
   public static is = '';
 
-  // @see https://github.com/Microsoft/TypeScript/issues/3841#issuecomment-337560146
-  // eslint-disable-next-line @typescript-eslint/ban-types
-  override ['constructor']!: typeof ESLBaseElement & Function;
-
   /** Event to indicate component significant state change that may affect other components state */
   @prop('esl:refresh') public REFRESH_EVENT: string;
 
   protected _connected: boolean = false;
 
+  /** @returns custom element tag name */
+  public get baseTagName(): string {
+    return (this.constructor as typeof ESLBaseElement).is;
+  }
+
   protected connectedCallback(): void {
     this._connected = true;
-    this.classList.add(this.constructor.is);
+    this.classList.add(this.baseTagName);
 
     ESLEventUtils.subscribe(this);
   }
diff --git a/src/modules/esl-footnotes/core/esl-note.ts b/src/modules/esl-footnotes/core/esl-note.ts
index a3b2e17f7..336ed466b 100644
--- a/src/modules/esl-footnotes/core/esl-note.ts
+++ b/src/modules/esl-footnotes/core/esl-note.ts
@@ -127,7 +127,7 @@ export class ESLNote extends ESLBaseElement {
 
   /** Gets attribute value from the closest element with group behavior settings */
   protected getClosestRelatedAttr(attrName: string): string | null {
-    const relatedAttrName = `${this.constructor.is}-${attrName}`;
+    const relatedAttrName = `${this.baseTagName}-${attrName}`;
     const $closest = this.closest(`[${relatedAttrName}]`);
     return $closest ? $closest.getAttribute(relatedAttrName) : null;
   }
diff --git a/src/modules/esl-forms/esl-select-list/core/esl-select-item.ts b/src/modules/esl-forms/esl-select-list/core/esl-select-item.ts
index ed7da4068..105565575 100644
--- a/src/modules/esl-forms/esl-select-list/core/esl-select-item.ts
+++ b/src/modules/esl-forms/esl-select-list/core/esl-select-item.ts
@@ -51,7 +51,7 @@ export class ESLSelectItem extends ESLBaseElement {
 
   /** Helper to create an option item */
   public static build(option: ESLSelectOption): ESLSelectItem {
-    const item = document.createElement(ESLSelectItem.is) as ESLSelectItem;
+    const item = ESLSelectItem.create();
     item.update(option);
     return item;
   }
diff --git a/src/modules/esl-forms/esl-select-list/core/esl-select-list.ts b/src/modules/esl-forms/esl-select-list/core/esl-select-list.ts
index 8f165f918..8fe0e9d67 100644
--- a/src/modules/esl-forms/esl-select-list/core/esl-select-list.ts
+++ b/src/modules/esl-forms/esl-select-list/core/esl-select-list.ts
@@ -40,9 +40,9 @@ export class ESLSelectList extends ESLSelectWrapper {
     this.$list.setAttribute('role', 'list');
     this.$list.classList.add('esl-scrollable-content');
     this.$list.classList.add('esl-select-list-container');
-    this.$scroll = document.createElement(ESLScrollbar.is) as ESLScrollbar;
+    this.$scroll = ESLScrollbar.create();
     this.$scroll.target = '::prev';
-    this.$selectAll = document.createElement(ESLSelectItem.is) as ESLSelectItem;
+    this.$selectAll = ESLSelectItem.create();
     this.$selectAll.classList.add('esl-select-all-item');
   }
 
diff --git a/src/modules/esl-mixin-element/ui/esl-mixin-element.ts b/src/modules/esl-mixin-element/ui/esl-mixin-element.ts
index 00448e4d4..95efa7c5b 100644
--- a/src/modules/esl-mixin-element/ui/esl-mixin-element.ts
+++ b/src/modules/esl-mixin-element/ui/esl-mixin-element.ts
@@ -24,10 +24,6 @@ export class ESLMixinElement implements ESLDomElementRelated {
   /** Additional observed attributes */
   static observedAttributes: string[] = [];
 
-  // @see https://github.com/Microsoft/TypeScript/issues/3841#issuecomment-337560146
-  // eslint-disable-next-line @typescript-eslint/ban-types
-  ['constructor']!: typeof ESLMixinElement & Function;
-
   /** Event to indicate component significant state change that may affect other components state */
   @prop('esl:refresh') public REFRESH_EVENT: string;
 
@@ -40,7 +36,7 @@ export class ESLMixinElement implements ESLDomElementRelated {
 
   /** Callback of mixin instance initialization */
   public connectedCallback(): void {
-    const {observedAttributes} = this.constructor;
+    const {observedAttributes} = this.constructor as typeof ESLMixinElement;
     if (observedAttributes.length) {
       this._attr$$ = new MutationObserver(this._onAttrMutation.bind(this));
       this._attr$$.observe(this.$host, {
diff --git a/src/modules/esl-toggleable/core/esl-toggleable.ts b/src/modules/esl-toggleable/core/esl-toggleable.ts
index 6d4857fa0..5d3f1fd55 100644
--- a/src/modules/esl-toggleable/core/esl-toggleable.ts
+++ b/src/modules/esl-toggleable/core/esl-toggleable.ts
@@ -121,7 +121,7 @@ export class ESLToggleable extends ESLBaseElement {
   protected override connectedCallback(): void {
     super.connectedCallback();
     if (!this.id && !this.noAutoId) {
-      this.id = sequentialUID(this.constructor.is, this.constructor.is + '-');
+      this.id = sequentialUID(this.baseTagName, this.baseTagName + '-');
     }
     this.initiallyOpened = this.hasAttribute('open');
     this.setInitialState();


Test patch:
diff --git a/src/modules/esl-base-element/test/element.listener.test.ts b/src/modules/esl-base-element/test/element.listener.test.ts
index a4d4384da..3daa05818 100644
--- a/src/modules/esl-base-element/test/element.listener.test.ts
+++ b/src/modules/esl-base-element/test/element.listener.test.ts
@@ -15,7 +15,7 @@ describe('ESLBaseElement: listeners', () => {
     }
     TestElement.register();
 
-    const el = document.createElement(TestElement.is);
+    const el = TestElement.create();
 
     beforeAll(() => document.body.appendChild(el));
 
diff --git a/src/modules/esl-toggleable/test/toggleable.request.test.ts b/src/modules/esl-toggleable/test/toggleable.request.test.ts
index a98d1300a..2ae48a3b4 100644
--- a/src/modules/esl-toggleable/test/toggleable.request.test.ts
+++ b/src/modules/esl-toggleable/test/toggleable.request.test.ts
@@ -6,7 +6,7 @@ describe('ESLToggleable: show/hide-request events', () => {
   beforeAll(() => ESLToggleable.register());
 
   describe('Direct events correctly caught by toggleable', () => {
-    const $el = document.createElement(ESLToggleable.is) as ESLToggleable;
+    const $el = ESLToggleable.create();
     jest.useFakeTimers();
     beforeAll(() => document.body.appendChild($el));
     afterAll(() => ($el.parentElement === document.body) && document.body.removeChild($el));
@@ -29,8 +29,8 @@ describe('ESLToggleable: show/hide-request events', () => {
   });
 
   describe('ESLToggleables catch and filtering bubbling events', () => {
-    const $root = document.createElement(ESLToggleable.is) as ESLToggleable;
-    const $childTbl = document.createElement(ESLToggleable.is) as ESLToggleable;
+    const $root = ESLToggleable.create();
+    const $childTbl = ESLToggleable.create();
     const $button = document.createElement('button');
     $root.classList.add('root');
     $root.appendChild($childTbl);
diff --git a/src/modules/esl-trigger/test/esl-trigger.a11y.test.ts b/src/modules/esl-trigger/test/esl-trigger.a11y.test.ts
index f2bfcaccd..29589abfa 100644
--- a/src/modules/esl-trigger/test/esl-trigger.a11y.test.ts
+++ b/src/modules/esl-trigger/test/esl-trigger.a11y.test.ts
@@ -1,12 +1,10 @@
 import '../../../polyfills/es5-target-shim';
-
 import {SyntheticEventTarget} from '../../esl-utils/dom/events/target';
 import {ESLEventUtils} from '../../esl-utils/dom/events';
 import {ESLTrigger} from '../core/esl-trigger';
-
 import type {ESLToggleable} from '../../esl-toggleable/core/esl-toggleable';
 
-function createTargetMock(init: any = {}): ESLToggleable {
+function createTargetMock(init: Partial<ESLToggleable> = {}): ESLToggleable {
   const et = new SyntheticEventTarget();
   return Object.assign(et, {
     show: jest.fn(function () {
@@ -18,7 +16,7 @@ function createTargetMock(init: any = {}): ESLToggleable {
       ESLEventUtils.dispatch(this, 'esl:hide');
     }),
     open: false
-  }, init);
+  }, init) as any;
 }
 
 describe('esl-trigger a11y attributes test', () => {