From 7ac18bff76a1a18017e835c65875981e74febe8d Mon Sep 17 00:00:00 2001 From: Riccardo Palleschi Date: Fri, 9 Oct 2026 21:38:03 +0200 Subject: [PATCH 1/3] fix(web): Mod+T opens the tab menu while a pull request tab is open --- .../RightPanelTabs.keyboard.test.tsx | 12 ++++++++++++ apps/web/src/components/RightPanelTabs.tsx | 19 ++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/web/src/components/RightPanelTabs.keyboard.test.tsx b/apps/web/src/components/RightPanelTabs.keyboard.test.tsx index e7a952ba43c5..5502f9cb8b87 100644 --- a/apps/web/src/components/RightPanelTabs.keyboard.test.tsx +++ b/apps/web/src/components/RightPanelTabs.keyboard.test.tsx @@ -194,11 +194,23 @@ describe("right panel new-tab shortcut", () => { expect((await press("t", { metaKey: true, isComposing: true })).defaultPrevented).toBe(false); const dialog = document.createElement("div"); dialog.dataset.slot = "dialog-popup"; + dialog.dataset.open = ""; container.append(dialog); expect((await press("t", { metaKey: true })).defaultPrevented).toBe(false); expect(document.querySelector('[role="menu"]')).toBeNull(); }); + it("opens past a closed popup that stays mounted", async () => { + await renderPanel(); + const popover = document.createElement("div"); + popover.dataset.slot = "popover-popup"; + popover.dataset.closed = ""; + popover.hidden = true; + container.append(popover); + expect((await press("t", { metaKey: true })).defaultPrevented).toBe(true); + expect(document.querySelector('[role="menu"]')).not.toBeNull(); + }); + it("supports arrow keys with an existing tab in sheet mode and clears the menu on close", async () => { const panel = { mode: "sheet" as const, diff --git a/apps/web/src/components/RightPanelTabs.tsx b/apps/web/src/components/RightPanelTabs.tsx index 6869dd7d1ec2..f2d866b26a9f 100644 --- a/apps/web/src/components/RightPanelTabs.tsx +++ b/apps/web/src/components/RightPanelTabs.tsx @@ -165,16 +165,17 @@ const SURFACE_DISABLED_REASONS = { device: "Devices are only available from a thread.", } as const; -/** Overlays that must win over the launcher's letter shortcuts. */ +/** Overlays that must win over the launcher's letter shortcuts. Popups match only while open or + closing: some stay mounted when closed, such as the pull request composer. */ const LAUNCHER_SHORTCUT_BLOCKING_LAYERS = [ - '[data-slot="dialog-popup"]', - '[data-slot="alert-dialog-popup"]', - '[data-slot="command-dialog-popup"]', - '[data-slot="menu-popup"]', - '[data-slot="select-popup"]', - '[data-slot="popover-popup"]', - '[data-slot="combobox-popup"]', - '[data-slot="autocomplete-popup"]', + '[data-slot="dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="alert-dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="command-dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="menu-popup"]:is([data-open],[data-ending-style])', + '[data-slot="select-popup"]:is([data-open],[data-ending-style])', + '[data-slot="popover-popup"]:is([data-open],[data-ending-style])', + '[data-slot="combobox-popup"]:is([data-open],[data-ending-style])', + '[data-slot="autocomplete-popup"]:is([data-open],[data-ending-style])', ].join(","); /** One-line unavailability hints for the empty-state rows. */ From 956c0f4a0fdb00c82f982737a2e91480a3d65cf5 Mon Sep 17 00:00:00 2001 From: Riccardo Palleschi Date: Sun, 11 Oct 2026 13:09:32 +0200 Subject: [PATCH 2/3] test(web): keep Mod+T blocked while a popup is closing --- .../src/components/RightPanelTabs.keyboard.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/web/src/components/RightPanelTabs.keyboard.test.tsx b/apps/web/src/components/RightPanelTabs.keyboard.test.tsx index 5502f9cb8b87..c58c37d2d3e9 100644 --- a/apps/web/src/components/RightPanelTabs.keyboard.test.tsx +++ b/apps/web/src/components/RightPanelTabs.keyboard.test.tsx @@ -211,6 +211,16 @@ describe("right panel new-tab shortcut", () => { expect(document.querySelector('[role="menu"]')).not.toBeNull(); }); + it("does not open over a popup while it is closing", async () => { + await renderPanel(); + const popover = document.createElement("div"); + popover.dataset.slot = "popover-popup"; + popover.dataset.endingStyle = ""; + container.append(popover); + expect((await press("t", { metaKey: true })).defaultPrevented).toBe(false); + expect(document.querySelector('[role="menu"]')).toBeNull(); + }); + it("supports arrow keys with an existing tab in sheet mode and clears the menu on close", async () => { const panel = { mode: "sheet" as const, From 72af41829e231a7b249e50cb43988cf089eb66ff Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 19 Oct 2026 07:27:39 +0000 Subject: [PATCH 3/3] fix(web): ignore closed popups for panel shortcuts --- .../RightPanelTabs.keyboard.browser.test.tsx | 30 ++++++++++++------- apps/web/src/components/RightPanelTabs.tsx | 17 ++++++----- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/web/src/components/RightPanelTabs.keyboard.browser.test.tsx b/apps/web/src/components/RightPanelTabs.keyboard.browser.test.tsx index 9adb5f83b4cd..11f70037eafc 100644 --- a/apps/web/src/components/RightPanelTabs.keyboard.browser.test.tsx +++ b/apps/web/src/components/RightPanelTabs.keyboard.browser.test.tsx @@ -202,25 +202,33 @@ describe("right panel new-tab shortcut", () => { expect(document.querySelector('[role="menu"]')).toBeNull(); }); - it("opens past a closed popup that stays mounted", async () => { + it("blocks panel shortcuts until a mounted popup finishes closing", async () => { await renderPanel(); const popover = document.createElement("div"); popover.dataset.slot = "popover-popup"; - popover.dataset.closed = ""; - popover.hidden = true; + popover.dataset.open = ""; container.append(popover); - expect((await press("t", { metaKey: true })).defaultPrevented).toBe(true); - expect(document.querySelector('[role="menu"]')).not.toBeNull(); - }); - it("does not open over a popup while it is closing", async () => { - await renderPanel(); - const popover = document.createElement("div"); - popover.dataset.slot = "popover-popup"; + expect((await press("t", { metaKey: true })).defaultPrevented).toBe(false); + expect((await press("f")).defaultPrevented).toBe(false); + expect(document.querySelector('[role="menu"]')).toBeNull(); + expect(addFiles).not.toHaveBeenCalled(); + + delete popover.dataset.open; + popover.dataset.closed = ""; popover.dataset.endingStyle = ""; - container.append(popover); expect((await press("t", { metaKey: true })).defaultPrevented).toBe(false); + expect((await press("f")).defaultPrevented).toBe(false); expect(document.querySelector('[role="menu"]')).toBeNull(); + expect(addFiles).not.toHaveBeenCalled(); + + delete popover.dataset.endingStyle; + popover.hidden = true; + expect((await press("f")).defaultPrevented).toBe(true); + expect(addFiles).toHaveBeenCalledOnce(); + expect((await press("t", { metaKey: true })).defaultPrevented).toBe(true); + expect(document.querySelector('[role="menu"]')?.textContent).toContain("Files"); + expect(document.activeElement?.closest('[role="menu"]')).not.toBeNull(); }); it("supports arrow keys with an existing tab in sheet mode and clears the menu on close", async () => { diff --git a/apps/web/src/components/RightPanelTabs.tsx b/apps/web/src/components/RightPanelTabs.tsx index c44de7b9bcbd..6578080aff1e 100644 --- a/apps/web/src/components/RightPanelTabs.tsx +++ b/apps/web/src/components/RightPanelTabs.tsx @@ -174,15 +174,16 @@ const SURFACE_DISABLED_REASONS = { device: "Devices are only available from a thread.", } as const; +// Closed popups can stay mounted, such as the pull request composer. const LAUNCHER_SHORTCUT_BLOCKING_LAYERS = [ - '[data-slot="dialog-popup"]', - '[data-slot="alert-dialog-popup"]', - '[data-slot="command-dialog-popup"]', - '[data-slot="menu-popup"]', - '[data-slot="select-popup"]', - '[data-slot="popover-popup"]', - '[data-slot="combobox-popup"]', - '[data-slot="autocomplete-popup"]', + '[data-slot="dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="alert-dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="command-dialog-popup"]:is([data-open],[data-ending-style])', + '[data-slot="menu-popup"]:is([data-open],[data-ending-style])', + '[data-slot="select-popup"]:is([data-open],[data-ending-style])', + '[data-slot="popover-popup"]:is([data-open],[data-ending-style])', + '[data-slot="combobox-popup"]:is([data-open],[data-ending-style])', + '[data-slot="autocomplete-popup"]:is([data-open],[data-ending-style])', ].join(","); const SURFACE_UNAVAILABLE_HINTS = {