Skip to content

feat: support rowspan expanded #1278

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

Open
wants to merge 27 commits into
base: master
Choose a base branch
from

Conversation

crazyair
Copy link
Contributor

@crazyair crazyair commented May 21, 2025

Summary by CodeRabbit

  • 新功能
    • 新增了“expandedRowSpan”演示页面,展示表格行合并与可展开行的用法示例。
    • 新增了“expandedSticky”演示页面,展示带有粘性列和可展开行的表格示例。
  • 文档
    • 增加了“expandedRowSpan.md”和“expandedSticky.md”文档,包含相关代码演示。
  • 修复
    • 优化了表格在行合并与行展开同时使用时的表现,确保合并单元格能正确适应展开行。
    • 调整展开行宽度计算,提升展开内容显示的准确性。

Copy link

vercel bot commented May 21, 2025

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

Name Status Preview Comments Updated (UTC)
table ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 24, 2025 2:39am

Copy link

coderabbitai bot commented May 21, 2025

"""

Walkthrough

此次更改引入了一个新的“expandedRowSpan”演示文档及相关 React 代码示例,展示了带有可展开行和自定义行合并(rowSpan)功能的表格。同时,核心表格组件逻辑被增强,以支持在行展开时动态调整单元格的 rowSpan 属性,并在数据结构中显式维护 rowKey。

Changes

文件路径分组 变更摘要
docs/demo/expandedRowSpan.md 新增文档页面“expandedRowSpan”,配置导航元数据,并嵌入了代码示例。
docs/examples/expandedRowSpan.tsx 新增 React 代码示例,演示 rc-table 结合可展开行与自定义 rowSpan 的用法,包含列定义、动态 rowSpan 逻辑、可展开行渲染及样式引入。导出默认组件 Demo。
docs/demo/expandedSticky.md 新增文档页面“expandedSticky”,配置导航元数据,并嵌入了对应的代码示例。
docs/examples/expandedSticky.tsx 新增 React 代码示例,演示带有可展开行和固定列(sticky)的表格,包含列定义、展开行状态管理及渲染。导出默认组件 Demo。
src/Body/BodyRow.tsx BodyRowProps 接口新增 rowKeys 属性,getCellProps 函数和 BodyRow 组件均支持 rowKeys。getCellProps 内部根据 expandedKeys 动态调整 rowSpan,确保单元格在行展开时正确合并。渲染时改用 memoized 的列数据,提升效率。
src/Body/index.tsx 重构数据处理逻辑:通过 useFlattenRecords 生成的 flattenData 提取 rowKey 数组 rowKeys。渲染 BodyRow 时传递 rowKey 及 rowKeys,简化 key 处理。
src/hooks/useFlattenRecords.ts FlattenData 接口及相关数据结构新增 rowKey 属性,确保每个扁平化数据项都显式携带其唯一 key。
src/VirtualTable/VirtualCell.tsx VirtualCell 组件调用 getCellProps 时增加空数组参数,未影响其他逻辑。
src/Body/ExpandedRow.tsx ExpandedRowProps 接口新增可选属性 offsetWidth。ExpandedRow 组件中调整宽度计算,减去 offsetWidth 以适配额外的水平偏移。
src/Table.tsx Table 组件中将 classNames 和 styles 加入 TableContextValue 的依赖数组,确保上下文更新。
src/context/TableContext.tsx 将 TableProps 改为类型导入,明确仅用于类型注解,无运行时影响。
src/Panel/index.tsx Panel 组件的 JSX 返回格式由单行改为多行,功能无变更。
tests/Expanded.spec.tsx 新增测试套件验证表格展开行与 rowSpan 结合的渲染行为,模拟 DOM 属性,断言单元格的 rowSpan 和 colSpan 正确应用。

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DemoPage
    participant DemoComponent
    participant Table
    participant BodyRow

    User->>DemoPage: 访问 expandedRowSpan 演示页
    DemoPage->>DemoComponent: 渲染 Demo 组件
    DemoComponent->>Table: 渲染带有 columns/data/expandable 的表格
    Table->>BodyRow: 传递 rowKeys, rowKey, columns, expandedKeys 等
    BodyRow->>getCellProps: 计算每个单元格的 rowSpan(考虑展开行)
    getCellProps-->>BodyRow: 返回调整后的单元格属性
    BodyRow-->>Table: 渲染行
    Table-->>DemoComponent: 渲染完整表格
Loading

Possibly related PRs

  • 无用的getRowKey #1210: 该 PR 也涉及 BodyRowrowKeys 属性调整,虽然关注点不同,但在组件接口层面相关。

Suggested reviewers

  • zombieJ
  • afc163

Poem

兔子跳跃新功能,
表格行合并真灵活。
展开收起随心意,
rowKey 明确不迷离。
文档示例齐上阵,
代码世界更精彩!
🐇✨
"""

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.

tests/Expanded.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.

Note

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fa25174 and dc67020.

📒 Files selected for processing (1)
  • tests/Expanded.spec.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Expanded.spec.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test / react component workflow
✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate sequence diagram to generate a sequence diagram of the changes in 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.

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

🧹 Nitpick comments (5)
src/Body/index.tsx (1)

45-50: 改进代码格式和可读性

useFlattenRecords 调用重新格式化为多行,提高了代码的可读性。每个参数单独一行,使函数调用更加清晰。

docs/examples/expandedRowSpan.tsx (3)

12-21: 考虑优化onCell回调函数的实现方式

当前实现使用了多个独立的if语句来处理不同行索引的rowSpan设置,这种方式虽然直观但略显冗长。

可以考虑使用映射或switch语句来简化实现:

- onCell: (_, index) => {
-   const props: React.TdHTMLAttributes<HTMLTableCellElement> = {};
-   if (index === 0) props.rowSpan = 1;
-   if (index === 1) props.rowSpan = 4;
-   if (index === 2) props.rowSpan = 0;
-   if (index === 3) props.rowSpan = 0;
-   if (index === 4) props.rowSpan = 0;
-   if (index === 5) props.rowSpan = undefined;
-   return props;
- },
+ onCell: (_, index) => {
+   const rowSpanMap = {
+     0: 1,
+     1: 4,
+     2: 0,
+     3: 0,
+     4: 0,
+     5: undefined
+   };
+   return { rowSpan: rowSpanMap[index] };
+ },

8-9: 考虑使用英文列标题以保持一致性

示例中使用了中文列标题"手机号",但大多数其他列使用了英文标题。为保持一致性,建议统一使用一种语言的列标题。


29-36: 建议使用更具描述性的数据字段名

当前数据结构使用了通用字段名(a, b, c, d, e),这不够直观。建议使用与列标题匹配的具有描述性的字段名,以提高代码可读性。

- { a: '12313132132', b: '0571-43243256', c: '小二', d: '文零西路', e: 'Male', key: 'z' },
+ { phoneNumber: '12313132132', tel: '0571-43243256', name: '小二', address: '文零西路', gender: 'Male', key: 'z' },
src/Body/BodyRow.tsx (1)

82-92: 考虑添加边界情况处理

当前实现假定rowSpan和rowKeys数组都是有效的。可以考虑添加边界情况检查,比如当rowSpan为undefined或NaN时,或者当i索引超出rowKeys数组范围时的处理。

if (expandable) {
  let currentRowSpan = rowSpan;

+ // 确保rowSpan是有效的数值
+ if (typeof currentRowSpan !== 'number' || isNaN(currentRowSpan)) {
+   currentRowSpan = 1;
+ }

  for (let i = index; i < index + rowSpan; i += 1) {
+   // 确保索引在数组范围内
+   if (i >= rowKeys.length) break;
    
    const rowKey = rowKeys[i];
    if (expandedKeys.has(rowKey)) {
      currentRowSpan += 1;
    }
  }
  additionalCellProps.rowSpan = currentRowSpan;
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d696f75 and 1cb0013.

📒 Files selected for processing (6)
  • docs/demo/expandedRowSpan.md (1 hunks)
  • docs/examples/expandedRowSpan.tsx (1 hunks)
  • src/Body/BodyRow.tsx (6 hunks)
  • src/Body/index.tsx (2 hunks)
  • src/VirtualTable/VirtualCell.tsx (1 hunks)
  • src/hooks/useFlattenRecords.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/hooks/useFlattenRecords.ts (1)
src/interface.ts (1)
  • Key (21-21)
src/Body/BodyRow.tsx (2)
src/interface.ts (1)
  • Key (21-21)
src/hooks/useRowInfo.tsx (1)
  • useRowInfo (8-123)
🔇 Additional comments (14)
src/VirtualTable/VirtualCell.tsx (1)

59-66: 确保 getCellProps 参数满足新的函数签名要求

在调用 getCellProps 时添加了空数组 [] 作为新的 rowKeys 参数,这符合 BodyRow.tsx 中更新后的函数签名要求。此更改使 VirtualCell 组件与扩展的行和行合并(rowSpan)功能兼容。

docs/demo/expandedRowSpan.md (1)

1-8: 新增扩展行合并示例文档

这个新增的文档文件创建了一个展示扩展行合并(expandedRowSpan)功能的演示页面,引用了 ../examples/expandedRowSpan.tsx 中的代码示例。这是对新功能的良好文档支持。

src/hooks/useFlattenRecords.ts (3)

14-19: 向扁平化数据中添加 rowKey 属性

fillRecords 函数中,将 rowKey 属性添加到扁平化数据中,确保每条记录都明确携带其唯一键。这是支持可展开行和行合并功能的重要基础。


41-46: 扩展 FlattenData 接口

FlattenData 接口中添加 rowKey: Key 属性,确保类型安全并与实际实现保持一致。这种改进增强了代码的可维护性和类型检查。


80-86: 确保非扩展状态下的 rowKey 一致性

在数据映射的回退逻辑中添加 rowKey 属性,确保即使不使用扩展功能,每个扁平化记录也具有正确的 rowKey。这种一致性很重要,可以避免在不同表格状态下出现不一致的行为。

src/Body/index.tsx (3)

52-52: 添加行键(rowKeys)记忆化数组

添加了一个新的记忆化数组 rowKeys,用于存储所有扁平化数据项的 rowKey。这是支持扩展行合并功能的关键变更,确保每个 BodyRow 都能访问完整的行键列表,从而正确计算行跨度(rowSpan)。


68-68: 从扁平化数据中直接解构 rowKey

从扁平化数据项中直接解构 rowKey,替代之前通过 getRowKey 计算 key 的方式。这简化了代码并消除了重复计算,与 useFlattenRecords.ts 中的更改保持一致。


71-83: 向 BodyRow 组件传递行键信息

rowKey 同时作为 React key 和 prop 传递给 BodyRow 组件,并额外传递 rowKeys 数组作为新的 prop。这使得每个行组件都能获取行键信息以及完整的行键列表,从而支持基于展开状态的动态行合并计算。

docs/examples/expandedRowSpan.tsx (2)

1-51: 新增的扩展行与行合并示例全面展示了功能

这个新增的示例组件很好地展示了表格支持扩展行与行合并(rowSpan)的功能。完整的示例包含了所有必要的配置项,包括列定义、数据源和行展开渲染函数。


45-45: 确保展开行渲染函数的样式是必要的

展开行渲染函数中设置了margin为0的样式。请确认这个样式设置是必要的,如果只是为了示例,可以考虑添加更多有意义的内容展示。

src/Body/BodyRow.tsx (4)

22-22: 接口扩展支持行键数组参数

为BodyRowProps接口添加rowKeys参数,这是支持展开行状态下行合并计算的关键部分。


34-34: getCellProps函数参数扩展

为getCellProps函数添加rowKeys参数,确保函数能够访问所有行键以正确计算行合并。


78-92: 优雅实现可展开行的行合并逻辑

这段新增代码非常关键,它解决了当表格行可展开时行合并(rowSpan)的正确计算问题。当一个单元格跨越多行且其中某些行被展开时,需要相应地增加rowSpan值以保持表格布局的正确性。

算法思路清晰:遍历原始rowSpan范围内的所有行,检查每行是否被展开,如果是则增加rowSpan值。


177-177: 正确传递行键数组参数

在渲染单元格时正确地将rowKeys参数传递给getCellProps函数,确保行合并计算能够正常工作。

Copy link

codecov bot commented May 21, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.12%. Comparing base (d696f75) to head (dc67020).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1278      +/-   ##
==========================================
+ Coverage   98.08%   98.12%   +0.03%     
==========================================
  Files          77       78       +1     
  Lines        7609     7771     +162     
  Branches     1164     1194      +30     
==========================================
+ Hits         7463     7625     +162     
  Misses        140      140              
  Partials        6        6              

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

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb0013 and 227581c.

📒 Files selected for processing (3)
  • src/Body/BodyRow.tsx (6 hunks)
  • src/Body/index.tsx (2 hunks)
  • src/Panel/index.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/Panel/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Body/index.tsx
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/Body/BodyRow.tsx

[warning] 86-86: src/Body/BodyRow.tsx#L86
Added line #L86 was not covered by tests


[warning] 88-95: src/Body/BodyRow.tsx#L88-L95
Added lines #L88 - L95 were not covered by tests

🔇 Additional comments (5)
src/Body/BodyRow.tsx (5)

25-25: 接口扩展:rowKeys 属性添加

BodyRowProps 接口中添加了 rowKeys: React.Key[] 属性,用于支持行展开时的 rowSpan 调整功能。


37-37: 函数签名更新:getCellProps 函数参数扩展

函数参数列表中添加了 rowKeys 参数,与接口更新保持一致,用于后续行展开逻辑中计算正确的 rowSpan。


51-52: 增加解构属性:expandableexpandedKeys

rowInfo 对象中解构出新增的两个属性,用于确定行是否可展开以及哪些行已被展开。


129-129: BodyRow 组件属性解构

从 props 中解构 rowKeys,为组件内部函数提供必要参数。


188-188: 传递 rowKeys 参数

在调用 getCellProps 时传入 rowKeys 参数,使得单元格能够正确计算行合并时的 rowSpan 值。

Comment on lines 81 to 95
const { rowSpan } = additionalCellProps;

// For expandable row with rowSpan,
// We should increase the rowSpan if the row is expanded
if (expandable) {
let currentRowSpan = rowSpan;

for (let i = index; i < index + rowSpan; i += 1) {
const rowKey = rowKeys[i];
if (expandedKeys.has(rowKey)) {
currentRowSpan += 1;
}
}
additionalCellProps.rowSpan = currentRowSpan;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

实现行合并与行展开的协同处理

这段代码实现了行展开时动态调整 rowSpan 的核心逻辑,但缺少测试覆盖。

    const { rowSpan } = additionalCellProps;

    // For expandable row with rowSpan,
    // We should increase the rowSpan if the row is expanded
-    if (expandable) {
+    if (expandable && rowSpan > 0) {
      let currentRowSpan = rowSpan;

      for (let i = index; i < index + rowSpan; i += 1) {
+       if (i >= rowKeys.length) break;
        const rowKey = rowKeys[i];
        if (expandedKeys.has(rowKey)) {
          currentRowSpan += 1;
        }
      }
      additionalCellProps.rowSpan = currentRowSpan;
    }

代码存在以下隐患:

  1. 没有检查 rowSpan 是否为有效值 (>0)
  2. 没有检查 i 是否超出 rowKeys 数组边界
  3. 建议为新增的复杂逻辑添加单元测试

🏁 Script executed:

#!/bin/bash
# 检查是否有相关测试文件涵盖 BodyRow 组件中的 rowSpan 功能

echo "查找与 BodyRow 相关的测试文件..."
fd -e test.tsx -e test.ts -e spec.tsx -e spec.ts | grep -i "bodyrow\|body.*row"

echo "查找测试文件中是否包含 rowSpan 相关测试..."
rg -i "rowspan.*test|test.*rowspan" --type ts --type tsx

Length of output: 288


🏁 Script executed:

#!/bin/bash
echo "查找包含 BodyRow 的测试文件..."
rg -l "BodyRow" -g "*.test.ts" -g "*.test.tsx" -g "*.spec.ts" -g "*.spec.tsx"

echo "在测试文件中查找 rowSpan 相关测试..."
rg -n "rowSpan" -g "*.test.ts" -g "*.test.tsx" -g "*.spec.ts" -g "*.spec.tsx"

Length of output: 715


校验 rowSpan 有效性并防止数组越界

src/Body/BodyRow.tsx(约 81–95 行)中,这段逻辑负责在展开行时动态调整 rowSpan,但存在以下隐患需要修复并补充测试:

  1. 未过滤 rowSpan 为 0 或负数的情况
  2. 在循环中可能读取超出 rowKeys 长度的索引
  3. 缺少针对该逻辑的单元测试

建议修改如下:

-   if (expandable) {
+   // 仅对可展开且 rowSpan > 0 的单元格生效
+   if (expandable && rowSpan > 0) {
      let currentRowSpan = rowSpan;

      for (let i = index; i < index + rowSpan; i += 1) {
+       // 防止越界访问
+       if (i >= rowKeys.length) break;
        const rowKey = rowKeys[i];
        if (expandedKeys.has(rowKey)) {
          currentRowSpan += 1;
        }
      }
      additionalCellProps.rowSpan = currentRowSpan;
    }

· 请在 tests/ 下新增 BodyRow.spec.tsx,覆盖以下场景:

  • rowSpan 为 0、1、>1
  • 不同 expandedKeys 集合下的展开行为
  • 边界情况(index + rowSpan 超出 rowKeys.length
📝 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 { rowSpan } = additionalCellProps;
// For expandable row with rowSpan,
// We should increase the rowSpan if the row is expanded
if (expandable) {
let currentRowSpan = rowSpan;
for (let i = index; i < index + rowSpan; i += 1) {
const rowKey = rowKeys[i];
if (expandedKeys.has(rowKey)) {
currentRowSpan += 1;
}
}
additionalCellProps.rowSpan = currentRowSpan;
}
const { rowSpan } = additionalCellProps;
// For expandable row with rowSpan,
// We should increase the rowSpan if the row is expanded
// 仅对可展开且 rowSpan > 0 的单元格生效
if (expandable && rowSpan > 0) {
let currentRowSpan = rowSpan;
for (let i = index; i < index + rowSpan; i += 1) {
// 防止越界访问
if (i >= rowKeys.length) break;
const rowKey = rowKeys[i];
if (expandedKeys.has(rowKey)) {
currentRowSpan += 1;
}
}
additionalCellProps.rowSpan = currentRowSpan;
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 86-86: src/Body/BodyRow.tsx#L86
Added line #L86 was not covered by tests


[warning] 88-95: src/Body/BodyRow.tsx#L88-L95
Added lines #L88 - L95 were not covered by tests

🤖 Prompt for AI Agents
In src/Body/BodyRow.tsx around lines 81 to 95, the code adjusts rowSpan when
rows are expanded but lacks validation and boundary checks. Fix this by first
verifying that rowSpan is a positive number greater than zero before proceeding.
Then, in the loop, ensure the index i does not exceed the length of the rowKeys
array to prevent out-of-bounds access. Additionally, create a new test file
tests/BodyRow.spec.tsx to add unit tests covering cases where rowSpan is 0, 1,
or greater than 1, different expandedKeys sets, and boundary conditions where
index plus rowSpan exceeds rowKeys length.

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

🧹 Nitpick comments (1)
docs/examples/expandedSticky.tsx (1)

1-58: 很好的演示了行合并与固定列结合的展开行功能

这个示例展示了表格组件的高级功能,包括:

  1. 可展开行与固定(sticky)列的结合使用
  2. 使用 rowSpan 实现单元格合并
  3. 行展开状态的管理
  4. 固定左侧和右侧列的配置

代码清晰简洁,是一个非常好的教学示例。对于用户理解如何使用这些功能非常有帮助。

不过,有几点小建议:

  1. 注释掉的代码(第10行)建议直接删除,避免混淆
  2. 可以添加更多注释说明示例的关键点,特别是关于 rowSpan 设置的逻辑
  3. 考虑添加一些更复杂的数据来更好地展示行合并的效果
-    // { title: '分割', dataIndex: 'ca' },
+    // 第一列:固定在左侧,并应用行合并
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 227581c and a63d6a7.

📒 Files selected for processing (6)
  • docs/demo/expandedSticky.md (1 hunks)
  • docs/examples/expandedSticky.tsx (1 hunks)
  • src/Body/BodyRow.tsx (10 hunks)
  • src/Body/ExpandedRow.tsx (3 hunks)
  • src/Table.tsx (1 hunks)
  • src/context/TableContext.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/context/TableContext.tsx
  • docs/demo/expandedSticky.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Body/BodyRow.tsx
🧰 Additional context used
🧠 Learnings (1)
src/Table.tsx (1)
Learnt from: bbb169
PR: react-component/table#1202
File: src/Table.tsx:903-904
Timestamp: 2024-11-08T12:53:09.293Z
Learning: 在 `src/Table.tsx` 文件的 React 组件 `Table` 中,即使 `bodyScrollLeft` 频繁更新,也需要在 `TableContextValue` 的 `useMemo` 依赖数组中包含 `bodyScrollLeft` 和 `headerCellRefs`,因为每次滚动时重新计算 `TableContextValue` 是解决该问题所必须的。
🧬 Code Graph Analysis (1)
docs/examples/expandedSticky.tsx (2)
src/interface.ts (2)
  • Key (21-21)
  • ColumnType (108-123)
tests/FixedColumn-IE.spec.jsx (1)
  • columns (27-40)
🔇 Additional comments (4)
src/Table.tsx (1)

945-946: 将 classNames 和 styles 添加到依赖数组是正确的做法

这个改动确保当 classNamesstyles 属性发生变化时,TableContextValue 会被重新计算。这对于确保表格组件样式的正确传播和更新是必要的。

根据之前的反馈,即使 bodyScrollLeft 等属性频繁更新,也需要将它们包含在 useMemo 的依赖数组中,这个变更与该指导原则一致。

src/Body/ExpandedRow.tsx (3)

17-17: 很好地添加了 offsetWidth 可选属性

ExpandedRowProps 接口添加 offsetWidth 可选属性是一个良好的设计决策,这使得组件更灵活,能够适应不同的布局需求。


34-34: 合理设置了默认值

offsetWidth 设置默认值为 0 是一个明智的选择,保持了向后兼容性,确保现有代码不会因为这个新属性而出错。


49-49: 正确调整了展开行的宽度计算

更新了宽度计算逻辑,从 componentWidth 中减去 offsetWidth,这确保了当使用 rowSpan 时展开行的宽度能够正确对齐。这个修改对于支持复杂表格布局(如带有自定义行合并的可展开行)是必要的。

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

♻️ Duplicate comments (1)
src/Body/BodyRow.tsx (1)

81-95: 💡 Verification agent

❓ Verification inconclusive

检查 rowSpan 的有效性并防止数组越界

当前实现在处理可展开行的 rowSpan 时存在以下潜在问题:

  1. 没有验证 rowSpan 是否为正数
  2. 在循环中没有边界检查,如果 index + rowSpan 超出 rowKeys 的长度,将导致越界访问

建议修改如下:

    const { rowSpan } = additionalCellProps;

    // For expandable row with rowSpan,
    // We should increase the rowSpan if the row is expanded
-   if (expandable && rowSpan !== undefined) {
+   if (expandable && rowSpan !== undefined && rowSpan > 0) {
      let currentRowSpan = rowSpan;

      for (let i = index; i < index + rowSpan; i += 1) {
+       if (i >= rowKeys.length) break;
        const rowKey = rowKeys[i];
        if (expandedKeys.has(rowKey)) {
          currentRowSpan += 1;
        }
      }
      additionalCellProps.rowSpan = currentRowSpan;
    }

根据代码覆盖率报告,这段代码没有测试覆盖。请添加单元测试来覆盖这个新功能,包括以下场景:

  • rowSpan 为 0、1 和大于 1 的情况
  • 不同 expandedKeys 集合下的展开行为
  • 边界情况(index + rowSpan 超出 rowKeys.length

请验证 rowSpan 有效性并防止数组越界

当前 src/Body/BodyRow.tsx(约第 81–95 行)在处理可展开行的 rowSpan 时存在以下风险:

  • 未校验 rowSpan 是否为正数
  • 循环到 index + rowSpan 时,若超出 rowKeys.length 会导致越界访问

建议修改如下:

    const { rowSpan } = additionalCellProps;

    // 对于可展开的行,如果 rowSpan 已定义且大于 0,
    // 展开行时应相应增加 rowSpan
-   if (expandable && rowSpan !== undefined) {
+   if (expandable && rowSpan !== undefined && rowSpan > 0) {
      let currentRowSpan = rowSpan;

      for (let i = index; i < index + rowSpan; i += 1) {
+       if (i >= rowKeys.length) break; // 防止越界
        const rowKey = rowKeys[i];
        if (expandedKeys.has(rowKey)) {
          currentRowSpan += 1;
        }
      }
      additionalCellProps.rowSpan = currentRowSpan;
    }

此外,根据最新的覆盖率报告,本段逻辑未被任何单元测试覆盖。请补充测试,至少涵盖:

  • rowSpan 为 0、1 及 > 1 的场景
  • 不同 expandedKeys 集合下的展开行为
  • 边界情况(index + rowSpan 超出 rowKeys.length
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 86-86: src/Body/BodyRow.tsx#L86
Added line #L86 was not covered by tests


[warning] 88-95: src/Body/BodyRow.tsx#L88-L95
Added lines #L88 - L95 were not covered by tests

🧹 Nitpick comments (2)
src/Body/BodyRow.tsx (2)

106-129: 优化 getOffsetData 函数实现

新增的 getOffsetData 函数在计算列偏移量时有以下几点可以改进:

  1. 在访问 column.width 时缺少类型检查
  2. 当遇到第一个没有 rowSpan 的列时就停止计算,这种行为可能不符合预期

建议修改如下:

const getOffsetData = (
  columnsData: {
    column: ColumnType<any>;
    cell: { additionalCellProps: React.TdHTMLAttributes<HTMLElement> };
  }[],
) => {
  let offsetWidth = 0;
  let offsetColumn = 0;
  let isRowSpanEnd = false;
  columnsData.forEach(item => {
    if (!isRowSpanEnd) {
      const { column, cell } = item;
      if (cell.additionalCellProps.rowSpan !== undefined) {
        offsetColumn += 1;
-       if (typeof column.width === 'number') {
+       const columnWidth = column.width;
+       if (typeof columnWidth === 'number') {
-         offsetWidth = offsetWidth + (column.width ?? 0);
+         offsetWidth = offsetWidth + columnWidth;
        }
      } else {
        isRowSpanEnd = true;
      }
    }
  });
  return { offsetWidth, offsetColumn };
};

请添加单元测试来验证这个新函数的行为,尤其是在不同列配置下的表现。

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 121-122: src/Body/BodyRow.tsx#L121-L122
Added lines #L121 - L122 were not covered by tests


180-189: 依赖数组优化建议

useMemo 中,依赖数组包含了 rowKeys,这意味着每当 rowKeys 变化时,即使其他依赖保持不变,也会重新计算 columnsDataoffsetData

根据使用场景,可以考虑是否需要在依赖数组中包含 rowKeys。如果 rowKeys 的变化频率很高,而实际上对计算结果影响不大,可以考虑将其从依赖数组中移除,或者使用其他方式优化。

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a63d6a7 and adf710c.

📒 Files selected for processing (1)
  • src/Body/BodyRow.tsx (10 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/Body/BodyRow.tsx (2)
src/interface.ts (2)
  • Key (21-21)
  • ColumnType (108-123)
src/hooks/useRowInfo.tsx (1)
  • useRowInfo (8-123)
🪛 GitHub Check: codecov/patch
src/Body/BodyRow.tsx

[warning] 86-86: src/Body/BodyRow.tsx#L86
Added line #L86 was not covered by tests


[warning] 88-95: src/Body/BodyRow.tsx#L88-L95
Added lines #L88 - L95 were not covered by tests


[warning] 121-122: src/Body/BodyRow.tsx#L121-L122
Added lines #L121 - L122 were not covered by tests

🔇 Additional comments (1)
src/Body/BodyRow.tsx (1)

10-10: 代码结构调整正确,确保文档更新

导入类型、接口扩展和参数添加的变更看起来是合理的。这些变更使得组件能够支持行合并与行展开的协同工作。

请确保更新相关文档,明确说明 rowKeys 参数的用途和要求,特别是对于 API 调用者来说,了解这个新增参数的含义和正确使用方式非常重要。

Also applies to: 25-25, 37-37, 51-52

Comment on lines +259 to 261
offsetWidth={offsetData.offsetWidth}
colSpan={flattenColumns.length - offsetData.offsetColumn}
isEmpty={false}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

确保 colSpan 不会为负值

当计算展开行的 colSpan 时,需要确保结果不会为负值:

<ExpandedRow
  expanded={expanded}
  className={cls(
    `${prefixCls}-expanded-row`,
    `${prefixCls}-expanded-row-level-${indent + 1}`,
    expandedClsName,
  )}
  prefixCls={prefixCls}
  component={RowComponent}
  cellComponent={cellComponent}
  offsetWidth={offsetData.offsetWidth}
- colSpan={flattenColumns.length - offsetData.offsetColumn}
+ colSpan={Math.max(1, flattenColumns.length - offsetData.offsetColumn)}
  isEmpty={false}
>

offsetData.offsetColumn 大于或等于 flattenColumns.length 时,计算结果可能为零或负值,这将导致意外的渲染结果。

📝 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
offsetWidth={offsetData.offsetWidth}
colSpan={flattenColumns.length - offsetData.offsetColumn}
isEmpty={false}
<ExpandedRow
expanded={expanded}
className={cls(
`${prefixCls}-expanded-row`,
`${prefixCls}-expanded-row-level-${indent + 1}`,
expandedClsName,
)}
prefixCls={prefixCls}
component={RowComponent}
cellComponent={cellComponent}
offsetWidth={offsetData.offsetWidth}
colSpan={Math.max(
1,
flattenColumns.length - offsetData.offsetColumn
)}
isEmpty={false}
>
🤖 Prompt for AI Agents
In src/Body/BodyRow.tsx around lines 259 to 261, the calculation of colSpan as
flattenColumns.length minus offsetData.offsetColumn can result in zero or
negative values if offsetData.offsetColumn is greater than or equal to
flattenColumns.length. To fix this, add a check to ensure colSpan is never less
than 1 by using a conditional or Math.max to set a minimum value of 1 for
colSpan.

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

🧹 Nitpick comments (1)
tests/Expanded.spec.tsx (1)

64-86: 考虑使用Testing Library推荐的查询方法

当前使用的直接DOM查询方法(querySelectorquerySelectorAll)虽然有效,但考虑使用Testing Library推荐的查询方法(如getByRolegetByTestId等)可能更好,这样对实现变更更具弹性。

测试断言逻辑本身非常全面,验证了:

  • 每行的单元格数量
  • 特定单元格的rowspan和colspan属性
  • 展开行的正确渲染

确保了rowspan功能在展开行存在时能正常工作。

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between adf710c and fa25174.

📒 Files selected for processing (1)
  • tests/Expanded.spec.tsx (1 hunks)
🔇 Additional comments (4)
tests/Expanded.spec.tsx (4)

1-5: 导入组件和测试工具实现得很好

导入选择恰当,包括了React、测试工具和必要的类型定义。使用@rc-component/util/lib/test/domHook来模拟DOM元素原型是测试DOM相关功能的好方法。


6-27: 测试环境设置合理且完整

测试环境设置很完善:

  • 使用vi.useFakeTimers()模拟定时器
  • 通过spyElementPrototypes模拟DOM元素属性
  • 在测试后正确清理资源

这确保了测试在可控环境中进行,提高了测试的可靠性和一致性。


28-45: 列定义和测试数据设计得当

列定义中巧妙地利用了onCell回调为不同行设置不同的rowSpan值,覆盖了多种场景:

  • rowSpan=1(正常行)
  • rowSpan=2(跨两行)
  • rowSpan=0(隐藏行)
  • rowSpan=undefined(默认值)

同时包含了Table.EXPAND_COLUMN以测试展开功能,测试数据结构简单但足够完成测试目标。


47-63: 测试用例设置和异步处理正确

测试用例设置了带有展开功能的表格,并使用了defaultExpandAllRows确保所有行都被展开。
异步处理部分使用act包装并正确运行了所有计时器,确保组件完全渲染后再进行断言。

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