diff --git a/client-src/overlay.js b/client-src/overlay.js index 81b5f05de4..0de01e9834 100644 --- a/client-src/overlay.js +++ b/client-src/overlay.js @@ -644,11 +644,34 @@ const createOverlay = (options) => { }, trustedTypesPolicyName); } + /** @type {(event: KeyboardEvent) => void} */ + let handleEscapeKey; + + /** + * @returns {void} + */ + + const hideOverlayWithEscCleanup = () => { + window.removeEventListener("keydown", handleEscapeKey); + hide(); + }; + const overlayService = createOverlayMachine({ showOverlay: ({ level = "error", messages, messageSource }) => show(level, messages, options.trustedTypesPolicyName, messageSource), - hideOverlay: hide, + hideOverlay: hideOverlayWithEscCleanup, }); + /** + * ESC key press to dismiss the overlay. + * @param {KeyboardEvent} event Keydown event + */ + handleEscapeKey = (event) => { + if (event.key === "Escape" || event.key === "Esc" || event.keyCode === 27) { + overlayService.send({ type: "DISMISS" }); + } + }; + + window.addEventListener("keydown", handleEscapeKey); if (options.catchRuntimeError) { /** diff --git a/test/client/ReactErrorBoundary.test.js b/test/client/ReactErrorBoundary.test.js index 0313386b9c..8932de9a2c 100644 --- a/test/client/ReactErrorBoundary.test.js +++ b/test/client/ReactErrorBoundary.test.js @@ -232,4 +232,41 @@ describe("createOverlay", () => { }); showOverlayMock.mockRestore(); }); + // ESC key test cases + + it("should dismiss overlay when ESC key is pressed", () => { + const options = { trustedTypesPolicyName: null, catchRuntimeError: true }; + const overlay = createOverlay(options); + const showOverlayMock = jest.spyOn(overlay, "send"); + + const escEvent = new KeyboardEvent("keydown", { key: "Escape" }); + globalThis.window.dispatchEvent(escEvent); + + expect(showOverlayMock).toHaveBeenCalledWith({ type: "DISMISS" }); + showOverlayMock.mockRestore(); + }); + + it("should dismiss overlay when 'Esc' key is pressed (older browsers)", () => { + const options = { trustedTypesPolicyName: null, catchRuntimeError: true }; + const overlay = createOverlay(options); + const showOverlayMock = jest.spyOn(overlay, "send"); + + const escEvent = new KeyboardEvent("keydown", { key: "Esc" }); + globalThis.window.dispatchEvent(escEvent); + + expect(showOverlayMock).toHaveBeenCalledWith({ type: "DISMISS" }); + showOverlayMock.mockRestore(); + }); + + it("should not dismiss overlay for other keys", () => { + const options = { trustedTypesPolicyName: null, catchRuntimeError: true }; + const overlay = createOverlay(options); + const showOverlayMock = jest.spyOn(overlay, "send"); + + const otherKeyEvent = new KeyboardEvent("keydown", { key: "Enter" }); + globalThis.window.dispatchEvent(otherKeyEvent); + + expect(showOverlayMock).not.toHaveBeenCalled(); + showOverlayMock.mockRestore(); + }); });