Skip to content

Commit 1a5cd8b

Browse files
authored
fix(app): give focus back when ⌘W closes the side panel's last New tab (#60435)
1 parent 8f11d59 commit 1a5cd8b

3 files changed

Lines changed: 27 additions & 15 deletions

File tree

‎packages/app/e2e/regression/tabs.spec.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ test("⌘W closes the session tab again once the window loses focus after the si
12351235
await expect(sessions.alphaTab).toHaveCount(0)
12361236
})
12371237

1238-
test("⌘W in the side panel closes its New tab, then the panel, and only then the session tab", async ({ page }) => {
1238+
test("⌘W in the side panel closes its last New tab with the panel, and only then the session tab", async ({ page }) => {
12391239
await page.setViewportSize({ width: 1440, height: 900 })
12401240
const alpha = { id: "ses_side_new_alpha", title: "Side new alpha" }
12411241
const beta = { id: "ses_side_new_beta", title: "Side new beta" }
@@ -1254,14 +1254,14 @@ test("⌘W in the side panel closes its New tab, then the panel, and only then t
12541254
// A launcher row in the New tab view holds focus as ⌘W removes it.
12551255
await panel.getByRole("button", { name: /^Open file/ }).focus()
12561256

1257+
// The New tab was the panel's last tab, so closing it closes the panel too, and focus goes back.
12571258
await page.keyboard.press("ControlOrMeta+w")
12581259
await expect(newTab).toHaveCount(0)
1259-
await expect(toggle).toHaveAttribute("aria-expanded", "true")
1260-
1261-
// Nothing is left to close in the panel, so ⌘W closes the panel rather than the session, and focus goes back.
1262-
await page.keyboard.press("ControlOrMeta+w")
12631260
await expect(toggle).toHaveAttribute("aria-expanded", "false")
12641261
await expect(composer).toBeFocused()
1262+
1263+
// One press too many closes nothing rather than the session.
1264+
await page.keyboard.press("ControlOrMeta+w")
12651265
await expect(alphaTab).toHaveCount(1)
12661266
await expectPath(page, sessionHref(alpha.id))
12671267

‎packages/app/src/runtime/plugin/panels.tsx‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,27 @@ export function createRegion(input: {
340340

341341
const empty = createMemo(() => strip().length === 0)
342342

343+
// Focus leaves before the close starts, back to where it was; otherwise the panel hands it to its toggle.
344+
const giveBackFocus = () => {
345+
const inside = !!document.getElementById("review-panel")?.contains(document.activeElement)
346+
const back = returnFocus
347+
returnFocus = undefined
348+
349+
if (inside && back?.isConnected) back.focus({ preventScroll: true })
350+
}
351+
352+
// Emptying the region closes it, so focus goes back as the user's own close would give it.
353+
const emptied = () => {
354+
giveBackFocus()
355+
input.onEmptied?.()
356+
}
357+
343358
// Closing the last New tab of a strip with no tabs closes the region, as closing its last tab does.
344359
const dismissNewTab = (id: string) =>
345360
batch(() => {
346361
setState("adding", (list) => list.filter((value) => value.id !== id))
347362

348-
if (empty() && !state.adding.some(live)) input.onEmptied?.()
363+
if (empty() && !state.adding.some(live)) emptied()
349364
})
350365

351366
return {
@@ -409,12 +424,7 @@ export function createRegion(input: {
409424
const focused = document.activeElement
410425

411426
if (side.opened()) {
412-
const inside = !!document.getElementById("review-panel")?.contains(focused)
413-
const back = returnFocus
414-
returnFocus = undefined
415-
416-
// Focus leaves before the close starts, back to where it was; otherwise the panel hands it to its toggle.
417-
if (inside && back?.isConnected) back.focus({ preventScroll: true })
427+
giveBackFocus()
418428
side.toggle()
419429

420430
return
@@ -444,7 +454,7 @@ export function createRegion(input: {
444454
batch(() => {
445455
input.tabs().close(key)
446456

447-
if (last) input.onEmptied?.()
457+
if (last) emptied()
448458
})
449459

450460
if (entry)

‎packages/app/src/session/commands/use-session-commands.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,10 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
241241
if (actions.region.closeNewTab(focusedPane())) {
242242
const panel = document.getElementById("review-panel")
243243

244-
// A launcher row that held focus is gone; focus stays in the panel so closing it next returns focus.
245-
if (panel && !panel.contains(document.activeElement)) panel.focus({ preventScroll: true, focusVisible: false })
244+
// A launcher row that held focus is gone; focus stays in the panel so closing it next returns focus. Closing the
245+
// strip's last New tab closes the panel too, which already gave focus back.
246+
if (panel && actions.session.layout.view().side.opened() && !panel.contains(document.activeElement))
247+
panel.focus({ preventScroll: true, focusVisible: false })
246248

247249
return
248250
}

0 commit comments

Comments
 (0)