From 49fbbdf9db6d50601b31c5f7b28eaab0302435c5 Mon Sep 17 00:00:00 2001
From: John Betancur <1385932+jbetancur@users.noreply.github.com>
Date: Sat, 18 Jul 2026 19:07:45 -0400
Subject: [PATCH 1/3] Add ctx.error and ctx.inputRef to custom cell editors,
live validation/server-save demos (#1356)
Custom editors couldn't see validation errors or get the same focus handling
as built-in editors. Also adds live demos for validate and optimistic
server-side saves with rollback to the inline editing docs.
Closes #1355
---
CHANGELOG.md | 9 ++
apps/docs/public/llms.txt | 2 +-
.../components/demos/InlineEditingDemo.tsx | 13 ++-
.../components/demos/ServerSideEditDemo.tsx | 93 +++++++++++++++++++
apps/docs/src/pages/docs/api.astro | 2 +
apps/docs/src/pages/docs/inline-editing.astro | 87 ++++++++++++++++-
src/__tests__/inlineEditing.test.tsx | 74 +++++++++++++++
src/components/CellEditor.tsx | 3 +
src/hooks/useCellEdit.ts | 9 +-
src/types.ts | 4 +
10 files changed, 285 insertions(+), 11 deletions(-)
create mode 100644 apps/docs/src/components/demos/ServerSideEditDemo.tsx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eccbdfef..db2ba88f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
A summary of notable changes per release. For the full commit history see the [repository on GitHub](https://github.com/jbetancur/react-data-table-component/commits/master).
+## 8.7.0
+
+### New features
+
+- **`ctx.error` for custom editors** — the custom editor render context now includes the current validation error (`string | null`), so custom editors can style their own invalid state when `validate` rejects a commit. → [Inline editing](/docs/inline-editing) ([#1355](https://github.com/jbetancur/react-data-table-component/issues/1355))
+- **`ctx.inputRef` for custom editors** — attach it as the `ref` of your focusable element to get auto-focus when the editor opens and refocus after a rejected commit, matching the built-in editors. → [Inline editing](/docs/inline-editing) ([#1355](https://github.com/jbetancur/react-data-table-component/issues/1355))
+
+---
+
## 8.6.2
### Bug fixes
diff --git a/apps/docs/public/llms.txt b/apps/docs/public/llms.txt
index b332f946..fb2c5d07 100644
--- a/apps/docs/public/llms.txt
+++ b/apps/docs/public/llms.txt
@@ -49,7 +49,7 @@ const columns = [
- [Fixed Header](https://reactdatatable.com/docs/fixed-header): sticky header with scrollable body; when to disable the responsive scroll container
- [Footer](https://reactdatatable.com/docs/footer): summary and pagination footers
- [Loading State](https://reactdatatable.com/docs/loading): progress and skeleton states
-- [Inline Editing](https://reactdatatable.com/docs/inline-editing): edit cells in place
+- [Inline Editing](https://reactdatatable.com/docs/inline-editing): edit cells in place with built-in editor types, per-column validation, and optimistic server-save patterns
## Styling
diff --git a/apps/docs/src/components/demos/InlineEditingDemo.tsx b/apps/docs/src/components/demos/InlineEditingDemo.tsx
index 2ad8b08d..bcc89f39 100644
--- a/apps/docs/src/components/demos/InlineEditingDemo.tsx
+++ b/apps/docs/src/components/demos/InlineEditingDemo.tsx
@@ -34,7 +34,7 @@ export default function InlineEditingDemo() {
const handleCellEdit = (row: Employee, value: string, column: TableColumn) => {
const field = column.id as keyof Employee;
- const parsed = field === 'salary' ? Number(value) || row.salary : field === 'remote' ? value === 'true' : value;
+ const parsed = field === 'salary' ? Number(value) : field === 'remote' ? value === 'true' : value;
setData(prev => prev.map(r => (r.id === row.id ? { ...r, [field]: parsed } : r)));
setLastEdit(`Updated ${row.name} → ${String(column.name)}: "${value}"`);
};
@@ -46,6 +46,7 @@ export default function InlineEditingDemo() {
selector: r => r.name,
sortable: true,
editable: true,
+ validate: value => (value.trim() === '' ? 'Name is required' : true),
onCellEdit: handleCellEdit,
},
{
@@ -99,6 +100,10 @@ export default function InlineEditingDemo() {
sortable: true,
right: true,
editable: true,
+ validate: value => {
+ const n = Number(value);
+ return Number.isFinite(n) && n > 0 ? true : 'Enter a positive number';
+ },
onCellEdit: handleCellEdit,
},
{
@@ -117,8 +122,10 @@ export default function InlineEditingDemo() {
Click any cell to edit. Name and Salary are text inputs;{' '}
Department and Status are dropdowns; Remote is a checkbox.{' '}
- Enter commits, Esc cancels. Keyboard navigation is enabled too: click or Tab into the
- table, move between cells and headers with the arrow keys, and press Enter to edit or sort.
+ Enter commits, Esc cancels. Name and Salary are
+ validated: try committing an empty name or a negative salary to see the inline error. Keyboard navigation is
+ enabled too: click or Tab into the table, move between cells and headers with the arrow keys, and press{' '}
+ Enter to edit or sort.
+ Edits apply optimistically, then save to a simulated server with ~1s latency. The row dims while the save is in
+ flight. The server rejects any Price over $1,000 — try it to watch the edit roll back.
+
+ Validation works the same as for built-in editors: ctx.commit runs the
+ column's validate first, and a string result keeps the editor open with the
+ inline error tooltip. Since 8.7.0 the render context also carries the current error as
+ ctx.error (string | null), so your editor can style its own
+ invalid state, e.g. aria-invalid={!!ctx.error}. Attach
+ ctx.inputRef as the ref of your focusable element to get the
+ same focus handling as the built-in editors: auto-focus when the editor opens and
+ refocus after a rejected commit.
+