Skip to content

Add inline cell editing to object-grid with batch save support#390

Merged
hotlong merged 10 commits intomainfrom
copilot/add-row-editing-to-object-grid
Feb 6, 2026
Merged

Add inline cell editing to object-grid with batch save support#390
hotlong merged 10 commits intomainfrom
copilot/add-row-editing-to-object-grid

Conversation

Copy link
Contributor

Copilot AI commented Feb 6, 2026

Implementation Plan: Object-Grid Row Editing Feature

  • Analyze existing codebase and understand requirements
  • Add row editing state management to ObjectGrid component
  • Implement inline cell editing with double-click or Enter key
  • Add editable prop handling in ObjectGrid.tsx
  • Integrate with data-table component for editing UI
  • Add onCellChange callback support
  • Add keyboard navigation for editing (Tab, Enter, Escape)
  • Update DataTableSchema to support editable cells
  • Create tests for row editing functionality
  • Update documentation with row editing examples
  • Add Storybook examples for visual testing
  • Address code review feedback (remove onBlur, improve accessibility)
  • Add multi-cell editing support with pending changes tracking
  • Implement row-level save functionality
  • Implement batch save for multiple rows
  • Add visual indicators for modified rows/cells
  • Add tests for batch editing
  • Update Storybook with batch editing example
  • Update documentation with batch editing features
  • Fix code review feedback (simplify startEdit, fix test)
  • Enhance Storybook examples with comprehensive demonstrations
  • Add clear step-by-step instructions for using save buttons in Storybook
  • Fix SchemaRendererProvider error in data-table stories
Original prompt

完善object-grid,支持启用行编辑功能


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 6, 2026 4:18am
objectui-console Ready Ready Preview, Comment Feb 6, 2026 4:18am
objectui-storybook Ready Ready Preview, Comment Feb 6, 2026 4:18am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI and others added 2 commits February 6, 2026 02:50
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@hotlong
Copy link
Contributor

hotlong commented Feb 6, 2026

@copilot 参考以下提示词的功能点进行修改,具体的细节结合现在的代码你来定。

这是一个非常清晰的开发阶段:从“只读视图”升级为“可编辑视图”。
既然你已经有了静态展示的 Grid 和现成的 Field 组件,接下来的核心工作不是写 UI,而是**“状态管理”和“条件渲染”**。你需要告诉 AI 如何把你现有的 DisplayComponent 在特定时刻替换为 EditComponent。
以下是针对“已有静态 Grid,添加编辑功能”场景优化的 AI 提示词(Prompts),按逻辑步骤排列:
第一步:状态管理与交互触发 (State & Trigger)
你需要先让表格“知道”哪个单元格正在被编辑。

提示词 1:
"我目前的 DataGrid 组件已经可以遍历数据并调用 进行静态展示。
请帮我修改 Grid 的核心代码,增加‘行内编辑’的状态管理逻辑:

  • 新增状态: 使用 useState 定义 editingCell,结构为 { rowId: string, colId: string } | null。
  • 交互事件:
    • 给每个单元格(Cell)的容器 div 绑定 onDoubleClick 事件。
    • 当用户双击某个单元格时,将该单元格的 rowId 和 colId 更新到 editingCell 状态中,从而进入编辑模式。
  • 样式标记: 当一个单元格处于编辑状态时,给它的父容器添加一个 CSS 类(如 bg-white shadow-md z-10),以便后续处理层级问题。"

第二步:条件渲染与组件桥接 (Conditional Rendering)
这是最关键的一步。你需要告诉 AI,当状态改变时,如何把你现有的“静态组件”切换成“编辑组件”。

提示词 2:
"现在根据上一步的 editingCell 状态,我们需要实现组件的动态切换。
请修改单元格的渲染逻辑(renderCell):

  • 条件判断: 检查当前渲染的单元格是否匹配 editingCell 的坐标。
  • 切换逻辑:
    • 默认情况(False): 保持原样,渲染 。
    • 编辑情况(True): 渲染对应的编辑组件 (或者你现有的编辑组件)。
  • Props 传递:
    • 必须将当前单元格的 value 传给编辑组件作为初始值。
    • 传递一个 onCommit(newValue) 回调函数给编辑组件,用于后续保存。
    • 传递 onCancel() 回调,用于退出编辑状态(将 editingCell 设为 null)。
  • 自动聚焦: 确保当编辑组件挂载(Mount)时,内部的 input/select 自动获得焦点(autoFocus)。"

第三步:数据保存与全流程闭环 (Save & Feedback)
让 AI 帮你处理从组件回调到 API 调用的过程。

提示词 3:
"接上一步,请帮我实现 DataGrid 组件中的核心保存函数 handleCommit。
功能要求:

  • 乐观更新(Optimistic UI): 当收到子组件传出来的 newValue 时,立即更新本地的 Grid dataSource 状态,让用户感觉修改是瞬间完成的,并立即把 editingCell 设为 null 退出编辑模式。
  • 后台同步: 在更新 UI 后,异步调用 updateRecord(rowId, colId, value) API。
  • 错误回滚: 如果 API 请求失败(Catch block),请弹出一个 Toast 错误提示,并将该单元格的值回滚到修改前的状态。
  • 键盘支持: 请给 Grid 添加全局键盘监听,当处于编辑模式时:
    • 按 Esc:触发 onCancel。
    • 按 Enter:触发 onCommit(注意:如果是 Textarea 或多选框可能需要排除 Enter 键)。"

第四步:Airtable 风格的细节优化 (Airtable Polish)
如果你希望体验更像 Airtable,可以加这个可选的提示词。

提示词 4(可选):
"为了达到 Airtable 的体验,请优化编辑器的样式容器:

  • 无缝切换: 确保编辑组件的大小、内边距(Padding)与静态显示的组件完全一致,避免进入编辑模式时内容发生‘跳动’或位移。
  • 点击外部保存: 引入 useClickOutside 钩子。当用户正在编辑时,如果点击了表格外部或其他单元格,视为‘确认保存’(触发 handleCommit),而不是取消。"

总结开发流程

  • 先用 提示词 1 让 Grid 拥有“记录谁在编辑”的大脑。
  • 再用 提示词 2 实现“由于我在编辑,所以显示 Input 而不是 Span”的视觉切换。
  • 最后用 提示词 3 确保“改了数据能存进去,存不进去能退回来”。
    这种方式可以最大程度利用你现有的 fields 组件代码,只需编写少量的胶水代码即可完成升级。

Copy link
Contributor Author

Copilot AI commented Feb 6, 2026

@copilot 参考以下提示词的功能点进行修改,具体的细节结合现在的代码你来定。

...

The comment appears to be cut off. Could you please provide the complete prompt/requirements? I can see it starts with "提示词 1:我目前的 DataGrid 组件已经可以遍历数据并调用" but the rest is missing.

What specific changes would you like me to make to the current implementation?

@hotlong hotlong marked this pull request as ready for review February 6, 2026 03:22
Copilot AI review requested due to automatic review settings February 6, 2026 03:22
@hotlong
Copy link
Contributor

hotlong commented Feb 6, 2026

@copilot 简单地说,在快捷编辑的模式下,需要考虑多个字段的编辑以及数据保存,包括单行的保存和多行的批量保存。

Copy link
Contributor

Copilot AI 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 overview

This PR implements inline cell editing for object-grid and data-table components, enabling spreadsheet-like editing with keyboard navigation. It adds global and per-column editable controls with callbacks for handling cell value changes.

Changes:

  • Added editable boolean flag and onCellChange callback to type schemas for both global and column-level control
  • Implemented inline editing in data-table renderer with Enter/Escape keyboard shortcuts and double-click activation
  • Integrated editing support into object-grid component by passing through the new props
  • Added comprehensive documentation and Storybook examples demonstrating the editing features

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/types/src/data-display.ts Added editable property to TableColumn and DataTableSchema, plus onCellChange and onRowClick callbacks
packages/components/src/renderers/complex/data-table.tsx Implemented cell editing state management, keyboard handlers, and edit mode rendering with Input component
packages/plugin-grid/src/ObjectGrid.tsx Updated to pass editable and onCellChange props through to underlying data-table
packages/plugin-grid/README.md Added "Inline Editing" section with usage examples and feature documentation
content/docs/plugins/plugin-grid.mdx Added documentation for inline editing feature with JSON examples
packages/components/src/stories-json/object-grid.stories.tsx Added EditableGrid story demonstrating inline editing capabilities
packages/components/src/stories-json/data-table.stories.tsx Added EditableTable story with example data and onCellChange handler
packages/components/src/renderers/complex/tests/data-table-editing.test.tsx Added comprehensive test suite covering edit mode activation, saving, canceling, and column-level control

}
}
});
});
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Missing test coverage for activating edit mode by pressing Enter on a focused cell. The implementation supports this feature (line 388-392 in data-table.tsx), but there's no test to verify it works correctly. Consider adding a test that focuses a cell and presses Enter to ensure edit mode is activated.

Copilot uses AI. Check for mistakes.
ref={editInputRef}
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleEditKeyDown}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

When a cell is in edit mode, clicking on the input field or the cell itself will trigger the onRowClick handler (if defined). This is likely unintended behavior - clicking within an editing cell should not trigger row click events. Consider adding logic to check if a cell is currently being edited (similar to how the code checks for buttons and checkboxes on lines 570-571) or adding e.stopPropagation() to the input's onClick handler.

Suggested change
onKeyDown={handleEditKeyDown}
onKeyDown={handleEditKeyDown}
onClick={(e) => e.stopPropagation()}

Copilot uses AI. Check for mistakes.
Comment on lines +361 to +363
// Don't save if we're in cancelled state (unless forced)
if (!force && editingCell === null) return;

Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

This condition is unreachable and redundant. Line 359 already returns early if editingCell is null, so line 362 checking editingCell === null will never evaluate to true. This appears to be dead code that should be removed.

Suggested change
// Don't save if we're in cancelled state (unless forced)
if (!force && editingCell === null) return;

Copilot uses AI. Check for mistakes.
}}
onDoubleClick={() => isEditable && startEdit(rowIndex, col.accessorKey, cellValue)}
onKeyDown={(e) => handleCellKeyDown(e, rowIndex, col.accessorKey)}
tabIndex={0}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

All table cells receive tabIndex={0} regardless of whether they are editable or not. This creates unnecessary tab stops and degrades keyboard navigation accessibility. The tabIndex should only be set conditionally when a cell is actually editable (when isEditable is true). Non-editable cells, including the ID column with editable: false, should not be in the tab order.

Suggested change
tabIndex={0}
tabIndex={isEditable ? 0 : undefined}

Copilot uses AI. Check for mistakes.
ref={editInputRef}
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleEditKeyDown}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The edit input is missing an onBlur handler to exit edit mode when the user clicks or tabs away from the cell. Currently, users can only save (Enter) or cancel (Escape) the edit. Without an onBlur handler, clicking outside the input cell leaves it in edit mode, which is inconsistent with standard spreadsheet behavior. Consider adding an onBlur handler that either auto-saves or cancels the edit based on the desired UX (the PR description mentions "No auto-save on blur" but there's no blur handler at all currently).

Suggested change
onKeyDown={handleEditKeyDown}
onKeyDown={handleEditKeyDown}
onBlur={() => {
// Exit edit mode on blur without auto-saving
setEditingCell(null);
setEditValue('');
}}

Copilot uses AI. Check for mistakes.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable container.

Suggested change
const { container } = render(<DataTableRenderer schema={schema} />);
render(<DataTableRenderer schema={schema} />);

Copilot uses AI. Check for mistakes.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable container.

Copilot uses AI. Check for mistakes.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable container.

Copilot uses AI. Check for mistakes.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

Unused variable container.

Copilot uses AI. Check for mistakes.
Copilot AI and others added 2 commits February 6, 2026 03:31
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
if (!editingCell) return;

// Don't save if we're in cancelled state (unless forced)
if (!force && editingCell === null) return;

Check warning

Code scanning / CodeQL

Comparison between inconvertible types Warning

Variable 'editingCell' cannot be of type null, but it is compared to
an expression
of type null.

Copilot Autofix

AI about 9 hours ago

In general, when a variable is already guarded against being null or undefined, performing an additional === null comparison inside the guarded block is both redundant and misleading. It can also trigger static analysis warnings about comparing inconvertible or impossible types.

The best fix here is to remove the logically impossible check if (!force && editingCell === null) return; from saveEdit. The function already exits early if editingCell is falsy (null or undefined), so any code that follows can safely assume editingCell is non-null. Removing this condition does not alter existing, reachable functionality and cleans up the control flow.

Concretely:

  • In packages/components/src/renderers/complex/data-table.tsx, inside the saveEdit function:
    • Delete the block:

      // Don't save if we're in cancelled state (unless forced)
      if (!force && editingCell === null) return;

No additional imports, methods, or definitions are needed.

Suggested changeset 1
packages/components/src/renderers/complex/data-table.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/components/src/renderers/complex/data-table.tsx b/packages/components/src/renderers/complex/data-table.tsx
--- a/packages/components/src/renderers/complex/data-table.tsx
+++ b/packages/components/src/renderers/complex/data-table.tsx
@@ -368,9 +368,6 @@
   const saveEdit = (force: boolean = false) => {
     if (!editingCell) return;
     
-    // Don't save if we're in cancelled state (unless forced)
-    if (!force && editingCell === null) return;
-    
     const { rowIndex, columnKey } = editingCell;
     const globalIndex = (currentPage - 1) * pageSize + rowIndex;
     const row = sortedData[globalIndex];
EOF
@@ -368,9 +368,6 @@
const saveEdit = (force: boolean = false) => {
if (!editingCell) return;

// Don't save if we're in cancelled state (unless forced)
if (!force && editingCell === null) return;

const { rowIndex, columnKey } = editingCell;
const globalIndex = (currentPage - 1) * pageSize + rowIndex;
const row = sortedData[globalIndex];
Copilot is powered by AI and may make mistakes. Always verify output.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable container.

Copilot Autofix

AI about 9 hours ago

In general, unused variables in tests should be removed or replaced with a destructuring that omits them, to improve clarity and satisfy static analysis tools. Here, in the second it('should call onCellChange when cell value is edited', ...) block, we destructure { container } from the render call but never use it.

The best fix without changing existing functionality is to call render without capturing container in this second test. We can simply remove the destructuring and call render(<DataTableRenderer schema={schema} />);. This change is local to the second test, at line 71 in packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx. No new imports or helper methods are needed.

Suggested changeset 1
packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
--- a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
+++ b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
@@ -68,7 +68,7 @@
     const DataTableRenderer = ComponentRegistry.get('data-table');
     if (!DataTableRenderer) throw new Error('DataTableRenderer not found');
 
-    const { container } = render(<DataTableRenderer schema={schema} />);
+    render(<DataTableRenderer schema={schema} />);
     
     // Find the first editable cell (name column)
     const nameCell = screen.getByText('John Doe').closest('td');
EOF
@@ -68,7 +68,7 @@
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
render(<DataTableRenderer schema={schema} />);

// Find the first editable cell (name column)
const nameCell = screen.getByText('John Doe').closest('td');
Copilot is powered by AI and may make mistakes. Always verify output.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable container.

Copilot Autofix

AI about 9 hours ago

In general, to fix unused variable issues, either remove the variable (and its binding) or start using it meaningfully. Here, the best fix that does not change existing behavior is to stop destructuring container from the render result in the second test, since the test does not use it.

Concretely, in packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx, locate the line within the "should not allow editing when editable is false" test:

const { container } = render(<DataTableRenderer schema={schema} />);

Replace it with a call to render that does not bind the result to container, e.g.:

render(<DataTableRenderer schema={schema} />);

This preserves the render side effect while eliminating the unused variable. No new imports, methods, or definitions are required.

Suggested changeset 1
packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
--- a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
+++ b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
@@ -118,7 +118,7 @@
     const DataTableRenderer = ComponentRegistry.get('data-table');
     if (!DataTableRenderer) throw new Error('DataTableRenderer not found');
 
-    const { container } = render(<DataTableRenderer schema={schema} />);
+    render(<DataTableRenderer schema={schema} />);
     
     // Find a cell
     const nameCell = screen.getByText('John Doe').closest('td');
EOF
@@ -118,7 +118,7 @@
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
render(<DataTableRenderer schema={schema} />);

// Find a cell
const nameCell = screen.getByText('John Doe').closest('td');
Copilot is powered by AI and may make mistakes. Always verify output.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable container.

Copilot Autofix

AI about 9 hours ago

In general, to fix an unused variable reported by static analysis, either remove the variable (and any destructuring pattern that binds it) or start using it meaningfully. Here, the test does not need container at all: all queries are done via screen and DOM traversal from specific cells. The simplest and safest fix is to stop destructuring container from the render call and instead just call render without capturing its return value.

Concretely, in packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx, within the it('should respect column-level editable flag', ...) block, replace const { container } = render(<DataTableRenderer schema={schema} />); with a plain render(<DataTableRenderer schema={schema} />);. No imports or additional definitions are required, and no behavior changes, since container was never read.

Suggested changeset 1
packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
--- a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
+++ b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
@@ -150,7 +150,7 @@
     const DataTableRenderer = ComponentRegistry.get('data-table');
     if (!DataTableRenderer) throw new Error('DataTableRenderer not found');
 
-    const { container } = render(<DataTableRenderer schema={schema} />);
+    render(<DataTableRenderer schema={schema} />);
     
     // Try to edit ID column (which has editable: false)
     const idCell = screen.getByText('1').closest('td');
EOF
@@ -150,7 +150,7 @@
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
render(<DataTableRenderer schema={schema} />);

// Try to edit ID column (which has editable: false)
const idCell = screen.getByText('1').closest('td');
Copilot is powered by AI and may make mistakes. Always verify output.
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable container.

Copilot Autofix

AI about 9 hours ago

To fix the problem, remove the unused container variable from the test so that CodeQL no longer reports it and the code is cleaner. The simplest and safest change is to call render without assigning its return value in this test, since none of the returned helpers are used.

Concretely, in packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx, in the "should cancel edit on Escape key" test, replace:

const { container } = render(<DataTableRenderer schema={schema} />);

with:

render(<DataTableRenderer schema={schema} />);

No additional imports or definitions are needed, and this does not alter existing functionality because container was not used.

Suggested changeset 1
packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
--- a/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
+++ b/packages/components/src/renderers/complex/__tests__/data-table-editing.test.tsx
@@ -181,7 +181,7 @@
     const DataTableRenderer = ComponentRegistry.get('data-table');
     if (!DataTableRenderer) throw new Error('DataTableRenderer not found');
 
-    const { container } = render(<DataTableRenderer schema={schema} />);
+    render(<DataTableRenderer schema={schema} />);
     
     // Find the first editable cell
     const nameCell = screen.getByText('John Doe').closest('td');
EOF
@@ -181,7 +181,7 @@
const DataTableRenderer = ComponentRegistry.get('data-table');
if (!DataTableRenderer) throw new Error('DataTableRenderer not found');

const { container } = render(<DataTableRenderer schema={schema} />);
render(<DataTableRenderer schema={schema} />);

// Find the first editable cell
const nameCell = screen.getByText('John Doe').closest('td');
Copilot is powered by AI and may make mistakes. Always verify output.
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 6, 2026

@copilot 更新storybook中的例子

已更新 Storybook 示例 (commit 9ac7575)!

data-table.stories.tsx:

  • EditableTable: 简单的单元格编辑示例
  • BatchEditTable: 新增!展示批量编辑、行级保存和批量保存功能

object-grid.stories.tsx:

  • EditableGrid: 优化了基础编辑示例,添加了第4行数据和更清晰的回调提示
  • BatchEditGrid: 增强了批量编辑示例,添加了 category 字段,更详细的保存提示
  • AdvancedBatchEdit: 新增!完整的库存管理示例,包含8条数据、分页、更多字段(reorderLevel)

所有示例都包含详细的 JSDoc 说明和实际的保存回调演示。

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1775.99KB 419.42KB
core (index.js) 0.63KB 0.30KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 21.35KB 5.35KB
fields (index.js) 87.61KB 17.17KB
layout (index.js) 12.91KB 3.97KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 31.31KB 8.02KB
plugin-charts (AdvancedChartImpl-DcIHnCct.js) 124.69KB 25.93KB
plugin-charts (BarChart-C_I0OFbj.js) 542.77KB 135.30KB
plugin-charts (ChartImpl-CU5lEzui.js) 3.17KB 1.10KB
plugin-charts (index-DgxI83zT.js) 14.58KB 4.36KB
plugin-charts (index.js) 0.19KB 0.16KB
plugin-chatbot (index.js) 1200.27KB 344.58KB
plugin-dashboard (index.js) 212.71KB 49.09KB
plugin-detail (index.js) 1673.68KB 395.78KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 15.35KB 4.96KB
plugin-gantt (index.js) 193.61KB 42.55KB
plugin-grid (index.js) 40.99KB 11.35KB
plugin-kanban (KanbanEnhanced-BqDEu7Z6.js) 31.43KB 9.03KB
plugin-kanban (KanbanImpl-B8nu2BvG.js) 5.28KB 1.91KB
plugin-kanban (index-CrR06na7.js) 17.02KB 4.99KB
plugin-kanban (index.js) 0.27KB 0.17KB
plugin-kanban (sortable.esm-ZHwgFQIO.js) 71.42KB 18.98KB
plugin-list (index.js) 1681.82KB 397.76KB
plugin-map (index.js) 126.03KB 30.49KB
plugin-map (maplibre-gl-CNsW26De.js) 1418.32KB 302.53KB
plugin-markdown (MarkdownImpl-DufQ-eRU.js) 256.68KB 64.45KB
plugin-markdown (index-CrmE78vF.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 105.00KB 24.12KB
plugin-view (index.js) 16.69KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.28KB 1.30KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@hotlong
Copy link
Contributor

hotlong commented Feb 6, 2026

@copilot storybook 中没有看到保存按钮和功能

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 6, 2026

@copilot storybook 中没有看到保存按钮和功能

保存按钮是动态显示的,需要先编辑单元格才会出现 (commit 5830eff)。

如何查看保存按钮:

  1. 双击任意可编辑单元格(除了 ID/SKU 等标记为 editable: false 的列)
  2. 按 Enter 保存编辑 - 此时单元格会变成琥珀色高亮
  3. 观察以下变化:
    • 修改的行背景变为琥珀色
    • 修改的单元格显示为粗体琥珀色文字
    • Actions 列出现绿色 ✓ 保存按钮和 X 取消按钮
    • 顶部工具栏出现 "X rows modified" 以及 "Cancel All" 和 "Save All (X)" 按钮

已在所有批量编辑示例的 JSDoc 中添加了详细的步骤说明:

  • BatchEditGrid: 包含 6 步完整使用说明
  • AdvancedBatchEdit: 添加了使用指南
  • BatchEditTable: 在 caption 中添加了提示

这是正确的行为设计 - 保存按钮只在有待保存的更改时才显示,避免界面混乱。

@hotlong
Copy link
Contributor

hotlong commented Feb 6, 2026

@copilot

Error: useSchemaContext must be used within a SchemaRendererProvider
at wS (/assets/preview-DLidl5Ap.js:336:20239)
at Rf (/assets/client-DyyAJM6c.js:8:47842)
at ec (/assets/client-DyyAJM6c.js:8:70513)
at e1 (/assets/client-DyyAJM6c.js:8:116345)
at Rm (/assets/client-DyyAJM6c.js:8:115427)
at oc (/assets/client-DyyAJM6c.js:8:115265)
at Iv (/assets/client-DyyAJM6c.js:8:112105)
at s1 (/assets/client-DyyAJM6c.js:8:123742)
at _t (/assets/client-DyyAJM6c.js:1:1

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 6, 2026

@copilot

Error: useSchemaContext must be used within a SchemaRendererProvider...

已修复 (commit 33c9eab)!

问题是 data-table.stories.tsx 缺少 SchemaRendererProvider 包装器。已更新所有 data-table 故事使用与 object-grid 故事相同的渲染方式:

import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
import { createStorybookDataSource } from '@storybook-config/datasource';

const dataSource = createStorybookDataSource();

const renderStory = (args: any) => (
  <SchemaRendererProvider dataSource={dataSource}>
    <SchemaRenderer schema={args as unknown as BaseSchema} />
  </SchemaRendererProvider>
);

所有故事现在都使用 render: renderStory 而不是直接渲染 SchemaRenderer。这解决了 useSchemaContext 错误。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants