Repository: exadel-inc/esl
Pull request #2130 (2023-12-20 14:50:16)

fix(esl-event-listener): fix support for any object-like host


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f88594a9..8d26f7f8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,3 @@
-# [5.0.0-beta.2](https://github.com/exadel-inc/esl/compare/v5.0.0-beta.1...v5.0.0-beta.2) (2023-12-19)
-
-
-### Bug Fixes
-
-* **eslint:** fix peerDependency constraint ([20b5d9d](https://github.com/exadel-inc/esl/commit/20b5d9d2c8a1b1e51288524024dfe6c17b665671))
-
 # [5.0.0-beta.1](https://github.com/exadel-inc/esl/compare/v4.14.1...v5.0.0-beta.1) (2023-12-19)
 
 
diff --git a/eslint/package.json b/eslint/package.json
index 251b8e16a..bf7bb69f9 100644
--- a/eslint/package.json
+++ b/eslint/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@exadel/eslint-plugin-esl",
-  "version": "5.0.0-beta.2",
+  "version": "5.0.0-beta.1",
   "keywords": [
     "eslint",
     "eslint-plugin",
diff --git a/package-lock.json b/package-lock.json
index 61336cc64..5449113be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@exadel/esl",
-  "version": "5.0.0-beta.2",
+  "version": "5.0.0-beta.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@exadel/esl",
-      "version": "5.0.0-beta.2",
+      "version": "5.0.0-beta.1",
       "license": "MIT",
       "workspaces": [
         "pages",
@@ -59,7 +59,7 @@
     },
     "eslint": {
       "name": "@exadel/eslint-plugin-esl",
-      "version": "5.0.0-beta.2",
+      "version": "5.0.0-beta.1",
       "license": "MIT",
       "peerDependencies": {
         "@exadel/esl": "*",
@@ -19293,7 +19293,7 @@
     },
     "pages": {
       "name": "@exadel/esl-website",
-      "version": "5.0.0-beta.2",
+      "version": "5.0.0-beta.1",
       "license": "ISC",
       "dependencies": {
         "@11ty/eleventy": "^2.0.1",
diff --git a/package.json b/package.json
index 3c0bcf0e1..d5bd0645d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@exadel/esl",
-  "version": "5.0.0-beta.2",
+  "version": "5.0.0-beta.1",
   "private": false,
   "description": "Exadel Smart Library (ESL) is the lightweight custom elements library that provide a set of super-flexible components",
   "license": "MIT",
diff --git a/pages/package.json b/pages/package.json
index f1a54ab5d..c53d9fd26 100644
--- a/pages/package.json
+++ b/pages/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@exadel/esl-website",
-  "version": "5.0.0-beta.2",
+  "version": "5.0.0-beta.1",
   "private": true,
   "description": "ESL Website source",
   "homepage": "https://esl-ui.com/",
diff --git a/src/modules/esl-event-listener/core/listener.ts b/src/modules/esl-event-listener/core/listener.ts
index 17bff4ac3..fd3f69a39 100644
--- a/src/modules/esl-event-listener/core/listener.ts
+++ b/src/modules/esl-event-listener/core/listener.ts
@@ -2,7 +2,7 @@ import {wrap} from '../../esl-utils/misc/array';
 import {isElement} from '../../esl-utils/dom/api';
 import {isPassiveByDefault} from '../../esl-utils/dom/events/misc';
 import {resolveProperty} from '../../esl-utils/misc/functions';
-import {isObject, isSimilar} from '../../esl-utils/misc/object';
+import {isObject, isObjectLike, isSimilar} from '../../esl-utils/misc/object';
 import {resolveDomTarget} from '../../esl-utils/abstract/dom-target';
 import {memoize} from '../../esl-utils/decorators/memoize';
 import {ESLTraversingQuery} from '../../esl-traversing-query/core';
@@ -149,14 +149,14 @@ export class ESLEventListener implements ESLListenerDefinition, EventListenerObj
    * Supports additional filtration criteria
    */
   public static get(host?: object, ...criteria: ESLListenerCriteria[]): ESLEventListener[] {
-    if (!isObject(host)) return [];
+    if (!isObjectLike(host)) return [];
     const listeners = ((host as any)[LISTENERS] || []) as ESLEventListener[];
     if (!criteria.length) return listeners;
     return listeners.filter((listener) => criteria.every(listener.matches, listener));
   }
   /** Adds a listener to the listener store of the host object */
   protected static add(host: object, instance: ESLEventListener): void {
-    if (!isObject(host)) return;
+    if (!isObjectLike(host)) return;
     if (!Object.hasOwnProperty.call(host, LISTENERS)) (host as any)[LISTENERS] = [];
     (host as any)[LISTENERS].push(instance);
   }
@@ -201,7 +201,7 @@ export class ESLEventListener implements ESLListenerDefinition, EventListenerObj
     handler: ESLListenerHandler,
     desc: ESLListenerDescriptor
   ): ESLEventListener[] {
-    if (!isObject(host)) return [];
+    if (!isObjectLike(host)) return [];
     const eventString = resolveProperty(desc.event, host);
     const listeners: ESLEventListener[] = [];
     for (const event of splitEvents(eventString)) {


Test patch:
diff --git a/src/modules/esl-event-listener/test/listener.subscribe.test.ts b/src/modules/esl-event-listener/test/listener.subscribe.test.ts
index bc32bc9da..e7bdaeec4 100644
--- a/src/modules/esl-event-listener/test/listener.subscribe.test.ts
+++ b/src/modules/esl-event-listener/test/listener.subscribe.test.ts
@@ -73,4 +73,33 @@ describe('ESLEventUtils:subscribe tests', () => {
     ESLEventUtils.subscribe(host, {event: 'click'}, handle);
     expect(ESLEventUtils.listeners(host).length).toBe(0);
   });
+
+  describe('ESLEventListener subscribes correctly for any object-like host', () => {
+    test.each([
+      {},
+      Object.create(null),
+      function hostFunction() {},
+      class HostClass {},
+      new (class HostClassInst {})()
+    ])('ESLEventListener subscribes correctly for %o host',  (host) => {
+      const handle = jest.fn();
+      ESLEventUtils.subscribe(host, {event: 'click', target: document.body}, handle);
+      expect(ESLEventUtils.listeners(host).length).toBe(1);
+      ESLEventUtils.unsubscribe(host);
+    });
+
+    test('ESLEventListener can not subscribe for primitive host', () => {
+      jest.spyOn(console, 'warn').mockImplementationOnce(() => {});
+      const handle = jest.fn();
+      const count = ESLEventUtils.subscribe(1 as any, {event: 'click', target: document.body}, handle).length;
+      expect(count).toBe(0);
+    });
+
+    test('ESLEventListener can not subscribe for null host', () => {
+      jest.spyOn(console, 'warn').mockImplementationOnce(() => {});
+      const handle = jest.fn();
+      const count = ESLEventUtils.subscribe(null as any, {event: 'click', target: document.body}, handle).length;
+      expect(count).toBe(0);
+    });
+  });
 });