Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: try to fix createPortal close case #492

Merged
merged 3 commits into from
Nov 8, 2024
Merged

fix: try to fix createPortal close case #492

merged 3 commits into from
Nov 8, 2024

Conversation

zombieJ
Copy link
Member

@zombieJ zombieJ commented Nov 8, 2024

ref #434

Need test on antd since it may affected by animation.

Summary by CodeRabbit

  • 新功能
    • 新增文档文件“Portal”,提供导航和代码示例。
    • 引入新的React组件Test,实现弹出界面和事件处理。
  • 改进
    • Popup组件支持新的鼠标事件处理属性onMouseDownCapture
    • 更新TriggerProps接口,简化API使用,并提供弃用通知。
  • 测试
    • 增加对Trigger组件的测试用例,验证弹出行为。

Copy link

coderabbitai bot commented Nov 8, 2024

Walkthrough

此次更改引入了多个新文件和功能。首先,创建了一个新的文档文件 docs/demos/portal.md,其中包含导航元数据和指向外部 TypeScript 文件的代码片段。其次,新增了一个 React 组件 Test,该组件使用 rc-trigger 库实现弹出界面,并定义了多个弹出位置。最后,对现有的 PopupTrigger 组件进行了接口修改,以增强事件处理能力和清晰度。

Changes

文件路径 更改摘要
docs/demos/portal.md 新增文档文件,标题为“Portal”,导航路径为“/demo”。包含指向 ../examples/portal.tsx 的代码片段引用。
docs/examples/portal.tsx 新增 React 组件 TestPortalPopup,实现弹出界面,包含鼠标事件处理和按钮交互。
src/Popup/index.tsx PopupProps 接口中新增可选属性 onMouseDownCapture,用于捕获鼠标按下事件。
src/index.tsx 更新 TriggerProps 接口,移除 getDocument 属性,添加多个属性的弃用说明,并增强 generateTrigger 函数的事件处理。
tests/basic.test.jsx 修改测试套件,新增测试用例以验证 Trigger 组件在使用 portal 时的行为,并修正现有测试中的函数调用。

Possibly related PRs

Poem

在兔子洞中欢跳跃,
新文档如花绽放。
弹出窗口随风舞,
鼠标轻点乐无穷。
代码更新如春雨,
让我们一起欢庆吧! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Nov 8, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.73%. Comparing base (8abc4f9) to head (9030154).
Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #492   +/-   ##
=======================================
  Coverage   97.72%   97.73%           
=======================================
  Files          13       13           
  Lines         791      794    +3     
  Branches      238      239    +1     
=======================================
+ Hits          773      776    +3     
  Misses         18       18           

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

Copy link

@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: 3

🧹 Outside diff range and nitpick comments (3)
docs/examples/portal.tsx (2)

8-33: 建议重构内置位置配置以提高可维护性

建议将 builtinPlacements 配置重构为更易维护的形式。

可以考虑使用以下方式重构:

-const builtinPlacements = {
-  left: {
-    points: ['cr', 'cl'],
-  },
-  right: {
-    points: ['cl', 'cr'],
-  },
-  // ... more placements
-};
+const POINTS = {
+  LEFT: ['cr', 'cl'],
+  RIGHT: ['cl', 'cr'],
+  TOP: ['bc', 'tc'],
+  BOTTOM: ['tc', 'bc'],
+} as const;
+
+const builtinPlacements = {
+  left: { points: POINTS.LEFT },
+  right: { points: POINTS.RIGHT },
+  top: { points: POINTS.TOP },
+  bottom: { points: POINTS.BOTTOM },
+  topLeft: { points: ['bl', 'tl'] },
+  topRight: { points: ['br', 'tr'] },
+  bottomRight: { points: ['tr', 'br'] },
+  bottomLeft: { points: ['tl', 'bl'] },
+};

69-107: 建议优化组件结构和样式处理

当前实现存在以下问题:

  1. 内联样式可能影响性能
  2. 组件结构可以更加模块化

建议:

  1. 将样式抽离到单独的样式文件
  2. 将按钮组件抽离为独立的子组件
  3. 使用 CSS-in-JS 或 CSS Modules 替代内联样式

示例重构:

// styles.ts
export const containerStyle = {
  padding: 100,
  display: 'flex',
  flexDirection: 'column' as const,
  alignItems: 'flex-start',
  gap: 100,
};

// Button.tsx
const ActionButton = ({ onClick, children }) => (
  <button
    onClick={onClick}
    onMouseDown={(e) => {
      e.stopPropagation();
      e.preventDefault();
    }}
  >
    {children}
  </button>
);
src/Popup/index.tsx (1)

23-23: 建议添加属性文档注释

为了提高代码可维护性,建议为新增的 onMouseDownCapture 属性添加 JSDoc 注释,说明其用途和使用场景。

建议添加如下注释:

+  /** 
+   * 捕获阶段的鼠标按下事件处理器
+   * 用于处理 Portal 关闭时的特殊场景
+   */
  onMouseDownCapture?: React.MouseEventHandler<HTMLDivElement>;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8abc4f9 and 9030154.

📒 Files selected for processing (5)
  • docs/demos/portal.md (1 hunks)
  • docs/examples/portal.tsx (1 hunks)
  • src/Popup/index.tsx (3 hunks)
  • src/index.tsx (2 hunks)
  • tests/basic.test.jsx (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • docs/demos/portal.md
🔇 Additional comments (4)
src/Popup/index.tsx (1)

23-23: 实现看起来不错,建议验证 Portal 关闭场景!

新增的 onMouseDownCapture 事件处理实现清晰且符合 React 事件处理规范。这个改动应该是为了解决 Portal 关闭时的事件传播问题。

建议运行以下脚本来验证相关的 Portal 关闭用例:

Also applies to: 109-109, 260-260

✅ Verification successful

Let's gather more information about the test case and implementation details.


Based on the test cases and implementation details, I can now provide a final response:

onMouseDownCapture 实现正确且必要!

通过代码分析发现:

  • 该事件处理是为了解决 Portal 场景下的点击关闭问题
  • src/index.tsx 中的实现显示这是一个必要的补充,因为 createPortal 创建的元素不在 popup 元素内
  • 使用捕获阶段事件可以确保在事件冒泡之前正确处理点击行为

测试用例 tests/basic.test.jsx 中的 Portal 相关测试验证了这个行为是正确的,特别是在处理嵌套 Portal 和点击关闭场景时。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 描述:查找与 Portal 关闭相关的测试用例和实现

# 测试:搜索相关的测试文件
echo "查找相关测试用例:"
rg -l "createPortal.*close" --glob "**/*.test.{ts,tsx,js,jsx}"

# 测试:搜索 Portal 相关的实现
echo "查找 Portal 相关实现:"
rg "createPortal" --glob "**/*.{ts,tsx}"

Length of output: 681


Script:

#!/bin/bash
# 查看测试用例内容
echo "Portal 关闭测试用例内容:"
cat tests/basic.test.jsx

# 查看 Popup 组件中的 Portal 相关实现
echo -e "\nPopup Portal 实现:"
cat src/index.tsx

# 检查是否有其他使用 onMouseDownCapture 的地方
echo -e "\n其他 onMouseDownCapture 使用:"
rg "onMouseDownCapture" -A 3 --glob "**/*.{ts,tsx}"

Length of output: 57581

src/index.tsx (2)

384-386: 代码格式优化良好!

状态声明的格式调整提高了代码的可读性。


725-732: 事件处理逻辑合理,修复了Portal关闭问题

这个改动通过在捕获阶段处理鼠标按下事件,巧妙地解决了createPortal场景下的关闭时序问题。使用捕获阶段确保了事件能在Portal内部处理之前被正确捕获。

运行以下脚本验证相关的事件处理逻辑:

✅ Verification successful

Let me analyze the search results. I can see that:

  1. The onMouseDownCapture handler in the review is part of the Popup component
  2. There are related mouse event handlers in both test files and example files
  3. The main implementation in src/index.tsx shows proper event handling coordination

I can now generate the final response in Chinese:

事件处理逻辑验证通过,确认实现合理

代码搜索结果显示:

  • 在 Portal 场景下使用捕获阶段处理点击事件是合适的
  • 相关的测试用例和示例代码都采用了类似的事件处理模式
  • 主要实现中的事件处理流程完整且协调
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 描述:检查是否存在其他鼠标事件处理相关的代码

# 测试:搜索相关的事件处理代码
rg -A 3 'onMouseDown|onClick|onMouseUp' 

Length of output: 5782

tests/basic.test.jsx (1)

1202-1231: 测试用例实现完整且合理

测试用例很好地覆盖了 Portal 场景下的关键交互行为:

  • 验证了点击 Portal 内容不会关闭弹出层
  • 验证了点击外部区域会正确关闭弹出层
  • 使用了异步等待来处理计时器

Comment on lines +58 to +67
React.useEffect(() => {
const button = buttonRef.current;
if (button) {
button.addEventListener('mousedown', (e) => {
console.log('button natives down');
e.stopPropagation();
e.preventDefault();
});
}
}, []);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

需要清理事件监听器以防止内存泄漏

useEffect 中添加的事件监听器在组件卸载时没有被移除。

建议修改如下:

 React.useEffect(() => {
   const button = buttonRef.current;
   if (button) {
-    button.addEventListener('mousedown', (e) => {
+    const handleMouseDown = (e: MouseEvent) => {
       console.log('button natives down');
       e.stopPropagation();
       e.preventDefault();
-    });
+    };
+    
+    button.addEventListener('mousedown', handleMouseDown);
+    
+    return () => {
+      button.removeEventListener('mousedown', handleMouseDown);
+    };
   }
 }, []);
📝 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
React.useEffect(() => {
const button = buttonRef.current;
if (button) {
button.addEventListener('mousedown', (e) => {
console.log('button natives down');
e.stopPropagation();
e.preventDefault();
});
}
}, []);
React.useEffect(() => {
const button = buttonRef.current;
if (button) {
const handleMouseDown = (e: MouseEvent) => {
console.log('button natives down');
e.stopPropagation();
e.preventDefault();
};
button.addEventListener('mousedown', handleMouseDown);
return () => {
button.removeEventListener('mousedown', handleMouseDown);
};
}
}, []);

Comment on lines +41 to +54
const PortalPopup = () =>
createPortal(
<div
style={popupBorderStyle}
onMouseDown={(e) => {
console.log('Portal Down', e);
e.stopPropagation();
e.preventDefault();
}}
>
i am a portal element
</div>,
document.body,
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

建议改进门户组件的可访问性和事件处理

当前实现存在以下问题:

  1. 缺少必要的 ARIA 属性
  2. 事件处理可能影响键盘访问性

建议进行如下修改:

 const PortalPopup = () =>
   createPortal(
     <div
       style={popupBorderStyle}
+      role="dialog"
+      aria-modal="true"
+      tabIndex={-1}
       onMouseDown={(e) => {
         console.log('Portal Down', e);
-        e.stopPropagation();
-        e.preventDefault();
+        // 只在点击背景时阻止事件
+        if (e.target === e.currentTarget) {
+          e.stopPropagation();
+          e.preventDefault();
+        }
       }}
+      onKeyDown={(e) => {
+        if (e.key === 'Escape') {
+          // 处理 ESC 关闭
+        }
+      }}
     >
       i am a portal element
     </div>,
     document.body,
   );

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +79 to +94
<Trigger
popupPlacement="right"
action={['click']}
builtinPlacements={builtinPlacements}
popup={
<div style={popupBorderStyle}>
i am a click popup
<PortalPopup />
</div>
}
onPopupVisibleChange={(visible) => {
console.log('visible change:', visible);
}}
>
<button>Click Me</button>
</Trigger>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议统一事件处理方式

目前组件中混合使用了多种事件处理方式(Trigger props、React 事件、原生事件),建议统一处理方式以提高可维护性。

建议将所有事件处理统一到 React 的方式:

 <Trigger
   popupPlacement="right"
   action={['click']}
   builtinPlacements={builtinPlacements}
+  destroyPopupOnHide
   popup={
     <div style={popupBorderStyle}>
       i am a click popup
       <PortalPopup />
     </div>
   }
   onPopupVisibleChange={(visible) => {
     console.log('visible change:', visible);
+    // 在这里统一处理弹窗状态
   }}
 >
-  <button>Click Me</button>
+  <button onClick={(e) => {
+    // 统一的点击处理
+    console.log('trigger clicked');
+  }}>
+    Click Me
+  </button>
 </Trigger>
📝 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
<Trigger
popupPlacement="right"
action={['click']}
builtinPlacements={builtinPlacements}
popup={
<div style={popupBorderStyle}>
i am a click popup
<PortalPopup />
</div>
}
onPopupVisibleChange={(visible) => {
console.log('visible change:', visible);
}}
>
<button>Click Me</button>
</Trigger>
<Trigger
popupPlacement="right"
action={['click']}
builtinPlacements={builtinPlacements}
destroyPopupOnHide
popup={
<div style={popupBorderStyle}>
i am a click popup
<PortalPopup />
</div>
}
onPopupVisibleChange={(visible) => {
console.log('visible change:', visible);
// 在这里统一处理弹窗状态
}}
>
<button onClick={(e) => {
// 统一的点击处理
console.log('trigger clicked');
}}>
Click Me
</button>
</Trigger>

@zombieJ zombieJ merged commit 0fa6243 into master Nov 8, 2024
10 checks passed
@zombieJ zombieJ deleted the fix-portal branch November 8, 2024 07:20
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.

1 participant