Repository: exadel-inc/esl
Pull request #1633 (2023-04-28 16:25:00)

feat: ES5/IE11 cleanup (part 1)


Patch:
diff --git a/pages/src/landing/marquee/marquee.ts b/pages/src/landing/marquee/marquee.ts
index ab1265159..73f660c19 100644
--- a/pages/src/landing/marquee/marquee.ts
+++ b/pages/src/landing/marquee/marquee.ts
@@ -1,7 +1,6 @@
 import {ESLBaseElement} from '../../../../src/modules/esl-base-element/core';
 import {bind, ready, memoize, attr} from '../../../../src/modules/esl-utils/decorators';
 import {range} from '../../../../src/modules/esl-utils/misc/array';
-import {isIE} from '../../../../src/modules/esl-utils/environment/device-detector';
 
 export class ESLDemoMarquee extends ESLBaseElement {
   static override is = 'esl-d-marquee';
@@ -16,7 +15,6 @@ export class ESLDemoMarquee extends ESLBaseElement {
   @ready
   protected override connectedCallback(): void {
     super.connectedCallback();
-    if (isIE) return;
     this.startAnimation();
   }
   protected override disconnectedCallback(): void {
diff --git a/src/modules/esl-alert/core/esl-alert.less b/src/modules/esl-alert/core/esl-alert.less
index 20abc75df..b1b979958 100644
--- a/src/modules/esl-alert/core/esl-alert.less
+++ b/src/modules/esl-alert/core/esl-alert.less
@@ -1,5 +1,3 @@
-@import "../../esl-utils/fixes/ie-fixes";
-
 esl-alert {
   display: flex;
   justify-content: center;
diff --git a/src/modules/esl-alert/core/esl-alert.ts b/src/modules/esl-alert/core/esl-alert.ts
index e709f3b45..54b7b2ea8 100644
--- a/src/modules/esl-alert/core/esl-alert.ts
+++ b/src/modules/esl-alert/core/esl-alert.ts
@@ -2,9 +2,7 @@ import {ExportNs} from '../../esl-utils/environment/export-ns';
 import {attr, jsonAttr, prop, listen} from '../../esl-utils/decorators';
 import {isMatches} from '../../esl-utils/dom/traversing';
 import {ESLToggleable} from '../../esl-toggleable/core';
-import {DeviceDetector} from '../../esl-utils/environment/device-detector';
 import {CSSClassUtils} from '../../esl-utils/dom/class';
-import {createZIndexIframe} from '../../esl-utils/fixes/ie-fixes';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
 
 import type {ESLToggleableActionParams, ESLToggleableRequestDetails} from '../../esl-toggleable/core';
@@ -87,7 +85,6 @@ export class ESLAlert extends ESLToggleable {
     this.$content.className = 'esl-alert-content';
     this.innerHTML = '';
     this.appendChild(this.$content);
-    if (DeviceDetector.isIE) this.appendChild(createZIndexIframe());
     if (this.target) {
       this.$target = ESLTraversingQuery.first(this.target, this) as EventTarget;
     }
diff --git a/src/modules/esl-utils/dom/events/target.ts b/src/modules/esl-utils/dom/events/target.ts
index d7508d2f4..1697196c2 100644
--- a/src/modules/esl-utils/dom/events/target.ts
+++ b/src/modules/esl-utils/dom/events/target.ts
@@ -1,4 +1,4 @@
-import {flat, uniq} from '../../misc/array';
+import {uniq} from '../../misc/array';
 import {overrideEvent} from './misc';
 
 /** Key to store listeners on the {@link SyntheticEventTarget} instance*/
@@ -10,7 +10,7 @@ const LISTENERS = Symbol('_listeners');
  * Doesn't give explicit access to callback storage
  */
 export class SyntheticEventTarget implements EventTarget {
-  // Event type to use in the shortcutted calls
+  // Event type to use in the shortcut calls
   public static DEFAULT_EVENT = 'change';
 
   private readonly [LISTENERS]: Record<string, EventListenerOrEventListenerObject[]> = {};
@@ -18,19 +18,13 @@ export class SyntheticEventTarget implements EventTarget {
   protected getEventListeners(): EventListenerOrEventListenerObject[];
   protected getEventListeners(type: string): EventListenerOrEventListenerObject[];
   protected getEventListeners(type?: string): EventListenerOrEventListenerObject[] {
-    if (typeof type !== 'string') return uniq(flat(Object.values(this[LISTENERS])));
+    if (typeof type !== 'string') return uniq(Object.values(this[LISTENERS]).flat(1));
     return this[LISTENERS][type] || [];
   }
 
-  public hasEventListener(): boolean;
-  public hasEventListener(type: string): boolean;
-  /** @deprecated alias for `addEventListener` */
-  public hasEventListener(type: string | number): boolean;
-  /** @deprecated alias for `addEventListener` */
-  public hasEventListener(type: string, minCount: number): boolean;
-  public hasEventListener(type: string | number = 'change', minCount: number = 0): boolean {
-    if (typeof type !== 'string') return this.hasEventListener('change', type || 0); // TODO: remove in 5.0.0
-    return this.getEventListeners(type).length > minCount;
+  public hasEventListener(type?: string): boolean {
+    if (typeof type === 'string') return this.getEventListeners(type).length > 0;
+    return this.hasEventListener((this.constructor as typeof SyntheticEventTarget).DEFAULT_EVENT);
   }
 
   public addEventListener(callback: EventListenerOrEventListenerObject): void;
@@ -56,14 +50,10 @@ export class SyntheticEventTarget implements EventTarget {
     this[LISTENERS][type] = listeners.filter((cb) => cb !== callback);
   }
 
-  public dispatchEvent(e: Event): boolean;
-  /** @deprecated use `overrideEvent` to declare `target` */
-  public dispatchEvent(e: Event, target?: EventTarget): boolean;
-  public dispatchEvent(e: Event, target?: EventTarget): boolean {
+  public dispatchEvent(e: Event): boolean {
     overrideEvent(e, 'currentTarget', this);
-    if (target) overrideEvent(e, 'target', target); // TODO: remove in 5.0.0
     if (!e.target) overrideEvent(e, 'target', this);
-    if (!e.srcElement) overrideEvent(e, 'srcElement', e.target); // TODO: remove in 5.0.0
+    if (!e.srcElement) overrideEvent(e, 'srcElement', e.target);
     this.getEventListeners(e.type).forEach((listener) => {
       if (typeof listener === 'function') listener.call(this, e);
       else listener.handleEvent.call(listener, e);
diff --git a/src/modules/esl-utils/fixes.ts b/src/modules/esl-utils/fixes.ts
index 5ea3790a0..6265f18a8 100644
--- a/src/modules/esl-utils/fixes.ts
+++ b/src/modules/esl-utils/fixes.ts
@@ -1,2 +1,1 @@
-export * from './fixes/ie-fixes';
 export * from './fixes/viewport';
diff --git a/src/modules/esl-utils/fixes/ie-fixes.less b/src/modules/esl-utils/fixes/ie-fixes.less
deleted file mode 100644
index f1b197cdb..000000000
--- a/src/modules/esl-utils/fixes/ie-fixes.less
+++ /dev/null
@@ -1,10 +0,0 @@
-.ie-zindex-fix {
-  position: absolute;
-  top: 0;
-  left: 0;
-  height: 100%;
-  width: 100%;
-  z-index: -1;
-  border: none;
-  visibility: hidden;
-}
diff --git a/src/modules/esl-utils/fixes/ie-fixes.ts b/src/modules/esl-utils/fixes/ie-fixes.ts
deleted file mode 100644
index 8f5980081..000000000
--- a/src/modules/esl-utils/fixes/ie-fixes.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/** Fix IE browser to allow to display alert under iframe */
-export function createZIndexIframe(): HTMLElement {
-  const iframe = document.createElement('iframe');
-  iframe.className = 'ie-zindex-fix';
-  iframe.src = 'about:blank';
-  return iframe;
-}
diff --git a/src/modules/esl-utils/fixes/viewport.ts b/src/modules/esl-utils/fixes/viewport.ts
index b96e0ab29..2feecc10c 100644
--- a/src/modules/esl-utils/fixes/viewport.ts
+++ b/src/modules/esl-utils/fixes/viewport.ts
@@ -12,9 +12,6 @@ export class ESLVSizeCSSProxy {
 
   /** Inits custom CSS variables for viewport sizes and it's observation */
   public static observe(): void {
-    // IE doesn’t support CSS Variables (hopefully same as 100vh issue :D)
-    if (DeviceDetector.isIE) return;
-
     this.update();
     window.addEventListener('resize', this.updateDebounced);
   }
diff --git a/src/modules/esl-utils/misc/array.ts b/src/modules/esl-utils/misc/array.ts
index d1f49c17b..c5c5515ac 100644
--- a/src/modules/esl-utils/misc/array.ts
+++ b/src/modules/esl-utils/misc/array.ts
@@ -10,9 +10,11 @@ export const tuple = <T>(arr: T[]): Tuple<T>[] => arr.reduce((acc: Tuple<T>[], e
   return acc;
 }, []);
 
-/** Flat array - unwraps one level of nested arrays */
-export const flat = <T>(arr: (null | T | T[])[]): T[] =>
-  arr.reduce((acc: T[], el) => el ? acc.concat(el) : acc, []) as T[];
+/**
+ * Flat array - unwraps one level of nested arrays
+ * @deprecated use `Array.prototype.flat` instead
+ */
+export const flat = <T>(arr: (null | T | T[])[]): T[] => arr.flat(1) as T[];
 
 /** Wraps passed object or primitive to array */
 export const wrap = <T>(arr: undefined | null | T | T[]): T[] => {
@@ -23,22 +25,18 @@ export const wrap = <T>(arr: undefined | null | T | T[]): T[] => {
 
 /** Unwraps and returns the first element if passed object is array-like, returns original object otherwise */
 export function unwrap(value: []): undefined;
-export function unwrap<T>(value: (ArrayLike<T> & {0: T}) | T): T;
+export function unwrap<T>(value: (ArrayLike<T> & {0: T})): T;
+export function unwrap<T>(value: ArrayLike<T>): T | undefined;
 export function unwrap<T extends Node>(value: NodeListOf<T>): T | undefined;
+export function unwrap<T>(value: T): T;
 export function unwrap(value: any): any;
 export function unwrap(value: any): any {
   return isArrayLike(value) ? value[0] : value;
 }
 
 /** Makes array values unique */
-export const uniq = <T> (arr: T[]): T[] => {
-  if (arr.length < 2) return arr.slice(0);
-  const result: T[] = [];
-  const set = new Set<T>();
-  arr.forEach((item) => set.add(item));
-  set.forEach((item) => result.push(item));
-  return result;
-};
+export const uniq = <T> (arr: T[]): T[] =>
+  arr.length > 1 ? [...new Set<T>(arr)] : arr.slice(0);
 
 /** Create an array filled with the range [0,..,N-1] */
 export function range(n: number): number[];
diff --git a/src/modules/esl-utils/misc/set.ts b/src/modules/esl-utils/misc/set.ts
index 05ff92074..b66d85fb1 100644
--- a/src/modules/esl-utils/misc/set.ts
+++ b/src/modules/esl-utils/misc/set.ts
@@ -8,11 +8,9 @@ export function intersection<T>(a: T[], b: T[], ...rest: T[][]): T[] {
 
 /** Create an array with unique values from each of the passed arrays */
 export function union<T>(...rest: T[][]): T[]  {
-  const set = new Set();
+  const set = new Set<T>();
   rest.forEach((item) => item.forEach((i) => set.add(i)));
-  const result: any[] = [];
-  set.forEach((value) => result.push(value));
-  return result;
+  return [...set];
 }
 
 /** Creates an array of unique values from the first array that are not present in the other arrays */


Test patch:
diff --git a/src/modules/esl-utils/dom/events/test/target.test.ts b/src/modules/esl-utils/dom/events/test/target.test.ts
index 394af4f2c..faecbc7eb 100644
--- a/src/modules/esl-utils/dom/events/test/target.test.ts
+++ b/src/modules/esl-utils/dom/events/test/target.test.ts
@@ -30,18 +30,6 @@ describe('dom/events: SyntheticEventTarget', () => {
         expect(listener).toHaveBeenLastCalledWith(event2);
       });
 
-      test('listener should be stored', () => {
-        expect(et.hasEventListener('change', 0)).toBe(true);
-      });
-
-      test('target shouldn`t have more listeners than it actually has', () => {
-        expect(et.hasEventListener('change', 1)).toBe(false);
-      });
-
-      test('listener shouldn`t be stored for wrong event type', () => {
-        expect(et.hasEventListener('change2', 0)).toBe(false);
-      });
-
       test('listener shouldn`t be called third time', () => {
         et.removeEventListener('change', listener);
         et.dispatchEvent(event3);
@@ -81,10 +69,6 @@ describe('dom/events: SyntheticEventTarget', () => {
         expect(et.hasEventListener('change2')).toBe(false);
       });
 
-      test('target shouldn`t have more listeners than it actually has', () => {
-        expect(et.hasEventListener(1)).toBe(false);
-      });
-
       test('listener shouldn`t be called second time', () => {
         et.removeEventListener(listener);
         et.dispatchEvent(event2);
@@ -186,19 +170,4 @@ describe('dom/events: SyntheticEventTarget', () => {
       });
     });
   });
-
-  describe('Listener duplicate subscription', () => {
-    const et = new SyntheticEventTarget();
-    const listener = jest.fn(() => {});
-
-    test('event should be subscribed', () => {
-      et.addEventListener('change', listener);
-      expect(et.hasEventListener(1)).toBe(false);
-    });
-
-    test('event shouldn`t be subscribed again', () => {
-      et.addEventListener('change', listener);
-      expect(et.hasEventListener(1)).toBe(false);
-    });
-  });
 });
diff --git a/src/modules/esl-utils/misc/test/array.test.ts b/src/modules/esl-utils/misc/test/array.test.ts
index 4739782f6..bec218a58 100644
--- a/src/modules/esl-utils/misc/test/array.test.ts
+++ b/src/modules/esl-utils/misc/test/array.test.ts
@@ -13,12 +13,13 @@ describe('misc/array helper tests', () => {
 
   test('flat', () => {
     expect(flat([])).toEqual([]);
+    expect(flat([0])).toEqual([0]);
     expect(flat([1])).toEqual([1]);
     expect(flat([1, 2])).toEqual([1, 2]);
     expect(flat([1, [2, 3]])).toEqual([1, 2, 3]);
     expect(flat([[1, 2], [3, 4]])).toEqual([1, 2, 3, 4]);
     expect(flat([[1], [2, 3, 4], [], [5]])).toEqual([1, 2, 3, 4, 5]);
-    expect(flat([null, 1, 2, 3, [4, 5], null, [6]])).toEqual([1, 2, 3, 4, 5, 6]);
+    expect(flat([null, 1, 2, 3, [4, 5], null, [6]])).toEqual([null, 1, 2, 3, 4, 5, null, 6]);
   });
 
   test('wrap', () => {
@@ -43,6 +44,7 @@ describe('misc/array helper tests', () => {
 
   test('range', () => {
     expect(range(3)).toEqual([0, 1, 2]);
+    // @ts-ignore
     expect(range(9, (x) => x / 8)).toEqual([...Array(9).keys()].map((x) => x / 8));
   });