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(Video): web h5 下支持 ref 调用 #2852

Merged
merged 6 commits into from
Dec 12, 2024
Merged

Conversation

xiaoyatong
Copy link
Collaborator

@xiaoyatong xiaoyatong commented Dec 11, 2024

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • fork仓库代码是否为最新避免文件冲突
  • Files changed 没有 package.json lock 等无关文件

Summary by CodeRabbit

  • 新特性

    • 更新了视频组件的翻译字符串,增强了多语言支持。
    • 新增了视频组件的引用方法playpause,支持外部控制视频播放。
    • 视频组件现在支持自动暂停和播放功能。
  • 文档

    • 更新了视频组件的文档,增加了关于引用方法的详细说明。

Copy link

coderabbitai bot commented Dec 11, 2024

Walkthrough

此次更改涉及多个文件,主要更新了视频组件的功能和文档。VideoDemo 组件的翻译字符串进行了更新,以提供更详细的描述。Demo2 组件添加了 useRef 钩子和 setTimeout 函数,以实现视频的自动暂停和播放。视频组件被转换为转发引用组件,允许外部控制播放和暂停。文档中新增了关于 Ref 方法的部分,详细说明了如何通过这些方法控制视频播放。

Changes

文件路径 更改摘要
src/packages/video/demo.tsx 更新 autoPlay 翻译字符串,包含更详细的描述。
src/packages/video/demos/h5/demo2.tsx 添加 useRef 钩子,创建视频元素引用,并增加 setTimeout 函数以控制视频的自动播放和暂停。
src/packages/video/doc.en-US.md 新增 Ref 部分,描述 playpause 方法。
src/packages/video/doc.md 新增 Ref 部分,描述 playpause 方法。
src/packages/video/doc.zh-TW.md 新增 Ref 部分,描述 playpause 方法。
src/packages/video/video.tsx Video 组件转换为转发引用组件,新增 pauseplay 方法,更新组件签名。
src/packages/video/__tests__/video.spec.tsx 新增测试用例以验证 Video 组件的暂停功能。
src/packages/empty/utils.ts 新增 getButtonType 函数,用于获取按钮类型。

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant VideoComponent
    participant VideoElement

    User->>VideoComponent: 调用 play()
    VideoComponent->>VideoElement: 播放视频
    User->>VideoComponent: 调用 pause()
    VideoComponent->>VideoElement: 暂停视频
Loading

Possibly related PRs

Suggested labels

size/M, 3.x

Suggested reviewers

  • oasis-cloud

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 generate docstrings to generate docstrings for this PR. (Experiment)
  • @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 Dec 11, 2024

Codecov Report

Attention: Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.

Project coverage is 84.02%. Comparing base (229b038) to head (ad3dd5c).
Report is 1 commits behind head on next.

Files with missing lines Patch % Lines
src/packages/video/video.tsx 85.71% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             next    #2852   +/-   ##
=======================================
  Coverage   84.02%   84.02%           
=======================================
  Files         220      220           
  Lines       17907    17917   +10     
  Branches     2625     2628    +3     
=======================================
+ Hits        15046    15055    +9     
- Misses       2856     2857    +1     
  Partials        5        5           

☔ 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: 2

🧹 Outside diff range and nitpick comments (2)
src/packages/video/demos/h5/demo2.tsx (1)

18-26: 建议优化 ref 控制示例的实现方式

当前使用 setTimeout 来演示 ref 的控制功能可能不是最佳选择,因为:

  1. 定时器可能会在组件卸载后继续执行
  2. 硬编码的延迟时间不够灵活

建议改为使用按钮触发的方式:

-  setTimeout(() => {
-    rootRef?.current?.pause()
-  }, 2000)
-
-  setTimeout(() => {
-    rootRef?.current?.play()
-  }, 4000)
+  const handlePause = () => {
+    rootRef?.current?.pause()
+  }
+
+  const handlePlay = () => {
+    rootRef?.current?.play()
+  }
+
+  return (
+    <>
+      <div>
+        <button onClick={handlePause}>暂停</button>
+        <button onClick={handlePlay}>播放</button>
+      </div>
+      <Cell>
src/packages/video/doc.md (1)

99-104: 建议补充 Ref 方法的详细说明

当前的 Ref 方法文档虽然简洁明了,但建议补充以下内容:

  1. 方法的返回值说明
  2. 使用示例代码
  3. 可能的异常情况处理

建议扩展文档内容如下:

 ### Ref
 
 | 方法名 | 说明 | 参数 |
 | --- | --- | --- |
-| play | 播放 | `-` |
-| pause | 暂停 | `-` |
+| play | 播放视频。返回一个 Promise,当视频开始播放时 resolve。 | `-` |
+| pause | 暂停视频。返回一个 Promise,当视频暂停时 resolve。 | `-` |
+
+```tsx
+// 示例代码
+const videoRef = useRef<HTMLVideoElement>(null)
+
+// 播放视频
+const handlePlay = async () => {
+  try {
+    await videoRef.current?.play()
+    console.log('视频开始播放')
+  } catch (error) {
+    console.error('播放失败:', error)
+  }
+}
+
+// 暂停视频
+const handlePause = async () => {
+  try {
+    await videoRef.current?.pause()
+    console.log('视频已暂停')
+  } catch (error) {
+    console.error('暂停失败:', error)
+  }
+}
+```
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 601b5d1 and c1538ab.

📒 Files selected for processing (6)
  • src/packages/video/demo.tsx (3 hunks)
  • src/packages/video/demos/h5/demo2.tsx (2 hunks)
  • src/packages/video/doc.en-US.md (1 hunks)
  • src/packages/video/doc.md (1 hunks)
  • src/packages/video/doc.zh-TW.md (1 hunks)
  • src/packages/video/video.tsx (4 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/packages/video/video.tsx

[warning] 98-99: src/packages/video/video.tsx#L98-L99
Added lines #L98 - L99 were not covered by tests


[warning] 101-102: src/packages/video/video.tsx#L101-L102
Added lines #L101 - L102 were not covered by tests


[warning] 105-106: src/packages/video/video.tsx#L105-L106
Added lines #L105 - L106 were not covered by tests

🔇 Additional comments (7)
src/packages/video/demos/h5/demo2.tsx (1)

31-31: ref 属性的使用正确

ref 属性的传递实现正确,能够正常获取到视频元素的引用。

src/packages/video/demo.tsx (1)

16-16: 翻译更新准确反映了新功能

各语言版本的翻译更新准确地描述了新增的 ref 控制功能,保持了良好的一致性。

Also applies to: 25-25, 34-34

src/packages/video/doc.zh-TW.md (1)

98-104: 文档结构清晰,内容完整!

新增的 Ref 部分很好地说明了 playpause 方法的用法。

src/packages/video/doc.en-US.md (1)

98-104: 英文文档与中文文档保持一致!

Ref 部分的翻译准确,与中文版本内容一致。

src/packages/video/video.tsx (3)

39-42: 类型定义清晰明确!

VideoRef 类型定义简洁且符合组件需求。


45-49: 组件类型定义合理!

使用 React.forwardRef 并正确处理了类型继承关系,通过 Omit 避免了事件处理器的类型冲突。


104-107: useImperativeHandle 使用正确!

正确使用 useImperativeHandle 暴露必要的方法。

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 105-106: src/packages/video/video.tsx#L105-L106
Added lines #L105 - L106 were not covered by tests

Comment on lines +97 to +107
const pause = () => {
rootRef?.current?.pause()
}
const play = () => {
rootRef?.current?.play()
}

React.useImperativeHandle(ref, () => ({
pause,
play,
}))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

需要添加单元测试!

新增的 ref 方法缺少测试覆盖,建议添加相应的单元测试用例。

建议添加以下测试场景:

  1. 测试 play 方法调用
  2. 测试 pause 方法调用
  3. 测试错误处理场景

需要我帮您生成测试用例代码吗?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 98-99: src/packages/video/video.tsx#L98-L99
Added lines #L98 - L99 were not covered by tests


[warning] 101-102: src/packages/video/video.tsx#L101-L102
Added lines #L101 - L102 were not covered by tests


[warning] 105-106: src/packages/video/video.tsx#L105-L106
Added lines #L105 - L106 were not covered by tests

Comment on lines +97 to +102
const pause = () => {
rootRef?.current?.pause()
}
const play = () => {
rootRef?.current?.play()
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议增加错误处理!

playpause 方法应该添加错误处理,因为 HTMLVideoElement 的这些方法会返回 Promise。

建议修改实现如下:

-  const pause = () => {
-    rootRef?.current?.pause()
-  }
-  const play = () => {
-    rootRef?.current?.play()
-  }
+  const pause = async () => {
+    try {
+      await rootRef?.current?.pause()
+    } catch (error) {
+      console.error('视频暂停失败:', error)
+    }
+  }
+  const play = async () => {
+    try {
+      await rootRef?.current?.play()
+    } catch (error) {
+      console.error('视频播放失败:', error)
+    }
+  }
📝 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
const pause = () => {
rootRef?.current?.pause()
}
const play = () => {
rootRef?.current?.play()
}
const pause = async () => {
try {
await rootRef?.current?.pause()
} catch (error) {
console.error('视频暂停失败:', error)
}
}
const play = async () => {
try {
await rootRef?.current?.play()
} catch (error) {
console.error('视频播放失败:', error)
}
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 98-99: src/packages/video/video.tsx#L98-L99
Added lines #L98 - L99 were not covered by tests


[warning] 101-102: src/packages/video/video.tsx#L101-L102
Added lines #L101 - L102 were not covered by tests

Copy link
Collaborator

@irisSong irisSong left a comment

Choose a reason for hiding this comment

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

单测加一下吧,覆盖率不通过

@pull-request-size pull-request-size bot added size/L and removed size/M labels Dec 11, 2024
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: 2

🧹 Outside diff range and nitpick comments (3)
src/packages/video/demos/h5/demo2.tsx (2)

1-1: 建议使用更严格的类型定义

建议为 source 状态添加明确的接口定义,以提高代码的可维护性和类型安全性。

+interface VideoSource {
+  src: string;
+  type: string;
+}
-const [source] = useState({
+const [source] = useState<VideoSource>({
   src: 'https://storage.360buyimg.com/nutui/video/video_NutUI.mp4',
   type: 'video/mp4',
})

Also applies to: 5-5


31-31: 建议添加注释说明 ref 的用途

为了提高代码可读性,建议添加注释说明 ref 的作用和使用场景。

 <Video
+  // ref 用于控制视频的播放和暂停
   ref={rootRef}
src/packages/video/__tests__/video.spec.tsx (1)

31-70: 建议增加更多测试场景

当前测试用例只覆盖了视频暂停功能,建议添加以下测试场景:

  1. 测试 play() 方法
  2. 测试 ref 为 null 时的错误处理
  3. 测试视频加载失败的情况

需要我帮您生成这些额外的测试用例吗?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c1538ab and 5fed466.

📒 Files selected for processing (2)
  • src/packages/video/__tests__/video.spec.tsx (2 hunks)
  • src/packages/video/demos/h5/demo2.tsx (2 hunks)
🔇 Additional comments (1)
src/packages/video/__tests__/video.spec.tsx (1)

2-2: 导入声明看起来没有问题!

新增的 fireEventButton 导入对于测试视频暂停功能是必要的。

Also applies to: 6-6

Comment on lines +18 to +25
const rootRef = useRef<HTMLVideoElement>(null)
setTimeout(() => {
rootRef?.current?.pause()
}, 2000)

setTimeout(() => {
rootRef?.current?.play()
}, 4000)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议优化定时器实现

当前实现存在以下几个问题:

  1. 没有清理定时器
  2. 硬编码的时间值不易维护
  3. 缺少错误处理

建议按照以下方式重构:

+const PAUSE_DELAY = 2000;
+const PLAY_DELAY = 4000;
 const rootRef = useRef<HTMLVideoElement>(null);
+
+useEffect(() => {
+  const pauseTimer = setTimeout(() => {
+    try {
+      rootRef?.current?.pause();
+    } catch (error) {
+      console.error('视频暂停失败:', error);
+    }
+  }, PAUSE_DELAY);
 
-setTimeout(() => {
-  rootRef?.current?.pause()
-}, 2000)
+  const playTimer = setTimeout(() => {
+    try {
+      rootRef?.current?.play();
+    } catch (error) {
+      console.error('视频播放失败:', error);
+    }
+  }, PLAY_DELAY);
 
-setTimeout(() => {
-  rootRef?.current?.play()
-}, 4000)
+  return () => {
+    clearTimeout(pauseTimer);
+    clearTimeout(playTimer);
+  };
+}, []);
📝 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
const rootRef = useRef<HTMLVideoElement>(null)
setTimeout(() => {
rootRef?.current?.pause()
}, 2000)
setTimeout(() => {
rootRef?.current?.play()
}, 4000)
const PAUSE_DELAY = 2000;
const PLAY_DELAY = 4000;
const rootRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
const pauseTimer = setTimeout(() => {
try {
rootRef?.current?.pause();
} catch (error) {
console.error('视频暂停失败:', error);
}
}, PAUSE_DELAY);
const playTimer = setTimeout(() => {
try {
rootRef?.current?.play();
} catch (error) {
console.error('视频播放失败:', error);
}
}, PLAY_DELAY);
return () => {
clearTimeout(pauseTimer);
clearTimeout(playTimer);
};
}, []);

Comment on lines 31 to 70
test('video props', () => {
const pause = vi.fn()
const App = () => {
const source = {
src: 'xxx.mp4',
type: 'video/mp4',
}
const itemRef = React.useRef<HTMLVideoElement>(null)
const options = {
controls: true,
autoplay: true,
playsinline: true,
loop: true,
poster:
'https://img12.360buyimg.com/ling/s345x208_jfs/t1/168105/33/8417/54825/603df06dEfcddc4cb/21f9f5d0a1b3dad4.jpg.webp',
}

return (
<>
<Video
ref={itemRef}
source={source}
options={options}
onPause={pause}
/>
<Button
size="small"
data-testid="emit-click"
onClick={() => itemRef?.current?.pause()}
>
暂停一下
</Button>
</>
)
}

const { getByTestId } = render(<App />)
fireEvent.click(getByTestId('emit-click'))
expect(pause).toBeCalled()
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议完善测试用例的健壮性

当前的测试用例可以通过以下几点来增强:

  1. 需要在测试后清理 mock 函数
  2. 应该验证 video ref 是否正确获取到了 video 元素
  3. 测试描述可以更具体,说明测试目的

建议按照以下方式修改:

-test('video props', () => {
+test('视频组件应该正确响应 ref.pause() 调用', () => {
   const pause = vi.fn()
+  // 确保在每个测试后清理 mock
+  afterEach(() => {
+    pause.mockClear()
+  })
   
   const App = () => {
     // ... existing code ...
   }

   const { getByTestId } = render(<App />)
+  const videoElement = itemRef.current
+  expect(videoElement).not.toBeNull()
   fireEvent.click(getByTestId('emit-click'))
   expect(pause).toBeCalled()
+  expect(pause).toHaveBeenCalledTimes(1)
 })

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

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

🧹 Outside diff range and nitpick comments (1)
src/packages/empty/utils.ts (1)

1-1: 建议添加类型导出

为了提高代码的可维护性,建议将 EmptyAction 类型也导出。

-import { EmptyAction } from './types'
+export { EmptyAction } from './types'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between aae7ae7 and b7c300d.

📒 Files selected for processing (2)
  • src/packages/empty/utils.ts (1 hunks)
  • src/packages/video/__tests__/video.spec.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/packages/video/tests/video.spec.tsx

Comment on lines 3 to 8
export const getButtonType = (actions: Array<EmptyAction>, index: number) => {
if (!actions || actions.length === 0) return 'default'
const action = actions[index]
if (!actions || actions.length === 0) return 'default'
if (action.type) return action.type
return actions.length > 1 && index === 0 ? 'default' : 'primary'
actions.length > 1 && index === 0 ? 'default' : 'primary'
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

缺少 return 语句导致函数返回 undefined

函数在某些情况下会返回 undefined,这可能导致意外行为。同时,函数的防御性编程可以更强。

建议按照以下方式修改代码:

 export const getButtonType = (actions: Array<EmptyAction>, index: number) => {
+  if (!actions?.length) return 'default'
   const action = actions[index]
-  if (!actions || actions.length === 0) return 'default'
+  if (!action) return 'default'
   if (action.type) return action.type
-  actions.length > 1 && index === 0 ? 'default' : 'primary'
+  return actions.length > 1 && index === 0 ? 'default' : 'primary'
 }

修改说明:

  1. 修复了缺少 return 语句的问题
  2. 增加了对 action 是否存在的检查
  3. 优化了空数组检查的位置
  4. 使用可选链操作符简化了代码
📝 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
export const getButtonType = (actions: Array<EmptyAction>, index: number) => {
if (!actions || actions.length === 0) return 'default'
const action = actions[index]
if (!actions || actions.length === 0) return 'default'
if (action.type) return action.type
return actions.length > 1 && index === 0 ? 'default' : 'primary'
actions.length > 1 && index === 0 ? 'default' : 'primary'
}
export const getButtonType = (actions: Array<EmptyAction>, index: number) => {
if (!actions?.length) return 'default'
const action = actions[index]
if (!action) return 'default'
if (action.type) return action.type
return actions.length > 1 && index === 0 ? 'default' : 'primary'
}

@irisSong irisSong merged commit 5a5f7c4 into jdf2e:next Dec 12, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants