From 98eae200cbae7235659b91c596a3839905370154 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 21 Jul 2025 16:09:42 +0330 Subject: [PATCH 1/6] fix(devtool): add zindex param for devtool --- packages/core/src/ClientSocketManager.ts | 8 ++++++-- packages/core/src/devtool/devtool.ts | 10 +++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/core/src/ClientSocketManager.ts b/packages/core/src/ClientSocketManager.ts index 1df2b45..766fc0b 100644 --- a/packages/core/src/ClientSocketManager.ts +++ b/packages/core/src/ClientSocketManager.ts @@ -462,9 +462,13 @@ class ClientSocketManager< /** * Show devtool in the browser programmatically. + * @param {object} options - Options for showing the devtool. + * @param {number} options.zIndex - Z-index of the devtool, overrides the previous z-index of the devtool. */ - public showDevtool(): void { - devtool.render({ force: true }); + public showDevtool(options?: { zIndex?: number }): void { + const { zIndex } = options ?? {}; + + devtool.render({ force: true, zIndex }); } /** diff --git a/packages/core/src/devtool/devtool.ts b/packages/core/src/devtool/devtool.ts index 154648f..5af4559 100644 --- a/packages/core/src/devtool/devtool.ts +++ b/packages/core/src/devtool/devtool.ts @@ -283,6 +283,9 @@ const init = () => { devtoolWrapper.style.position = "fixed"; devtoolWrapper.style.top = "8px"; devtoolWrapper.style.left = "8px"; + // initial z-index. can be override in render function. + devtoolWrapper.style.zIndex = "99999"; + devtoolWrapper.id = DEVTOOL_WRAPPER_ID; devtoolWrapper.innerHTML = renderDevtool(); @@ -334,10 +337,11 @@ const toggle = () => { type RenderOptions = { action?: (s: typeof devtool) => void; force?: boolean; + zIndex?: number; }; export const render = (options?: RenderOptions) => { - const { action, force = false } = options ?? {}; + const { action, force = false, zIndex } = options ?? {}; if (force) { init(); @@ -349,6 +353,10 @@ export const render = (options?: RenderOptions) => { if (!devtoolElement) init(); } + if (zIndex !== undefined) { + getDevtoolWrapperElement()!.style.zIndex = zIndex.toString(); + } + action?.(devtool); updateInfoSection(); }; From b31480c0e6a3b1289df55b21b48e299617be59ad Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 21 Jul 2025 16:11:13 +0330 Subject: [PATCH 2/6] docs(core): add zIndex param in readme file. --- packages/core/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/README.md b/packages/core/README.md index fde0365..73eec8f 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -213,11 +213,16 @@ and cleaned up. #### `showDevtool` ```ts -showDevtool(): void; +showDevtool(options): void; ``` Show devtool in the browser programmatically. +##### Parameters + +- `options`: Optional parameters. + - `zIndex`: Z-index of the devtool, overrides the previous z-index of the devtool. + #### `hideDevtool` ```ts From dcff35e4138099d6f250831308ed7fbb7d594e16 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 21 Jul 2025 16:15:45 +0330 Subject: [PATCH 3/6] docs: update changeset --- .changeset/chubby-boats-melt.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/chubby-boats-melt.md diff --git a/.changeset/chubby-boats-melt.md b/.changeset/chubby-boats-melt.md new file mode 100644 index 0000000..421f26b --- /dev/null +++ b/.changeset/chubby-boats-melt.md @@ -0,0 +1,6 @@ +--- +"@tapsioss/client-socket-manager": patch +--- + +Add `zIndex` param for `showDevtool` method. + \ No newline at end of file From 10f80497c0f53e2aa2e9ce5a1a787ba84343a8c0 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 21 Jul 2025 18:58:46 +0330 Subject: [PATCH 4/6] fix: move zIndex option to the constructor --- packages/core/src/ClientSocketManager.test.ts | 152 +++++++++++++++++- packages/core/src/ClientSocketManager.ts | 16 +- packages/core/src/devtool/devtool.test.ts | 1 + packages/core/src/devtool/devtool.ts | 20 ++- packages/core/src/types.ts | 31 ++-- playground/client/index.ts | 4 +- 6 files changed, 195 insertions(+), 29 deletions(-) diff --git a/packages/core/src/ClientSocketManager.test.ts b/packages/core/src/ClientSocketManager.test.ts index ed4582b..f6a38e5 100644 --- a/packages/core/src/ClientSocketManager.test.ts +++ b/packages/core/src/ClientSocketManager.test.ts @@ -299,7 +299,9 @@ describe("ClientSocketManager: unit tests", () => { socketManager.dispose(); socketManager = new ClientSocketManager(socketServerUri, { - devtool: true, + devtool: { + enabled: true, + }, }); expect(devtool.getDevtoolElement()).not.toBeNull(); }); @@ -311,7 +313,9 @@ describe("ClientSocketManager: unit tests", () => { const diposeResolver = createPromiseResolvers(); socketManager = new ClientSocketManager(socketServerUri, { - devtool: true, + devtool: { + enabled: true, + }, eventHandlers: { onSocketConnection() { connectResolver.resolve(); @@ -393,4 +397,148 @@ describe("ClientSocketManager: unit tests", () => { expect(socketManager.connected).toBe(false); expect(socketManager.disposed).toBe(true); }); + + it("should apply the specified z-index to the devtool and ensure correct click behavior", async () => { + const connectResolver = createPromiseResolvers(); + const zIndex = 20; + + // Create a div with a higher z-index to be on top of the devtool + const overlayDiv = document.createElement("div"); + + overlayDiv.style.position = "fixed"; + overlayDiv.style.top = "0"; + overlayDiv.style.left = "0"; + overlayDiv.style.width = "100%"; + overlayDiv.style.height = "100%"; + overlayDiv.style.backgroundColor = "rgba(0,0,0,0.1)"; // Semi-transparent + overlayDiv.style.zIndex = (zIndex + 1).toString(); // Higher z-index + overlayDiv.id = "overlay-div"; + document.body.appendChild(overlayDiv); + + // Track clicks on the overlay and the devtool + let overlayClicked = false; + let devtoolClicked = false; + + overlayDiv.addEventListener("click", () => { + overlayClicked = true; + }); + + socketManager = new ClientSocketManager(socketServerUri, { + devtool: { + enabled: true, + zIndex, + }, + eventHandlers: { + onSocketConnection() { + connectResolver.resolve(); + }, + }, + }); + + await connectResolver.promise; + + const devtoolElement = devtool.getDevtoolElement(); + + expect(devtoolElement).not.toBeNull(); + + // Add a click listener to the devtool itself to detect if it's reachable + devtoolElement?.addEventListener("click", () => { + devtoolClicked = true; + }); + + if (overlayDiv) { + const { left, top, width, height } = overlayDiv.getBoundingClientRect(); + const clientX = left + width / 2; + const clientY = top + height / 2; + + const clickEvent = new MouseEvent("click", { + bubbles: true, + cancelable: true, + clientX, + clientY, + }); + + overlayDiv.dispatchEvent(clickEvent); // Dispatch click on the overlay + } + + // Assert that only the overlay was clicked, indicating it was on top + expect(overlayClicked).toBe(true); + expect(devtoolClicked).toBe(false); // This should now pass if the overlay intercepted the click + + // Clean up the overlay div + document.body.removeChild(overlayDiv); + }); + + it("should capture click when devtool z-index is higher than overlay", async () => { + const connectResolver = createPromiseResolvers(); + const devtoolZIndex = 30; // Devtool will have a higher z-index + + // Create an overlay div with a lower z-index + const overlayDiv = document.createElement("div"); + + overlayDiv.style.position = "fixed"; + overlayDiv.style.top = "0"; + overlayDiv.style.left = "0"; + overlayDiv.style.width = "100%"; + overlayDiv.style.height = "100%"; + overlayDiv.style.backgroundColor = "rgba(0,0,0,0.1)"; // Semi-transparent + overlayDiv.style.zIndex = (devtoolZIndex - 1).toString(); // Lower z-index than devtool + overlayDiv.id = "lower-overlay-div"; + document.body.appendChild(overlayDiv); + + let overlayClicked = false; + let devtoolClicked = false; + + overlayDiv.addEventListener("click", () => { + overlayClicked = true; + }); + + socketManager = new ClientSocketManager(socketServerUri, { + devtool: { + enabled: true, + zIndex: devtoolZIndex, // Set devtool's z-index to be higher + }, + eventHandlers: { + onSocketConnection() { + connectResolver.resolve(); + }, + }, + }); + + await connectResolver.promise; + + const devtoolElement = devtool.getDevtoolElement(); + + expect(devtoolElement).not.toBeNull(); + + devtoolElement?.addEventListener("click", () => { + devtoolClicked = true; + }); + + // Simulate a click on the devtool. + // Since the devtool's z-index is higher, it should receive the click. + if (devtoolElement) { + const { left, top, width, height } = + devtoolElement.getBoundingClientRect(); + + const clientX = left + width / 2; + const clientY = top + height / 2; + + const clickEvent = new MouseEvent("click", { + bubbles: true, + cancelable: true, + clientX, + clientY, + }); + + devtoolElement.dispatchEvent(clickEvent); // Dispatch click directly on the devtool + } + + // Assert that the devtool was clicked, and the overlay was not + expect(devtoolClicked).toBe(true); + expect(overlayClicked).toBe(false); + + // Clean up the overlay div + document.body.removeChild(overlayDiv); + }); }); diff --git a/packages/core/src/ClientSocketManager.ts b/packages/core/src/ClientSocketManager.ts index 766fc0b..171ef45 100644 --- a/packages/core/src/ClientSocketManager.ts +++ b/packages/core/src/ClientSocketManager.ts @@ -28,10 +28,13 @@ class ClientSocketManager< reconnectionDelay = 500, reconnectionDelayMax = 2000, eventHandlers, - devtool: devtoolOpt = false, + devtool: devtoolOpt, ...restOptions } = options ?? {}; + const { enabled: devtoolEnabled = false, zIndex: devtoolZIndex = 999999 } = + devtoolOpt ?? {}; + try { this._socket = io(uri, { ...restOptions, @@ -49,7 +52,8 @@ class ClientSocketManager< this._inputListeners.onInit?.call(this); - if (devtoolOpt) { + if (devtoolEnabled) { + devtool.setZIndex(devtoolZIndex); this.showDevtool(); } } catch (err) { @@ -462,13 +466,9 @@ class ClientSocketManager< /** * Show devtool in the browser programmatically. - * @param {object} options - Options for showing the devtool. - * @param {number} options.zIndex - Z-index of the devtool, overrides the previous z-index of the devtool. */ - public showDevtool(options?: { zIndex?: number }): void { - const { zIndex } = options ?? {}; - - devtool.render({ force: true, zIndex }); + public showDevtool(): void { + devtool.render({ force: true }); } /** diff --git a/packages/core/src/devtool/devtool.test.ts b/packages/core/src/devtool/devtool.test.ts index c9c159b..9182be2 100644 --- a/packages/core/src/devtool/devtool.test.ts +++ b/packages/core/src/devtool/devtool.test.ts @@ -10,6 +10,7 @@ import * as devtool from "./devtool.ts"; describe("Devtool", () => { beforeEach(() => { + devtool.setZIndex(99999); devtool.render({ force: true, action: s => { diff --git a/packages/core/src/devtool/devtool.ts b/packages/core/src/devtool/devtool.ts index 38f37bd..84ac623 100644 --- a/packages/core/src/devtool/devtool.ts +++ b/packages/core/src/devtool/devtool.ts @@ -54,6 +54,7 @@ const devtool: DevtoolState = { let active = false; let expanded = false; +let zIndex: number = NaN; export const renderDivider = () => { return `
`; @@ -282,11 +283,15 @@ const init = () => { const devtoolWrapper = document.createElement("div"); + if (Number.isNaN(zIndex)) { + throw new Error("No z-index was set for the devtool."); + } else { + devtoolWrapper.style.zIndex = `${zIndex}`; + } + devtoolWrapper.style.position = "fixed"; devtoolWrapper.style.top = "8px"; devtoolWrapper.style.left = "8px"; - // initial z-index. can be override in render function. - devtoolWrapper.style.zIndex = "99999"; devtoolWrapper.id = DEVTOOL_WRAPPER_ID; devtoolWrapper.innerHTML = renderDevtool(); @@ -317,6 +322,10 @@ const init = () => { }); }; +export const setZIndex = (z: number) => { + zIndex = z; +}; + export const dispose = () => { getDevtoolWrapperElement()?.remove(); @@ -343,11 +352,10 @@ const toggle = () => { type RenderOptions = { action?: (s: typeof devtool) => void; force?: boolean; - zIndex?: number; }; export const render = (options?: RenderOptions) => { - const { action, force = false, zIndex } = options ?? {}; + const { action, force = false } = options ?? {}; if (force) { init(); @@ -359,10 +367,6 @@ export const render = (options?: RenderOptions) => { if (!devtoolElement) init(); } - if (zIndex !== undefined) { - getDevtoolWrapperElement()!.style.zIndex = zIndex.toString(); - } - action?.(devtool); updateInfoSection(); }; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ec3ab5b..d4a2174 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -136,17 +136,28 @@ export type ClientSocketManagerOptions = OverrideMembers< */ eventHandlers?: ClientSocketManagerListenerOptions; /** - * Enables the in-browser DevTool panel for socket debugging. - * - * When set to `true`, a floating DevTool UI will appear in the browser that displays: - * - The current socket connection status (`connected`, `disconnected`, `reconnecting`) - * - A list of currently subscribed channels - * - A log panel showing socket events and debugging messages + * Client Socket Devtool options. * * This is useful for development and debugging purposes. - * In production environments, it's recommended to leave this disabled. - * - * @default false + * In production environments, it's recommended to leave this section empty. */ - devtool?: boolean; + devtool?: { + /** + * Enables the in-browser DevTool panel for socket debugging. + * + * When set to `true`, a floating DevTool UI will appear in the browser that displays: + * - The current socket connection status (`connected`, `disconnected`, `reconnecting`) + * - A list of currently subscribed channels + * - A log panel showing socket events and debugging messages. + * + * @default false + */ + enabled: boolean; + /** + * The `z-index` of the devtool. + * + * @default 9999 + */ + zIndex?: number; + }; }; diff --git a/playground/client/index.ts b/playground/client/index.ts index 156bdc9..25ec030 100644 --- a/playground/client/index.ts +++ b/playground/client/index.ts @@ -4,7 +4,9 @@ import { ClientSocketManager } from "@tapsioss/client-socket-manager"; const socketManager = new ClientSocketManager("http://localhost:3000", { - devtool: true, + devtool: { + enabled: true, + }, eventHandlers: { onReconnectingError(err) { console.log("reconnecting error", err); From 4490e9d0e88c7bcc525c7cda7e8f5ba0bed637a3 Mon Sep 17 00:00:00 2001 From: Amirhossein Alibakhshi Date: Mon, 21 Jul 2025 19:05:37 +0330 Subject: [PATCH 5/6] docs: update changelog --- .changeset/chubby-boats-melt.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.changeset/chubby-boats-melt.md b/.changeset/chubby-boats-melt.md index 421f26b..f7f1a2c 100644 --- a/.changeset/chubby-boats-melt.md +++ b/.changeset/chubby-boats-melt.md @@ -1,6 +1,9 @@ --- -"@tapsioss/client-socket-manager": patch +"@tapsioss/client-socket-manager": minor --- -Add `zIndex` param for `showDevtool` method. +- The `devtool` field within the `ClientSocketManager`'s constructor `options` has been updated from a boolean to an object. This change allows for more granular control over development tools. + - Previously: `devtool: boolean` + - Now: `devtool: { enabled: boolean; zIndex?: number; }` + - The `enabled` property maintains the previous boolean functionality, while the optional `zIndex` property allows for specifying the stacking order of devtool elements. \ No newline at end of file From 97632f2ba1b9a0ef221dce55769ea1d7e9ec4185 Mon Sep 17 00:00:00 2001 From: Mostafa Shamsitabar <50550858+mimshins@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:16:06 +0330 Subject: [PATCH 6/6] Update chubby-boats-melt.md --- .changeset/chubby-boats-melt.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/chubby-boats-melt.md b/.changeset/chubby-boats-melt.md index f7f1a2c..abdefe7 100644 --- a/.changeset/chubby-boats-melt.md +++ b/.changeset/chubby-boats-melt.md @@ -2,8 +2,8 @@ "@tapsioss/client-socket-manager": minor --- -- The `devtool` field within the `ClientSocketManager`'s constructor `options` has been updated from a boolean to an object. This change allows for more granular control over development tools. +- [BREAKING CHANGE] The `devtool` field within the `ClientSocketManager`'s constructor `options` has been updated from a boolean to an object. This change allows for more granular control over development tools. - Previously: `devtool: boolean` - Now: `devtool: { enabled: boolean; zIndex?: number; }` - The `enabled` property maintains the previous boolean functionality, while the optional `zIndex` property allows for specifying the stacking order of devtool elements. - \ No newline at end of file +