From f9455db2a7a33ba840ccbbf6247765a53be2dbca Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:43:58 -0400 Subject: [PATCH] Stop closing menus from refocusing unrelated cards --- js/ui/components/VideoCard.js | 32 ++++-- tests/video-card-source-visibility.test.mjs | 111 ++++++++++++++++++++ 2 files changed, 137 insertions(+), 6 deletions(-) diff --git a/js/ui/components/VideoCard.js b/js/ui/components/VideoCard.js index a5c7ee9f..657221d3 100644 --- a/js/ui/components/VideoCard.js +++ b/js/ui/components/VideoCard.js @@ -226,8 +226,13 @@ export class VideoCard { closeMoreMenu(options = {}) { const restoreFocus = options?.restoreFocus !== false; + const trigger = this.moreMenuButton; + const wasExpanded = + typeof trigger?.getAttribute === "function" && + trigger.getAttribute("aria-expanded") === "true"; + const detail = { - trigger: this.moreMenuButton, + trigger, video: this.video, card: this, restoreFocus, @@ -247,9 +252,14 @@ export class VideoCard { } } - if (!handled && restoreFocus && this.moreMenuButton?.focus) { + if ( + !handled && + restoreFocus && + wasExpanded && + typeof trigger?.focus === "function" + ) { try { - this.moreMenuButton.focus(); + trigger.focus(); } catch (error) { /* noop */ } @@ -258,8 +268,13 @@ export class VideoCard { closeSettingsMenu(options = {}) { const restoreFocus = options?.restoreFocus !== false; + const trigger = this.settingsButton; + const wasExpanded = + typeof trigger?.getAttribute === "function" && + trigger.getAttribute("aria-expanded") === "true"; + const detail = { - trigger: this.settingsButton, + trigger, video: this.video, card: this, restoreFocus, @@ -279,9 +294,14 @@ export class VideoCard { } } - if (!handled && restoreFocus && this.settingsButton?.focus) { + if ( + !handled && + restoreFocus && + wasExpanded && + typeof trigger?.focus === "function" + ) { try { - this.settingsButton.focus(); + trigger.focus(); } catch (error) { /* noop */ } diff --git a/tests/video-card-source-visibility.test.mjs b/tests/video-card-source-visibility.test.mjs index 93e48610..e56020f4 100644 --- a/tests/video-card-source-visibility.test.mjs +++ b/tests/video-card-source-visibility.test.mjs @@ -139,3 +139,114 @@ test("VideoCard hides cards without playable sources until a healthy CDN update assert.equal(root.hidden, false, "a healthy CDN update should unhide the card"); assert.equal(root.dataset.sourceVisibility, "visible", "visibility dataset should reflect the restored state"); }); + +test("VideoCard.closeMoreMenu only restores focus when the trigger was expanded", (t) => { + const { document } = setupDom(t); + + const card = new VideoCard({ + document, + video: { + id: "event-focus", + title: "Focusable clip", + }, + formatters: { + formatTimeAgo: () => "just now", + }, + helpers: { + isMagnetSupported: () => true, + }, + }); + + const root = card.getRoot(); + document.body.appendChild(root); + + const trigger = card.moreMenuButton; + assert.ok(trigger, "the more menu trigger should exist"); + + let focusCalls = 0; + trigger.focus = () => { + focusCalls += 1; + }; + + card.onCloseMoreMenu = () => false; + + card.closeMoreMenu(); + assert.equal( + focusCalls, + 0, + "collapsed menus should not restore focus when closing", + ); + + trigger.setAttribute("aria-expanded", "true"); + card.closeMoreMenu(); + assert.equal( + focusCalls, + 1, + "expanded menus should restore focus back to the trigger", + ); + + trigger.setAttribute("aria-expanded", "true"); + card.closeMoreMenu({ restoreFocus: false }); + assert.equal( + focusCalls, + 1, + "restoreFocus=false should suppress focus restoration", + ); +}); + +test("VideoCard.closeSettingsMenu only restores focus when the trigger was expanded", (t) => { + const { document } = setupDom(t); + + const card = new VideoCard({ + document, + video: { + id: "event-settings", + title: "Editable clip", + }, + capabilities: { + canEdit: true, + }, + formatters: { + formatTimeAgo: () => "just now", + }, + helpers: { + isMagnetSupported: () => true, + }, + }); + + const root = card.getRoot(); + document.body.appendChild(root); + + const trigger = card.settingsButton; + assert.ok(trigger, "the settings trigger should exist when canEdit is true"); + + let focusCalls = 0; + trigger.focus = () => { + focusCalls += 1; + }; + + card.onCloseSettingsMenu = () => false; + + card.closeSettingsMenu(); + assert.equal( + focusCalls, + 0, + "collapsed settings menus should not restore focus when closing", + ); + + trigger.setAttribute("aria-expanded", "true"); + card.closeSettingsMenu(); + assert.equal( + focusCalls, + 1, + "expanded settings menus should restore focus back to the trigger", + ); + + trigger.setAttribute("aria-expanded", "true"); + card.closeSettingsMenu({ restoreFocus: false }); + assert.equal( + focusCalls, + 1, + "restoreFocus=false should suppress settings focus restoration", + ); +});