Skip to content

feat(data-table): add declarative cell-editing errors - #635

Open
IzumiSy wants to merge 3 commits into
mainfrom
feat/data-table-edit-errors
Open

IzumiSy wants to merge 3 commits into
mainfrom
feat/data-table-edit-errors

Conversation

@IzumiSy

@IzumiSy IzumiSy commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

DataTable's built-in editors already handle draft input, parsing, and keyboard navigation, but screens must build their own feedback for failures in editing callbacks. A screen can reject a proposed value by leaving data unchanged, yet that does not provide a consistent, accessible way to explain the failure beside the affected cell.

The goal here is declarative error presentation, not a new validation API. Existing callbacks should remain responsible for application rules and persistence, while DataTable supplies a standard error popover and lets consumers customize its content.

An initial prototype exposed an important UX inconsistency: Enter discarded the rejected draft and restored the adopted value while leaving the cell marked invalid. The displayed value no longer matched the error, and correcting or dismissing that error was unclear. This change treats the draft and its failure as one editing session so they cannot become detached.

Screen shot

An error in cell

スクリーンショット 2026-10-22 14 55 27

Error detail

スクリーンショット 2026-10-22 14 55 47

Design Decision

Callback-driven errors with a standard popover

By default, synchronous throws and returned Promise rejections from onValueChange and onValueCommitted use the cell's error popover. The standard body shows a non-empty Error.message, otherwise a localized fallback; arbitrary error objects and stack traces are not rendered.

Consumers only configure exceptions to the default:

error?: false | {
  render: (ctx: { row: TRow; error: unknown }) => ReactNode;
};

error: false preserves eager edit completion and leaves callback failures to the consumer. error.render replaces the popover body, including support for interactive React content. DataTable owns the indicator, positioning, keyboard access, and a Revert edit action.

There is no presentation discriminator because only popovers are supported. Inline presentation was left out because it introduces separate questions about replacing cell content, editor layout, and display while typing. There are also no new validate, schema, or business-constraint options.

Keep the failed draft until success or cancellation

Each cell has one internal session: idle, editing, confirming, or failed. A failed state always contains its target draft.

  • A failed change check prevents confirmation; a pending change check must succeed before onValueCommitted runs.
  • onValueCommitted is the confirmation attempt. A normal return or fulfilled Promise completes the edit; a throw or rejection retains the original draft for correction or explicit retry.
  • Esc or Revert edit discards the draft and error together and restores the latest consumer-provided data.
  • Tab and blur can move focus without discarding pending or failed drafts. Repeated confirmation and subsequent blur do not duplicate an in-flight attempt.
  • New input, cancellation, and unmount invalidate stale UI completions. Repeating an unchanged date action cannot bypass a failed check or restart a pending confirmation.

The public callback signatures remain => void to preserve compatibility with existing handlers and successful return values. Runtime Promise settlement is observed for the editing lifecycle, not used as a row value. Adoption, persistence, request cancellation, and rollback remain consumer-owned; suppressing stale UI results does not undo callback side effects.

Error inspection is part of the cell's focus group

Opening or interacting with the error popover must not accidentally confirm or discard a draft. The cell and its portaled inspector are treated as one logical focus group. Alt+ArrowDown opens error details without changing existing editor-only Tab navigation. Closing the inspector leaves the draft intact; cancelling the edit clears both its draft and error.

Summary

  • Add standard and consumer-defined callback-error popovers across typing, choice, date, and date-time editors.
  • Introduce a shared editing-session lifecycle that retains pending and rejected drafts, gates confirmation on change checks, and provides atomic cancellation.
  • Add focused state-machine and interaction regression coverage, including stale async results, repeated confirmation, exact raw draft retention, focus preservation, and opt-out behavior.
  • Add an interactive DataTable Lab showcase, update authored documentation and regenerated outputs, and include a minor changeset.

@IzumiSy
IzumiSy marked this pull request as ready for review October 22, 2026 06:00
@IzumiSy
IzumiSy requested a review from a team as a code owner October 22, 2026 06:00
@wemra3

wemra3 commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Since #583, components use the danger roles instead of the destructive utilities, and destructive is deprecated. This PR adds text-destructive and ring-destructive to the error icon, and the cell still uses ring-destructive from #611. Could you change them?

  • ring-destructive → ring-danger-solid (same colour)
  • text-destructive → text-danger-text, which other error icons already use (Field, CsvImporter, Alert)

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