jitsi/jitsi-meet · issues #17374 + #17373 · PR #17439 · branch fix/popover-esc-after-hover
An external accessibility audit filed six related bugs against meet.jit.si on the same day. Two of them, Esc doesn't close the reactions menu after hover and Esc doesn't close the three-dot menu after hover, share one structural root cause: the Popover component declares an Esc handler but attaches it only to the inner .popover-content div, which renders inside a DialogPortal. For hover-triggered popovers focus stays on the trigger, the Esc keydown never reaches the content handler, and the menu can't be closed by keyboard. Three-line fix moves the handler to the outer container.
The reactions button and the three-dot overflow button each open a popover. On desktop the reactions popover opens on hover (default trigger in ToolboxButtonWithPopup), the overflow popover opens on click. In both cases, focus stays on the trigger element after open, for hover that's by design, for click it's because focus-lock activation goes through a 100 ms resize-observer dance and the user can press Esc before it kicks in.
The component had an Esc handler all along, _onEscKey, but it was attached to the .popover-content div, which gets mounted into a separate DialogPortal subtree. The trigger lives in the outer container; the content lives in the portal. Esc bubbles up from the trigger and never enters the portal's DOM, so the handler simply never fires for keyboard users.
Focus on the trigger button → Esc bubbles through the outer container → no handler → event dies. The accessibility audit caught it cleanly: "Contents that open on hover, should be closable without moving focus (e.g. by pressing Esc). This is currently not the case for this menu."
Three lines change in react/features/base/popover/components/Popover.web.tsx: attach _onEscKey to the outer container so Esc bubbles up from the trigger and closes the popover; tighten the visibility guard so preventDefault/stopPropagation only runs when the popover is actually visible, so the handler doesn't swallow Esc for unrelated handlers when the popover is closed.
Same number of statements, just reordered so the visibility check gates the side-effecting calls instead of guarding only _onHideDialog. The outer onKeyDown on the container catches Esc whether focus is on the trigger, on the container itself (it has tabIndex=0 when trigger is hover + focusable), or anywhere inside the popover content.
The same audit also filed #17368 (participants pane focus on open), #17367 + #17365 (focus into menu on open), and #17366 (focus return after closing settings). Those need work in different components, ParticipantsPane.tsx, the react-focus-on activation timing inside the popover, and BaseDialog's isElementInTheViewport guard which returns false when the toolbox auto-hides. Bundling them into this PR would conflate four root causes; the PR body lists them explicitly and offers follow-up patches.
PR open against master, GitHub-noreply identity from the start (CLA-friendly), tsc --noEmit -p tsconfig.web.json + eslint Popover.web.tsx both clean. Two issues explicitly Closes-tagged, four cluster siblings cross-linked.
Read all six audit reports, walked OverflowMenuButton.tsx → ToolboxButtonWithPopup.tsx → Popover.web.tsx → DialogPortal.ts → BaseDialog.tsx → ParticipantsPane.tsx. Identified that #17374 + #17373 share one root cause (Popover Esc-handler location), and the other four need separate fixes in separate components. Scoped the PR honestly.
Three-line change in Popover.web.tsx. TypeScript clean on the full tsconfig.web.json project, ESLint clean on the patched file, no behaviour change for the click-trigger popovers where Esc already worked.
Two Closes # lines, the other four issues listed as same-cluster-different-root-cause with offer to follow up. Reporter (@emrahcom) cross-linked from the patched issues so they see the fix without having to find it on the PR list.
External-fork CI on jitsi-meet requires a maintainer to approve the workflow run before the lint + build pipelines fire for real. Area-owner candidates from git log -- <file>: raduanastase8x8 (most recent WCAG commit), Emmanuel Pelletier (author of #12969 "Globally improve accessibility for screen reader users"), Andrei Gavrilescu (latest Popover touch-interaction fix). Typical jitsi-meet turnaround on small a11y PRs: 3-10 days.
Merge odds on this PR: ~70% given the +3/-4 footprint, the precise root-cause analysis, the cluster-scoping discipline, and the company-funded audit framing the reports. If this lands, the four follow-up issues become a natural conversation opener, each is a self-contained fix demonstrating the same approach.
No unit tests added, jitsi-meet's test suite is end-to-end (wdio + a running Prosody/Jicofo/JVB stack), not unit-level, so manual verification on a deployment is the project's idiomatic test plan for UI patches of this size.