Skip to content

feat: manage focus for accessible click/context popups#613

Open
claytonlin1110 wants to merge 2 commits intoreact-component:masterfrom
claytonlin1110:feat/popup-focus-a11y
Open

feat: manage focus for accessible click/context popups#613
claytonlin1110 wants to merge 2 commits intoreact-component:masterfrom
claytonlin1110:feat/popup-focus-a11y

Conversation

@claytonlin1110
Copy link
Copy Markdown

@claytonlin1110 claytonlin1110 commented Apr 3, 2026

Summary

Implements focus behavior for floating popups as discussed for Ant Design Popover/Popconfirm-style usage (ant-design#57333): move focus into content on open, return it to the trigger on close, and keep Tab navigation inside the layer while open. Escape behavior is unchanged (existing Portal onEsc).

Default behavior

  • On when open is driven only by click, contextMenu, and/or focus (no hover in show actions).
  • Off when hover is a show action (including ['hover', 'click']), so hover tooltips do not steal focus.
  • focusPopup prop overrides the default explicitly.

Summary by CodeRabbit

  • 新功能
    • 弹出窗口现已支持可配置的自动焦点管理:打开时将焦点移至弹出内容,关闭时恢复到触发元素(默认在点击/聚焦触发时启用,悬停时禁用)。
    • Tab 键导航在弹出窗口内循环,防止焦点移出弹窗范围。
  • 测试
    • 新增覆盖弹窗聚焦、还原焦点与 Tab 抑制行为的自动化测试用例。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 3, 2026

Walkthrough

在 Trigger/Popup 链路中新增可选的焦点管理功能 focusPopup,包含聚焦弹窗根节点或首个可聚焦子元素、在关闭时恢复触发器焦点以及 Tab 键捕获行为的实现与测试覆盖。

Changes

Cohort / File(s) Summary
焦点工具
src/focusUtils.ts
新增焦点辅助模块:计算首/末可聚焦元素、聚焦弹窗根或第一个可聚焦子元素、以及处理 Tab 键环状捕获(export 三个函数)。
Popup 组件
src/Popup/index.tsx
新增 focusPopup?: boolean prop,加入 rootRef、先前 open 状态跟踪、useLayoutEffect 聚焦/恢复逻辑及 onKeyDownCapture 调用 Tab 捕获;调整早期 !show 返回位置以保证钩子执行。
UniqueProvider & 上下文
src/UniqueProvider/index.tsx, src/context.ts
UniqueShowOptions 加入 focusPopup?: boolean,并将 mergedOptions.focusPopup 传递到渲染的 Popup
Trigger 接口与合并逻辑
src/index.tsx
在导出 TriggerProps 添加 focusPopup?: boolean,计算 mergedFocusPopup(基于 show action 类型的默认规则),将其注入 UniqueProvider/Popup,移除重复的 show/hide action 重新计算。
测试
tests/focus.test.tsx
新增触点焦点管理测试套件(5 个用例):自动聚焦、hover 时不自动聚焦、关闭后恢复焦点、Tab 捕获、禁用 focusPopup 行为。

Sequence Diagram(s)

sequenceDiagram
  participant Trigger
  participant UniqueProvider
  participant Popup
  participant Document
  Trigger->>UniqueProvider: open request (with focusPopup)
  UniqueProvider->>Popup: render with focusPopup prop
  Popup->>Popup: mount rootRef, track prevOpen
  Popup->>Document: useLayoutEffect on open -> focusPopupRootOrFirst(root)
  Note right of Popup: onKeyDownCapture -> handlePopupTabTrap
  Popup->>Document: on close -> if active inside popup && target connected -> focus target
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 一只小兔蹦蹦跳,
弹窗里头亮又俏,
Tab 键兜转不乱跑,
关上窗来回家找,
焦点安居暖洋洋 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰地总结了主要改动——为可访问的点击/上下文菜单弹窗添加焦点管理功能,与提交的所有文件变更高度相关。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claytonlin1110
Copy link
Copy Markdown
Author

@meet-student Please review

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/focusUtils.ts`:
- Line 13: The instanceof check in isTabbable should use the frame's globals
rather than the top-level ones: change references from the global
HTMLInputElement to win.HTMLInputElement (where win is the defaultView passed
into isTabbable) so cross-realm inputs are detected correctly, and replace uses
of document.activeElement with container.ownerDocument.activeElement (or
win.document.activeElement) so activeElement is read from the same document as
the container; update the checks in the isTabbable function and any other
focus-related helpers that use global DOM constructors or document.activeElement
to use the passed-in win/container.ownerDocument to ensure correct behavior
across iframes.

In `@src/Popup/index.tsx`:
- Around line 255-266: The blur/refocus check uses the global
document.activeElement which breaks across documents (iframes/Shadow DOM);
update the logic in Popup's close branch (the block using root, target and
calling target.focus()—function/variables: focusPopupRootOrFirst, root, target)
to read active via root.ownerDocument.activeElement instead of
document.activeElement; apply the same change inside src/focusUtils.ts in
handlePopupTabTrap (replace document.activeElement with
root.ownerDocument.activeElement or the appropriate root/doc reference used
there) so cross-document focus containment checks (root.contains(active)) work
correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a5c9b77a-5ef2-40d3-acca-4d3662621910

📥 Commits

Reviewing files that changed from the base of the PR and between 59b659d and 65eee0b.

📒 Files selected for processing (6)
  • src/Popup/index.tsx
  • src/UniqueProvider/index.tsx
  • src/context.ts
  • src/focusUtils.ts
  • src/index.tsx
  • tests/focus.test.tsx

if ('disabled' in el && (el as HTMLButtonElement).disabled) {
return false;
}
if (el instanceof HTMLInputElement && el.type === 'hidden') {
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 只读核验:定位跨 realm 敏感写法
rg -n "instanceof HTMLInputElement|document\.activeElement" src/focusUtils.ts src/Popup/index.tsx

Repository: react-component/trigger

Length of output: 326


🏁 Script executed:

cat -n src/focusUtils.ts | head -80

Repository: react-component/trigger

Length of output: 2681


🏁 Script executed:

cat -n src/Popup/index.tsx | sed -n '250,270p'

Repository: react-component/trigger

Length of output: 762


🏁 Script executed:

# Check if there are any tests related to focusUtils
fd -e test -e spec | xargs grep -l "focusUtils\|isTabbable\|handlePopupTabTrap" 2>/dev/null | head -5

Repository: react-component/trigger

Length of output: 49


🏁 Script executed:

# Check for any tests or documentation about iframe/cross-realm support
fd -type f \( -name "*.test.*" -o -name "*.spec.*" -o -name "README*" \) | xargs grep -l "iframe\|cross.realm\|cross.document" 2>/dev/null

Repository: react-component/trigger

Length of output: 239


🏁 Script executed:

# Check git history or issues directory for cross-realm discussions
find . -name "*.md" -o -name "*.txt" | xargs grep -i "iframe\|cross.realm\|cross.document\|cross.window" 2>/dev/null | head -20

Repository: react-component/trigger

Length of output: 49


🏁 Script executed:

# Check if there are existing focus-related tests
fd -type f \( -name "*.test.*" -o -name "*.spec.*" \) | xargs grep -l "focus\|tabbable\|focusUtils" 2>/dev/null

Repository: react-component/trigger

Length of output: 239


🏁 Script executed:

# Find test files with correct syntax
fd "\.test\.|\.spec\." | head -20

Repository: react-component/trigger

Length of output: 465


🏁 Script executed:

# Search for focus-related tests
rg -l "focus|tabbable|focusUtils" --type ts --type tsx --type js --type jsx 2>/dev/null | grep -i "test\|spec"

Repository: react-component/trigger

Length of output: 49


🏁 Script executed:

# Check if container.contains works cross-document
rg -n "contains" src/focusUtils.ts

Repository: react-component/trigger

Length of output: 117


🏁 Script executed:

# Look for any cross-document or shadow DOM concerns in the codebase
rg -i "ownerDocument|defaultView|cross.document|shadow" src/ | head -20

Repository: react-component/trigger

Length of output: 1286


🏁 Script executed:

cat tests/focus.test.tsx

Repository: react-component/trigger

Length of output: 5444


🏁 Script executed:

# Check what win parameter is used for in isTabbable
rg -A 5 "function isTabbable" src/focusUtils.ts

Repository: react-component/trigger

Length of output: 275


跨 realm(iframe)下的 instanceof 判定与 activeElement 读取需修正。

Line 13 使用全局 HTMLInputElement 进行 instanceof 判定,但 isTabbable 已接收 win 参数(来自 container.ownerDocument.defaultView);当容器在不同 document 中时判定失效。
Line 66 使用全局 document.activeElement 而应使用 container.ownerDocument.activeElement,虽然 container.contains() 提供了部分保护但不是完整解决方案。

建议修正:

修复方案
 function isTabbable(el: HTMLElement, win: Window): boolean {
-  if (el instanceof HTMLInputElement && el.type === 'hidden') {
+  if (el instanceof win.HTMLInputElement && el.type === 'hidden') {
     return false;
   }

 export function handlePopupTabTrap(
   e: React.KeyboardEvent,
   container: HTMLElement,
 ): void {
-  const active = document.activeElement as HTMLElement | null;
+  const active = container.ownerDocument.activeElement as HTMLElement | null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (el instanceof HTMLInputElement && el.type === 'hidden') {
function isTabbable(el: HTMLElement, win: Window): boolean {
if (el instanceof win.HTMLInputElement && el.type === 'hidden') {
return false;
}
// ... rest of function
}
export function handlePopupTabTrap(
e: React.KeyboardEvent,
container: HTMLElement,
): void {
const active = container.ownerDocument.activeElement as HTMLElement | null;
// ... rest of function
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/focusUtils.ts` at line 13, The instanceof check in isTabbable should use
the frame's globals rather than the top-level ones: change references from the
global HTMLInputElement to win.HTMLInputElement (where win is the defaultView
passed into isTabbable) so cross-realm inputs are detected correctly, and
replace uses of document.activeElement with
container.ownerDocument.activeElement (or win.document.activeElement) so
activeElement is read from the same document as the container; update the checks
in the isTabbable function and any other focus-related helpers that use global
DOM constructors or document.activeElement to use the passed-in
win/container.ownerDocument to ensure correct behavior across iframes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check seems unnecessary: hidden input are, well, hidden, and therefore do not receive focus from the browser.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KittyGiraudel, you're right — <input type="hidden"> elements are non-focusable per the HTML spec, and calling .focus() on them is a no-op in all browsers. That check is redundant and can be safely removed.

That also makes the cross-realm instanceof win.HTMLInputElement concern I raised for line 13 moot, since the fix is simply to delete the check entirely rather than correct it. The separate concern about using container.ownerDocument.activeElement instead of document.activeElement on line 66 is independent and still applies, but the hidden-input guard can go.

Comment on lines +255 to +266
if (open && !wasOpen && root && isNodeVisible) {
focusPopupRootOrFirst(root);
} else if (!open && wasOpen && root) {
const active = document.activeElement as HTMLElement | null;
if (
target &&
active &&
(root === active || root.contains(active))
) {
if (target.isConnected) {
target.focus();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 只读核验:定位当前实现中 activeElement 的 document 作用域
rg -n "activeElement" src/Popup/index.tsx src/focusUtils.ts

Repository: react-component/trigger

Length of output: 241


🏁 Script executed:

# 查看src/Popup/index.tsx第255-270行的完整上下文
sed -n '255,270p' src/Popup/index.tsx

Repository: react-component/trigger

Length of output: 506


🏁 Script executed:

# 查看src/focusUtils.ts中activeElement的完整上下文
sed -n '60,75p' src/focusUtils.ts

Repository: react-component/trigger

Length of output: 407


🏁 Script executed:

# 检查root对象的声明和初始化,确认它是否总是可用
sed -n '1,260p' src/Popup/index.tsx | grep -n "root" | tail -20

Repository: react-component/trigger

Length of output: 305


🏁 Script executed:

# 搜索与iframe或跨document相关的测试或文档
rg -i "iframe|cross.document|ownerDocument" --type ts --type tsx

Repository: react-component/trigger

Length of output: 94


🏁 Script executed:

# 检查是否有其他地方也使用了document.activeElement且未使用ownerDocument
rg -n "document\.activeElement" --type ts --type tsx

Repository: react-component/trigger

Length of output: 94


🏁 Script executed:

# 查看focusUtils.ts的完整容器相关上下文
sed -n '50,85p' src/focusUtils.ts

Repository: react-component/trigger

Length of output: 834


🏁 Script executed:

# 查看src/Popup/index.tsx更多上下文,理解root的生命周期
sed -n '240,275p' src/Popup/index.tsx

Repository: react-component/trigger

Length of output: 927


🏁 Script executed:

# 搜索ownerDocument的现有使用
rg "ownerDocument" --type ts --type javascript

Repository: react-component/trigger

Length of output: 101


🏁 Script executed:

# 搜索与焦点相关的测试文件
fd -e "test" -e "spec"

Repository: react-component/trigger

Length of output: 49


🏁 Script executed:

# 检查是否有iframe相关的使用场景
rg -i "iframe" .

Repository: react-component/trigger

Length of output: 49


🏁 Script executed:

# 验证HTMLElement是否有ownerDocument属性(应该有)
rg "ownerDocument" --type ts --type tsx -B 3 -A 3

Repository: react-component/trigger

Length of output: 94


🏁 Script executed:

# 查看focusUtils.ts中handlePopupTabTrap的完整函数签名和使用
rg -B 5 -A 15 "function handlePopupTabTrap"

Repository: react-component/trigger

Length of output: 857


🏁 Script executed:

# 检查root对象的类型定义
sed -n '166,170p' src/Popup/index.tsx

Repository: react-component/trigger

Length of output: 235


🏁 Script executed:

# 搜索where handlePopupTabTrap被调用
rg "handlePopupTabTrap" --type ts --type tsx

Repository: react-component/trigger

Length of output: 94


🏁 Script executed:

# 检查是否有任何关于focus管理或文档隔离的注释或文档
rg -i "focus|document" README.md CHANGELOG.md 2>/dev/null | head -30

Repository: react-component/trigger

Length of output: 385


🏁 Script executed:

# 确认focusUtils.ts的同一问题,获取完整上下文
sed -n '55,75p' src/focusUtils.ts

Repository: react-component/trigger

Length of output: 498


🏁 Script executed:

# 验证root.contains是否跨document兼容
rg -B 5 -A 5 "root.contains"

Repository: react-component/trigger

Length of output: 582


跨 document 场景下回焦判定失效

Line 258 使用全局 document.activeElement,在 iframe、Shadow DOM 等跨 document 容器中,该元素属于主文档而非 popup 所在文档,导致 root.contains(active) 判定失败,回焦功能无法生效。

建议改为 root.ownerDocument.activeElement,同一问题也存在于 src/focusUtils.ts:66handlePopupTabTrap 函数中。

🔧 修复建议

src/Popup/index.tsx

-      const active = document.activeElement as HTMLElement | null;
+      const active = root.ownerDocument.activeElement as HTMLElement | null;

src/focusUtils.ts

-  const active = document.activeElement as HTMLElement | null;
+  const active = container.ownerDocument.activeElement as HTMLElement | null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Popup/index.tsx` around lines 255 - 266, The blur/refocus check uses the
global document.activeElement which breaks across documents (iframes/Shadow
DOM); update the logic in Popup's close branch (the block using root, target and
calling target.focus()—function/variables: focusPopupRootOrFirst, root, target)
to read active via root.ownerDocument.activeElement instead of
document.activeElement; apply the same change inside src/focusUtils.ts in
handlePopupTabTrap (replace document.activeElement with
root.ownerDocument.activeElement or the appropriate root/doc reference used
there) so cross-document focus containment checks (root.contains(active)) work
correctly.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces focus management and tab trapping for popups, adding a new focusPopup prop to the Trigger component and a dedicated focusUtils.ts utility file. The implementation automatically handles moving focus into the popup upon opening and restoring it to the trigger upon closing, with default activation for click, context menu, and focus triggers. The review feedback suggests several improvements to the accessibility utilities, including expanding the selector for tabbable elements, using the :disabled pseudo-class to account for disabled fieldsets, and refining visibility checks to detect elements hidden by their ancestors.

import type * as React from 'react';

const TABBABLE_SELECTOR =
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The TABBABLE_SELECTOR is missing some naturally tabbable elements such as summary, [contenteditable], and media elements with controls (audio[controls], video[controls]). Consider expanding the selector for better accessibility coverage.

const TABBABLE_SELECTOR =
  'a[href], button, input, select, textarea, summary, [contenteditable], audio[controls], video[controls], [tabindex]:not([tabindex="-1"])';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even that list still misses “tabbable” elements. See https://github.com/KittyGiraudel/focusable-selectors (which is still not exhaustive, but better than nothing).

if (el.closest('[aria-hidden="true"]')) {
return false;
}
if ('disabled' in el && (el as HTMLButtonElement).disabled) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking for the disabled property on the element itself does not account for elements disabled via a parent <fieldset disabled>. Using el.matches(':disabled') is a more robust way to check if an element is effectively disabled in the DOM.

Suggested change
if ('disabled' in el && (el as HTMLButtonElement).disabled) {
if (el.matches(':disabled')) {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing that check in JavaScript, you can shift that to the selectors themselves in the TABBABLE_SELECTOR constant.

return false;
}
const style = win.getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Checking display and visibility on the element itself via getComputedStyle does not detect if the element is hidden because one of its ancestors has display: none. A more reliable check for layout visibility is el.getClientRects().length > 0 or checking el.offsetParent === null (though the latter has edge cases for fixed elements).

Suggested change
if (style.display === 'none' || style.visibility === 'hidden') {
if (el.getClientRects().length === 0) {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggestion from Gemini makes sense. You want to check for the element’s offset dimensions and bounding rects instead of every possible way of manually hiding an element with CSS.

function isVisible(element) {
  return Boolean(
    element.offsetWidth ||
      element.offsetHeight ||
      element.getClientRects().length
  )
}

@claytonlin1110
Copy link
Copy Markdown
Author

@meet-student had a chance to review?

@claytonlin1110
Copy link
Copy Markdown
Author

@afc163 @meet-student please review

Comment on lines +16 to +19
const ti = el.getAttribute('tabindex');
if (ti !== null && Number(ti) < 0) {
return false;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can be moved into the selectors themselves to avoid having to do it in JavaScript.

}

/** Visible, tabbable descendants inside `container` (in DOM order). */
export function getTabbableElements(container: HTMLElement): HTMLElement[] {
Copy link
Copy Markdown

@KittyGiraudel KittyGiraudel Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s worth keeping in mind, we do not actually need to list all the tabbable elements. We just need the 2 tabbable edges: the first one, and the last one — anything in between is completely unused. This is something I’ve solved for a11y-dialog (including accounting for Shadow DOM) if you’re interested:

https://github.com/KittyGiraudel/a11y-dialog/blob/4674ff3e4d626430a028a64969328e339c533ce8/src/dom-utils.ts#L189-L214

Comment on lines +258 to +263
const active = document.activeElement as HTMLElement | null;
if (
target &&
active &&
(root === active || root.contains(active))
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we check whether we have an active element before moving the focus to the target (which I assume is the previously focused element prior opening the popover)? I think the code can be simplified into: target?.focus(). Unless I’m missing something.

@claytonlin1110
Copy link
Copy Markdown
Author

@KittyGiraudel Thanks for your review, I just updated PR. Please review again

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/focusUtils.ts (1)

154-154: ⚠️ Potential issue | 🟠 Major

跨 document 场景下 activeElement 读取对象不一致。

Line 154 使用全局 document.activeElement,在 iframe/多 document 场景会导致焦点判定偏差,进而让 Tab trap 失效。建议改为 container.ownerDocument.activeElement

🔧 建议修改
-  const active = document.activeElement as HTMLElement | null;
+  const active = container.ownerDocument.activeElement as HTMLElement | null;
#!/bin/bash
# 只读核验:确认是否仍在使用全局 activeElement
rg -n "document\.activeElement|ownerDocument\.activeElement" src/focusUtils.ts src/Popup/index.tsx -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/focusUtils.ts` at line 154, In focusUtils.ts the code reads the global
document.activeElement into the variable active which breaks in
iframe/multi-document contexts; update the logic in the function that defines
const active = document.activeElement as HTMLElement | null to use
container.ownerDocument.activeElement (falling back to container if
ownerDocument is missing) so focus checks and the Tab-trap use the container's
document; ensure you reference the same variable name (active) and preserve the
existing null typing/guards where functions like the Tab-trap rely on it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/focusUtils.ts`:
- Line 154: In focusUtils.ts the code reads the global document.activeElement
into the variable active which breaks in iframe/multi-document contexts; update
the logic in the function that defines const active = document.activeElement as
HTMLElement | null to use container.ownerDocument.activeElement (falling back to
container if ownerDocument is missing) so focus checks and the Tab-trap use the
container's document; ensure you reference the same variable name (active) and
preserve the existing null typing/guards where functions like the Tab-trap rely
on it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7752ad1-f9c5-411c-a739-25d838ca5a74

📥 Commits

Reviewing files that changed from the base of the PR and between 65eee0b and 041ff8c.

📒 Files selected for processing (2)
  • src/Popup/index.tsx
  • src/focusUtils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Popup/index.tsx

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 79.69925% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.10%. Comparing base (59b659d) to head (041ff8c).

Files with missing lines Patch % Lines
src/focusUtils.ts 74.22% 25 Missing ⚠️
src/Popup/index.tsx 96.29% 1 Missing ⚠️
src/index.tsx 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #613      +/-   ##
==========================================
- Coverage   97.26%   95.10%   -2.17%     
==========================================
  Files          17       18       +1     
  Lines         952     1082     +130     
  Branches      274      326      +52     
==========================================
+ Hits          926     1029     +103     
- Misses         26       53      +27     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants