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

feat: support paste upload file #543

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

madocto
Copy link

@madocto madocto commented Jan 19, 2024

ref: ant-design/ant-design#46429

Summary by CodeRabbit

  • 文档

    • 新增两个演示文档页面,展示文件上传示例和导航配置。
  • 新功能

    • 优化文件上传体验,新增支持拖拽和粘贴上传的交互方式。
    • 引入全新示例组件,直观演示文件上传操作及多文件处理行为。
  • 测试

    • 增加多项测试用例,验证粘贴上传、多文件限制以及错误处理,确保上传功能的稳定性。

Copy link

vercel bot commented Jan 19, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
upload ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 21, 2025 8:18am

Copy link

codecov bot commented Jan 20, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 89.93%. Comparing base (02ea32a) to head (4efc79a).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #543      +/-   ##
==========================================
+ Coverage   88.84%   89.93%   +1.08%     
==========================================
  Files           6        6              
  Lines         278      298      +20     
  Branches       73       83      +10     
==========================================
+ Hits          247      268      +21     
+ Misses         31       30       -1     

☔ 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.

@afc163
Copy link
Member

afc163 commented Jan 20, 2024

图片

@crazyair
Copy link

crazyair commented Mar 25, 2024

是不是要跟拖拽上传一样风格

<Dragger {...props}>

@afc163
Copy link
Member

afc163 commented Mar 21, 2025

This branch has conflicts that must be resolved.

Copy link

coderabbitai bot commented Mar 21, 2025

Walkthrough

此次变更新增了两个 Markdown 文件(paste.mdpasteDirectory.md),分别配置前置信息与引用外部 TypeScript 示例。同时在 docs/examples 文件夹中添加了两个 React 组件,利用 rc-upload 库实现上传功能(支持拖拽及目录上传)。此外,对 src/AjaxUploader.tsx 文件中的上传逻辑进行了扩展和重命名,新增了针对鼠标事件与粘贴事件的处理方法,并更新了事件监听。测试文件中也新增了多个用例,以验证上述新增功能的行为。

Changes

文件/文件组 改动描述
docs/demo/paste.md
docs/demo/pasteDirectory.md
新增 Markdown 文件,包含前置信息和代码块,引用各自的外部 TS 示例
docs/examples/paste.tsx
docs/examples/pasteDirectory.tsx
新增 React 组件 Test,使用 rc-upload 实现拖拽上传(后者支持目录上传)
src/AjaxUploader.tsx 调整上传逻辑:新增 isMouseEnter 状态;重命名方法为 onFileDropOrPaste;新增 onPrePastehandleMouseEnterhandleMouseLeave 方法;更新事件监听
tests/uploader.spec.tsx 新增并调整多个测试用例,针对粘贴上传、文件类型筛选、多文件处理以及错误处理逻辑进行验证

Sequence Diagram(s)

sequenceDiagram
    participant User as 用户
    participant Uploader as AjaxUploader
    participant RCUpload as rc-upload 组件

    User->>Uploader: 鼠标进入上传区域 (handleMouseEnter)
    User->>Uploader: 粘贴文件 (paste event)
    Uploader->>Uploader: onPrePaste 检测 isMouseEnter
    alt 鼠标在范围内
        Uploader->>Uploader: 执行 onFileDropOrPaste 处理粘贴文件
        Uploader->>RCUpload: 触发文件上传流程
    else
        Uploader-->>User: 忽略粘贴事件
    end
    User->>Uploader: 鼠标离开上传区域 (handleMouseLeave)
Loading

Poem

在代码森林中我蹦跳,
新增语句如花在枝头飘。
拖拽、粘贴巧协同,
测试守候不容稍,
小兔叹息欢欣笑 🐇✨
微风拂面代码飘。

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

docs/examples/paste.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "prettier" to extend from. Please check that the name of the config is correct.

The config "prettier" was referenced from the config file in "/.eslintrc.js".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

docs/examples/pasteDirectory.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "prettier" to extend from. Please check that the name of the config is correct.

The config "prettier" was referenced from the config file in "/.eslintrc.js".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

tests/uploader.spec.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "prettier" to extend from. Please check that the name of the config is correct.

The config "prettier" was referenced from the config file in "/.eslintrc.js".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

  • 1 others
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 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 generate docstrings to generate docstrings for this 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.

@madocto
Copy link
Author

madocto commented Mar 21, 2025

@afc163 done

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: 1

🧹 Nitpick comments (10)
docs/demo/paste.md (1)

8-8: 代码标签格式需要修正

代码引用标签的格式有误。当前的 <code src="../examples/paste.tsx"/></code> 同时包含了自闭合标签和关闭标签,这是不正确的 Markdown 语法。

建议修改为以下格式:

-<code src="../examples/paste.tsx"/></code>
+<code src="../examples/paste.tsx" />
docs/demo/pasteDirectory.md (1)

8-8: 代码标签格式需要修正

代码引用标签的格式有误。当前的 <code src="../examples/pasteDirectory.tsx"/></code> 同时包含了自闭合标签和关闭标签,这是不正确的 Markdown 语法。

建议修改为以下格式:

-<code src="../examples/pasteDirectory.tsx"/></code>
+<code src="../examples/pasteDirectory.tsx" />
docs/examples/paste.tsx (2)

6-8: 建议增加更多文件类型支持

当前仅接受 .png 文件类型,考虑到粘贴上传的场景,用户可能会粘贴其他格式的图片(如 JPEG、GIF 等)。

-  accept: '.png',
+  accept: 'image/*',

36-36: 考虑国际化支持

组件中硬编码了中文文本 "开始上传",这可能不利于国际化。

考虑使用配置项或国际化工具来处理文本:

-          <a>开始上传</a>
+          <a>{props.buttonText || '开始上传'}</a>

或者在实际的项目中,使用国际化工具如 i18n:

-          <a>开始上传</a>
+          <a>{intl.get('upload.start') || '开始上传'}</a>
docs/examples/pasteDirectory.tsx (2)

8-9: 建议增加更多文件类型支持

当前仅接受 .png 文件类型,考虑到目录上传的场景,用户可能会上传各种格式的文件。

-  accept: '.png',
+  accept: '*/*',

37-37: 考虑国际化支持

组件中硬编码了中文文本 "开始上传",这可能不利于国际化。

考虑使用配置项或国际化工具来处理文本:

-          <a>开始上传</a>
+          <a>{props.buttonText || '开始上传'}</a>

或者在实际的项目中,使用国际化工具如 i18n:

-          <a>开始上传</a>
+          <a>{intl.get('upload.start') || '开始上传'}</a>
tests/uploader.spec.tsx (3)

59-59: 改进错误处理方式

这里的修改改进了错误处理,但可以进一步优化使用可选链操作符。

-            return error && error(new Error('read file error'));
+            return error?.(new Error('read file error'));
🧰 Tools
🪛 Biome (1.9.4)

[error] 59-59: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


376-379: 考虑直接在 rcUpload 上触发粘贴事件

目前测试在 input 上触发粘贴事件,但在真实场景中,粘贴事件是在文档级别被捕获的,并且在实现中只有当鼠标位于上传组件上时才处理。为保持一致性,建议直接在 rcUpload 元素上触发粘贴事件。

fireEvent.mouseEnter(rcUpload);
-fireEvent.paste(input, {
+fireEvent.paste(rcUpload, {
  clipboardData: { files },
});

444-446: 与之前建议一致,考虑在 rcUpload 上触发粘贴事件

同样的问题出现在这个测试中,建议在 rcUpload 上触发粘贴事件而不是 input。

fireEvent.mouseEnter(rcUpload);
-fireEvent.paste(input, { clipboardData: { files } });
+fireEvent.paste(rcUpload, { clipboardData: { files } });
src/AjaxUploader.tsx (1)

108-112: 粘贴事件预处理方法可以优化

此方法实现粘贴功能,但有两点可以改进:

  1. 缺少对禁用状态的检查
  2. 没有考虑到如果用户正在编辑文本时,阻止默认行为可能会干扰正常文本粘贴
onPrePaste(e: ClipboardEvent) {
-  if (this.isMouseEnter) {
+  // 检查组件是否启用并且鼠标在组件上
+  if (this.isMouseEnter && !this.props.disabled) {
+    // 只有在剪贴板包含文件时才阻止默认行为
+    if (e.clipboardData?.files?.length > 0) {
     this.onFileDropOrPaste(e);
+    }
  }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02ea32a and 4efc79a.

📒 Files selected for processing (6)
  • docs/demo/paste.md (1 hunks)
  • docs/demo/pasteDirectory.md (1 hunks)
  • docs/examples/paste.tsx (1 hunks)
  • docs/examples/pasteDirectory.tsx (1 hunks)
  • src/AjaxUploader.tsx (4 hunks)
  • tests/uploader.spec.tsx (10 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
src/AjaxUploader.tsx (2)
src/interface.tsx (1) (1)
  • RcFile (78-80)
src/attr-accept.ts (1) (1)
  • file (4-53)
tests/uploader.spec.tsx (2)
src/AjaxUploader.tsx (1) (1)
  • render (295-361)
src/Upload.tsx (1) (1)
  • render (37-39)
🪛 Biome (1.9.4)
tests/uploader.spec.tsx

[error] 59-59: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (15)
docs/demo/paste.md (1)

1-8: 文件结构与元数据设置正确

Markdown 文件的 frontmatter 部分设置合理,包含了标题和导航路径的定义,这有助于在文档系统中正确显示和定位此示例。

docs/demo/pasteDirectory.md (1)

1-8: 文件结构与元数据设置正确

Markdown 文件的 frontmatter 部分设置合理,包含了标题和导航路径的定义,这有助于在文档系统中正确显示和定位此示例。

docs/examples/paste.tsx (2)

5-25: 配置对象结构清晰

props 对象的配置结构清晰,包含了上传所需的各种回调函数和样式属性。日志输出有助于调试和理解上传流程。


27-41: 组件实现简洁,但缺少实际的粘贴功能说明

虽然文件名为 paste.tsx,但组件中并没有明确处理粘贴事件的代码。根据 PR 的目标(支持粘贴上传文件),建议添加一些注释或文本说明,告知用户如何使用粘贴功能。

  return (
    <div
      style={{
        margin: 100,
      }}
    >
      <div>
+       <p style={{ marginBottom: 10 }}>支持粘贴上传文件 (Ctrl+V)</p>
        <Upload {...props}>
          <a>开始上传</a>
        </Upload>
      </div>
    </div>
  );
docs/examples/pasteDirectory.tsx (2)

5-26: 配置对象结构清晰

props 对象的配置结构清晰,包含了上传所需的各种回调函数和样式属性。日志输出有助于调试和理解上传流程。


28-42: 组件实现简洁,但缺少实际的粘贴功能说明

虽然文件名为 pasteDirectory.tsx,但组件中并没有明确处理粘贴事件的代码,也没有关于如何通过粘贴方式上传目录的说明。根据 PR 的目标(支持粘贴上传文件),建议添加一些注释或文本说明。

  return (
    <div
      style={{
        margin: 100,
      }}
    >
      <div>
+       <p style={{ marginBottom: 10 }}>支持粘贴上传目录 (Ctrl+V)</p>
        <Upload {...props}>
          <a>开始上传</a>
        </Upload>
      </div>
    </div>
  );
tests/uploader.spec.tsx (5)

352-384: 新增粘贴上传功能测试看起来很全面

这个测试用例验证了粘贴文件上传功能的正常工作,覆盖了鼠标进入、粘贴事件触发和请求响应等关键流程。设计合理,模拟了真实用户交互。


386-407: 不符合类型的粘贴文件测试逻辑完善

这个测试正确验证了当粘贴不符合 accept 类型的文件时,不会触发 onStart 回调。测试场景考虑全面。


479-492: 使用箭头函数和期望对象更新了测试断言

将函数声明更新为箭头函数并更新了期望对象中的断言,代码风格更加现代化和一致。


506-519: 添加鼠标进入和离开事件测试

这个测试很好地验证了 onMouseEnter 和 onMouseLeave 回调函数是否正确调用,支持新增的粘贴功能所需的鼠标状态跟踪。


785-805: 粘贴目录功能测试覆盖全面

测试正确验证了粘贴包含目录结构的功能,清晰地模拟了用户交互并验证了预期行为。

src/AjaxUploader.tsx (4)

31-31: 新增鼠标状态跟踪属性

添加 isMouseEnter 属性用于跟踪鼠标是否悬停在上传组件上,为支持粘贴上传功能奠定基础。


71-106: 重构方法支持文件拖放和粘贴

将 onFileDrop 重命名并扩展为 onFileDropOrPaste,支持处理拖放和粘贴事件。实现逻辑清晰,根据事件类型正确获取文件和项目。


283-293: 鼠标事件处理方法实现良好

handleMouseEnter 和 handleMouseLeave 方法实现简洁明了,既更新内部状态又调用可选的外部回调函数。


329-332: 事件监听更新支持新功能

事件处理器的更新正确使用了新添加的方法,支持粘贴上传和鼠标状态跟踪功能。

componentDidMount() {
this._isMounted = true;
document.addEventListener('paste', this.onPrePaste.bind(this));
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

事件监听器绑定方式可能导致内存泄漏

每次调用 .bind(this) 都会创建一个新的函数实例,这可能导致添加和移除的不是同一个函数引用,从而可能引起内存泄漏。

+constructor(props) {
+  super(props);
+  this.boundOnPrePaste = this.onPrePaste.bind(this);
+}

componentDidMount() {
  this._isMounted = true;
-  document.addEventListener('paste', this.onPrePaste.bind(this));
+  document.addEventListener('paste', this.boundOnPrePaste);
}

componentWillUnmount() {
  this._isMounted = false;
  this.abort();
-  document.removeEventListener('paste', this.onPrePaste.bind(this));
+  document.removeEventListener('paste', this.boundOnPrePaste);
}
📝 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
document.addEventListener('paste', this.onPrePaste.bind(this));
class AjaxUploader extends React.Component<Props, State> {
constructor(props) {
super(props);
this.boundOnPrePaste = this.onPrePaste.bind(this);
}
componentDidMount() {
this._isMounted = true;
document.addEventListener('paste', this.boundOnPrePaste);
}
componentWillUnmount() {
this._isMounted = false;
this.abort();
document.removeEventListener('paste', this.boundOnPrePaste);
}
onPrePaste(event) {
// existing logic for handling paste events
}
// additional component methods...
}

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.

3 participants