Skip to content

feat: support column.resizable #1106

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 5 commits into
base: master
Choose a base branch
from

Conversation

linxianxi
Copy link
Contributor

@linxianxi linxianxi commented Apr 8, 2024

原先内部 onColumnResize 重命名为 onColumnWidthChange,进行区分

Summary by CodeRabbit

  • 新功能
    • 表格列支持可调整宽度(resizable),新增列宽调整完成事件回调,提供被调整列及所有列的最新宽度。
    • 表头单元格新增可调整宽度的交互支持,支持最小宽度限制和左右方向适配。
    • 新增切换表格布局方向(RTL)的示例,展示列宽调整功能。
  • 样式
    • 增加列宽调整时的视觉样式,包括拖拽手柄和调整指示线,优化固定列阴影的交互体验。
  • 文档
    • 更新 README,新增 resizable 属性说明。
  • 测试
    • 新增列宽可调整功能测试,覆盖宽度变化、最小宽度限制及宽度调整事件回调验证。

Copy link

vercel bot commented Apr 8, 2024

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 Jun 21, 2025 1:23pm

@zombieJ
Copy link
Member

zombieJ commented Apr 23, 2024

体验有点怪异,如果是超出 width 的时候缩小应该就是缩小,不应该把右边的撑开才对。

@zombieJ
Copy link
Member

zombieJ commented May 10, 2024

看起来好像没对齐

截屏2024-05-10 13 56 36

Copy link

vercel bot commented May 15, 2025

@linxianxi is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

@linxianxi linxianxi requested a review from afc163 May 15, 2025 01:48
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 (10)
docs/examples/column-resize.tsx (7)

23-30: 列定义结构良好但可进一步优化

列定义清晰,使用.map()添加resizable属性和动态宽度的方式很灵活。不过,fallback逻辑widthMap.get(i.key ?? i.dataIndex) ?? i.width可能在某些情况下需要额外处理(如宽度值为非数字类型时)。

考虑添加类型保护,确保宽度总是数值类型:

-  width: widthMap.get(i.key ?? i.dataIndex) ?? i.width,
+  width: Number(widthMap.get(i.key ?? i.dataIndex) ?? i.width) || i.width,

32-49: 固定列配置合理但默认宽度处理不一致

固定列的配置很完善,包含了左固定和右固定列,以及最小宽度限制。但与第一个columns配置相比,这里添加了额外的默认宽度?? 150,两处的fallback逻辑不一致。虽然不影响功能,但维护时可能引起困惑。

考虑统一两处的默认宽度处理逻辑,或添加注释说明不同之处的原因。

-  width: widthMap.get(i.key ?? i.dataIndex) ?? i.width ?? 150,
+  // 固定列需要确保有默认宽度以避免布局问题
+  width: widthMap.get(i.key ?? i.dataIndex) ?? i.width ?? 150,

53-53: UI文本混合了中英文

在示例代码中混合中英文可能会对不懂中文的开发者造成困扰。考虑使用一种语言(英文)来保持示例的一致性和可访问性。

-      table width: 800px {'columns=[{width: 100, width: 100}]'} 情况
+      table width: 800px {'columns=[{width: 100, width: 100}]'} case

59-67: 重复的状态更新逻辑可以提取

onColumnResizeEnd回调在两个表格中有完全相同的实现。可以考虑将此逻辑提取为一个单独的函数,以减少代码重复并简化后续维护。

+ const handleColumnResizeEnd = ({ columnWidths }) => {
+   setWidthMap(prev => {
+     const result = new Map(prev);
+     columnWidths.forEach(i => {
+       result.set(i.columnKey, i.width);
+     });
+     return result;
+   });
+ };

  <Table
    // ...其他属性
-   onColumnResizeEnd={({ columnWidths }) => {
-     setWidthMap(prev => {
-       const result = new Map(prev);
-       columnWidths.forEach(i => {
-         result.set(i.columnKey, i.width);
-       });
-       return result;
-     });
-   }}
+   onColumnResizeEnd={handleColumnResizeEnd}
    // ...其他属性
  />

69-76: 容器宽度计算逻辑可以提取

getContainerWidth函数在两个表格中有完全相同的实现。建议将此逻辑提取为一个单独的函数,以减少代码重复。

+ const getTableContainerWidth = (ele, width) => {
+   // Minus border
+   const borderWidth = getComputedStyle(
+     ele.querySelector('.rc-table-body'),
+   ).borderInlineStartWidth;
+   const mergedWidth = width - parseInt(borderWidth, 10);
+   return mergedWidth;
+ };

  <Table
    // ...其他属性
-   getContainerWidth={(ele, width) => {
-     // Minus border
-     const borderWidth = getComputedStyle(
-       ele.querySelector('.rc-table-body'),
-     ).borderInlineStartWidth;
-     const mergedWidth = width - parseInt(borderWidth, 10);
-     return mergedWidth;
-   }}
+   getContainerWidth={getTableContainerWidth}
    // ...其他属性
  />

90-98: 与上面相同,函数逻辑重复

这与第一个表格中的onColumnResizeEnd回调完全相同,应该提取为一个共享函数。

  <Table
    // ...其他属性
-   onColumnResizeEnd={({ columnWidths }) => {
-     setWidthMap(prev => {
-       const result = new Map(prev);
-       columnWidths.forEach(i => {
-         result.set(i.columnKey, i.width);
-       });
-       return result;
-     });
-   }}
+   onColumnResizeEnd={handleColumnResizeEnd}
    // ...其他属性
  />

100-107: 与上面相同,函数逻辑重复

这与第一个表格中的getContainerWidth函数完全相同,应该提取为一个共享函数。

  <Table
    // ...其他属性
-   getContainerWidth={(ele, width) => {
-     // Minus border
-     const borderWidth = getComputedStyle(
-       ele.querySelector('.rc-table-body'),
-     ).borderInlineStartWidth;
-     const mergedWidth = width - parseInt(borderWidth, 10);
-     return mergedWidth;
-   }}
+   getContainerWidth={getTableContainerWidth}
    // ...其他属性
  />
tests/Resizable.spec.tsx (3)

45-55: 元素属性模拟设置适当

使用spyElementPrototype来模拟DOM元素属性是很好的实践,确保测试环境的一致性。不过,硬编码的宽度值(如offsetWidth为800和400)可能会限制测试的灵活性。

考虑使用变量来定义这些宽度值,使它们在测试中更容易调整:

+ const CONTAINER_WIDTH = 800;
+ const CELL_WIDTH = 400;

  containerSpy = spyElementPrototype(HTMLDivElement, 'offsetWidth', {
-   get: () => 800,
+   get: () => CONTAINER_WIDTH,
  });
  measureCellSpy = spyElementPrototype(HTMLTableCellElement, 'offsetWidth', {
-   get: () => 400,
+   get: () => CELL_WIDTH,
  });

130-174: 总宽度小于组件宽度的场景测试

此测试验证了当列的总宽度小于组件宽度时的调整行为,特别是验证了相邻列宽度的自动调整。测试中假设滚动条宽度为15px,这可能在不同环境中有所不同。

建议将滚动条宽度定义为常量,并添加注释说明这是一个假设值:

+ // 假设标准滚动条宽度为15px
+ const SCROLLBAR_WIDTH = 15;

  expect(onColumnResizeEnd).toHaveBeenCalledWith({
    columnKey: 'a',
    width: 300,
    columnWidths: [
      { columnKey: 'a', width: 300 },
-     { columnKey: 'b', width: 485 },
+     { columnKey: 'b', width: 800 - 300 - SCROLLBAR_WIDTH },
      // scrollbar 15px
    ],
  });

1-221: 测试覆盖全面但可考虑添加更多场景

测试文件整体设计合理,覆盖了调整列宽的核心功能。不过,可以考虑添加以下测试场景:

  1. RTL模式下的列宽调整行为
  2. 带有嵌套列头的表格调整行为
  3. 调整过程中的事件(如onColumnResizing而非仅测试onColumnResizeEnd
  4. 边缘情况处理,如快速连续调整多列

另外,建议将重复的App组件逻辑提取为一个辅助函数或使用测试上下文共享,可以减少测试代码的重复。

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ada35d2 and 7331c5b.

📒 Files selected for processing (16)
  • README.md (1 hunks)
  • assets/index.less (3 hunks)
  • docs/examples/column-resize.tsx (1 hunks)
  • package.json (0 hunks)
  • src/Body/MeasureCell.tsx (1 hunks)
  • src/Body/MeasureRow.tsx (2 hunks)
  • src/Body/index.tsx (3 hunks)
  • src/FixedHolder/index.tsx (1 hunks)
  • src/Header/HeaderCell.tsx (1 hunks)
  • src/Header/HeaderRow.tsx (3 hunks)
  • src/Header/useCellResize.tsx (1 hunks)
  • src/Table.tsx (9 hunks)
  • src/VirtualTable/BodyGrid.tsx (3 hunks)
  • src/context/TableContext.tsx (2 hunks)
  • src/interface.ts (2 hunks)
  • tests/Resizable.spec.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (13)
  • src/VirtualTable/BodyGrid.tsx
  • src/Body/index.tsx
  • src/FixedHolder/index.tsx
  • src/interface.ts
  • README.md
  • src/Header/HeaderCell.tsx
  • src/Body/MeasureCell.tsx
  • src/Body/MeasureRow.tsx
  • assets/index.less
  • src/Header/HeaderRow.tsx
  • src/context/TableContext.tsx
  • src/Table.tsx
  • src/Header/useCellResize.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
docs/examples/column-resize.tsx (2)
src/interface.ts (2)
  • Key (21-21)
  • ColumnType (108-124)
docs/examples/utils/useInput.ts (1)
  • useCheckbox (3-18)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test / react component workflow
🔇 Additional comments (5)
docs/examples/column-resize.tsx (1)

19-21: 钩子状态声明符合最佳实践

使用useState钩子和Map类型来管理列宽的状态非常合适,这样能够高效地追踪和更新每列的宽度。代码结构清晰,使用RTL布局切换功能也很实用。

tests/Resizable.spec.tsx (4)

7-12: 触发Resize事件的辅助函数实现良好

triggerResize辅助函数的实现符合测试最佳实践,使用act包装异步操作,并等待Promise解析,确保测试的可靠性。


14-38: 鼠标事件模拟函数设计合理

doMouseMove函数很好地模拟了拖动操作的完整生命周期(mouseDown, mouseMove, mouseUp),包括自定义事件属性的定义。逻辑清晰,参数灵活,支持可选的mouseUp事件。


67-128: 宽度变更测试用例实现完整

此测试用例完整验证了列宽调整功能,包括调整一列后对onColumnResizeEnd回调的验证,以及更新测量单元格宽度后再次调整另一列的验证。测试流程和断言都很完善。


176-220:

❓ Verification inconclusive

最小宽度约束测试实现正确

此测试验证了minWidth属性的工作方式,确保列宽不会小于指定的最小值。测试模拟了尝试将列宽调整到最小值以下的情况,并验证了结果约束在最小宽度。同样,这里也使用了15px的滚动条宽度假设。

考虑验证更多的边缘情况,例如:

  1. 当所有列都设置了最小宽度时,是否会影响调整行为
  2. 当表格宽度小于所有列最小宽度总和时的行为

🏁 Script executed:

#!/bin/bash
# 查找源代码中对于最小宽度处理的逻辑
echo "查找最小宽度处理相关代码..."
rg -n "minWidth" --type ts --type tsx src/

Length of output: 244


🏁 Script executed:

#!/bin/bash
echo "重新搜索 minWidth 相关逻辑..."
rg -n "minWidth" -g "*.ts*" src/

Length of output: 869


最小宽度约束测试实现正确

验证发现:

  • src/Header/useCellResize.tsx 中,第 64 行已实现 if (newWidth < minWidth) newWidth = minWidth; 的约束逻辑。
  • minWidth 属性在接口声明(src/interface.ts:119)、ColGroup.tsxHeaderCell.tsxHeaderRow.tsx 等多处正确传递与应用,确保渲染时会生效。

因此当前测试成功覆盖了当拖拽使列宽低于 minWidth 时自动约束至最小宽度的场景,行为符合预期。

请考虑补充以下边缘用例的测试:

  • 所有列均设置 minWidth 时,拖拽对其他列宽度的影响
  • 表格整体宽度小于所有列最小宽度总和时的渲染与调整表现

Copy link

socket-security bot commented May 18, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License

View full report

@verbetchii
Copy link

Hello
I hope you're doing well. I wanted to check in on the status of this PR. I'm really excited about this feature and was wondering if there's any rough estimate on when it might be delivered

@akashraj9828
Copy link

I am following this as I am also waiting for this feature.

@KresimirJurkovic
Copy link

Same here

@joebnb
Copy link

joebnb commented Jun 6, 2025

some one ship it out

@XuJin186
Copy link

I am following this as I am also waiting for this feature.

@FaberSober
Copy link

waiting for it😊

@yoyo837
Copy link
Member

yoyo837 commented Jun 21, 2025

重构table搁置了,处理一下冲突推进得了。

@XuJin186
Copy link

重构table搁置了,处理一下冲突推进得了。

那敢情好啊

@XuJin186
Copy link

waiting for it😊

4 similar comments
@XuJin186
Copy link

waiting for it😊

@XuJin186
Copy link

waiting for it😊

@AmirrezaAsadpour
Copy link

waiting for it😊

@XuJin186
Copy link

XuJin186 commented Jul 2, 2025

waiting for it😊

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.