feat: add onAddRow button to EditableTable and nested rows#3576
feat: add onAddRow button to EditableTable and nested rows#3576
Conversation
9222b61 to
c99b941
Compare
✅ No New Circular DependenciesNo new circular dependencies detected. Current count: 0 |
🔍 Visual review for your branch is published 🔍Here are the links to: |
📦 Alpha Package Version PublishedUse Use |
There was a problem hiding this comment.
Pull request overview
This PR introduces an Editable Table visualization for experimental/OneDataCollection, enabling inline cell editing (starting with text) and adding extensibility hooks to the existing TableCollection (row wrapper, cell renderer, add-row actions, separate visualization settings key).
Changes:
- Added a new
editableTablevisualization type + registry entry, with dedicated i18n keys and Storybook docs/stories. - Extended
TableCollectionwith customization hooks (rowWrapper,cellRenderer,showItemActions,visualizationSettings,onAddRow) to support wrappers like EditableTable without duplicating table logic. - Added a
ui/value-displayeditor contract + registry, plus aTextEditorimplementation used by the editable table cell renderer.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react/src/ui/value-display/types/text/text-editor.tsx | Adds TextEditor implementation using existing Input field. |
| packages/react/src/ui/value-display/types/text/index.tsx | Exports TextEditor from the text value-display type barrel. |
| packages/react/src/ui/value-display/types.ts | Introduces ValueDisplayEditorProps contract for editor components. |
| packages/react/src/ui/value-display/index.tsx | Re-exports editor registry from value-display entrypoint. |
| packages/react/src/ui/value-display/editors.ts | Adds valueDisplayEditors registry + EditableValueDisplayType. |
| packages/react/src/lib/providers/i18n/i18n-provider-defaults.ts | Adds default translations for editable table visualization + add-row/error strings. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/types.ts | Adds editableTable to visualization type union. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/collectionViewRegistry.tsx | Registers the new editableTable visualization + settings handling keyed by editableTable. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/types.ts | Adds customization prop types (rowWrapper, cellRenderer, showItemActions, visualizationSettings, onAddRow). |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/settings/handleResetSettings.ts | Removes dedicated reset handler in favor of inline reset in registry. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/settings/SettingsRenderer.tsx | Allows passing a visualization settings key into table settings UI. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/providers/NestedProvider.tsx | Persists nested expansion state in context to survive parent re-renders. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/index.tsx | Updates exports after removing reset handler file. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/components/TableSettings.tsx | Adds visualizationKey support so column settings are stored under table vs editableTable. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/components/Row.tsx | Adds optional cellRenderer and forwards nested wrapping/add-row props down to NestedRow. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/components/NestedRow.tsx | Moves provider responsibility to table-level context and adds nested “Add row” UI. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/tests/Table.spec.tsx | Adds tests for cellRenderer, rowWrapper, and showItemActions. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/Table.tsx | Wires customization props into TableCollection, mounts nested provider, and adds root-level “Add row” footer. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/EditableTable/types.ts | Defines editable table column/options types and onCellChange contract. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/EditableTable/index.tsx | Exports EditableTable visualization and supporting context/renderer helpers. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/EditableTable/context/EditableRowContext.tsx | Adds per-row optimistic editing state, per-cell loading/errors, and change handling. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/EditableTable/components/EditableCellRenderer.tsx | Implements editable cell rendering via valueDisplayEditors and row context. |
| packages/react/src/experimental/OneDataCollection/visualizations/collection/EditableTable/EditableTable.tsx | Wraps TableCollection with EditableRowProvider and EditableCellRenderer to enable editing. |
| packages/react/src/experimental/OneDataCollection/stories/visualizations/editable-table/editable-table.stories.tsx | Adds Storybook examples for editable table usage, settings, errors, nested, and add-row behavior. |
| packages/react/src/experimental/OneDataCollection/stories/visualizations/editable-table/editable-table.mdx | Adds MDX documentation for the Editable Table visualization. |
| packages/react/src/experimental/OneDataCollection/stories/visualizations.mdx | Adds “Editable Table” entry to the visualizations docs index. |
| packages/react/src/experimental/OneDataCollection/stories/mockData.tsx | Extends mock visualizations to include editable table + cache update helper and refetch dependencies. |
Comments suppressed due to low confidence (2)
packages/react/src/experimental/OneDataCollection/stories/visualizations/editable-table/editable-table.stories.tsx:17
- This new story meta is missing the standard
tagsused across OneDataCollection stories (e.g.["autodocs", "experimental"]). Also add a Snapshot story usingwithSnapshot({})to match the repo Storybook checklist and ensure Chromatic coverage.
const meta = {
title: "Data Collection/Visualizations/Editable Table",
parameters: {
layout: "padded",
docs: {
packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/Table.tsx:335
TableWrapperis onlyNestedDataProviderwhentableWithChildrenis truthy, buttableWithChildrenis currently derived fromdata?.records(flat data only). In grouped mode you can still renderNestedRow, anduseNestedDataContext()will throw because the provider isn’t mounted. Consider mountingNestedDataProviderwhenever nested rows are possible (e.g. whensource.itemsWithChildrenis defined), or computetableWithChildrenfor both flat and grouped data.
const TableWrapper = tableWithChildren ? NestedDataProvider : Fragment
.../src/experimental/OneDataCollection/visualizations/collection/Table/components/NestedRow.tsx
Outdated
Show resolved
Hide resolved
packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/types.ts
Outdated
Show resolved
Hide resolved
c99b941 to
0d3b3f2
Compare
Add an optional `onAddRow` callback to EditableTable that renders a ghost "Add row" button at the bottom of the table and at the bottom of each expanded nested row. The callback receives the parent item when triggered from a nested row (undefined at root level) and supports async functions for automatic loading state via F0Button. - Add `onAddRow` to EditableTableVisualizationOptions and TableCustomizationProps types - Propagate through Table → Row → NestedRow with proper depth-based indentation using SPACING_FACTOR - Add i18n key `collections.editableTable.addRow` - Add stories: flat table with functional row insertion, nested table with action logging Made-with: Cursor
0d3b3f2 to
7b189fa
Compare
packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/Table.tsx
Outdated
Show resolved
Hide resolved
...al/OneDataCollection/visualizations/collection/Table/components/__tests__/NestedRow.spec.tsx
Show resolved
Hide resolved
...al/OneDataCollection/visualizations/collection/Table/components/__tests__/NestedRow.spec.tsx
Show resolved
Hide resolved
…ableTable Allow consumers to customize the "Add row" button text at both root and nested levels. When not provided, the existing i18n default is used. Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/Table.tsx:627
- The summary row is rendered as
sticky bottom-0even when an additional footer row (onAddRow) is appended after it. This can cause the sticky summary row to visually overlap/obscure the “Add row” button (since both want to occupy the bottom area). Consider updating the footer layout so only the last footer row isbottom-0(e.g., make the add-row row sticky atbottom-0and offset the summary row above it, or disable stickiness whenonAddRowis present).
{(summaryData || onAddRow) && (
<TableFooter>
{summaryData && (
<TableRow
className={cn(
summaryData.sticky &&
"sticky bottom-0 z-10 bg-f1-background shadow-[0_-1px_0_0_var(--f1-border-secondary)] hover:bg-f1-background",
"font-medium"
)}
>
packages/react/src/experimental/OneDataCollection/visualizations/collection/Table/Table.tsx
Show resolved
Hide resolved
| const addRowButtons = screen.getAllByRole("button", { name: /add row/i }) | ||
| expect(addRowButtons.length).toBeGreaterThanOrEqual(2) | ||
|
|
||
| // The first "Add row" button in DOM order is the one inside the nested row | ||
| await user.click(addRowButtons[0]) |
There was a problem hiding this comment.
Clicking addRowButtons[0] depends on DOM order, which can change (e.g. if footer order changes, or other “Add row” buttons are introduced). To make the assertion robust, scope the query to the expanded nested section (e.g. using within(...) on a container row/cell that is part of the expanded area) and click the button found within that scope.
...al/OneDataCollection/visualizations/collection/Table/components/__tests__/NestedRow.spec.tsx
Show resolved
Hide resolved
Replace prop drilling of onAddRow, addRowButtonLabel, and nestedAddRowButtonLabel through TableCollection -> Row -> NestedRow with a React context provided by EditableTableCollection. This keeps the generic Table API clean of editable-table-specific concerns. Made-with: Cursor
741314f to
1cce251
Compare
| import { screen, waitFor } from "@testing-library/react" | ||
| import userEvent from "@testing-library/user-event" | ||
| import { describe, expect, it, vi } from "vitest" | ||
|
|
There was a problem hiding this comment.
This new test file uses a .spec.tsx suffix, but packages/react/AGENTS.md specifies tests must be named .test.ts/.test.tsx (never .spec.ts). Please rename this file accordingly so it follows the repo’s testing conventions.
| describe("onAddRow", () => { | ||
| it("does not render add-row button when onAddRow is not provided", async () => { | ||
| render( | ||
| <TableCollection< | ||
| Person, |
There was a problem hiding this comment.
This file is named Table.spec.tsx, but packages/react/AGENTS.md states test files should be .test.ts/.test.tsx (never .spec.ts). Since this PR adds new tests here, consider renaming the file to align with the project’s testing conventions.
| style={{ | ||
| marginLeft: `${((props.nestedRowProps?.depth ?? 0) + 1) * SPACING_FACTOR}px`, | ||
| }} |
There was a problem hiding this comment.
The nested add-row indentation re-implements the depth * SPACING_FACTOR calculation inline. To keep indentation consistent with the rest of the nested-table rendering (and avoid duplicating spacing logic), prefer using the existing getNestedMarginLeft(...) helper from TableCell/utils/nested instead of computing the pixel value manually here.
| <AddRowProvider | ||
| value={{ | ||
| onAddRow: onAddRow as AddRowProviderProps["value"]["onAddRow"], | ||
| addRowButtonLabel, | ||
| nestedAddRowButtonLabel, | ||
| }} |
There was a problem hiding this comment.
onAddRow is cast to the context type when building the AddRowProvider value. This assertion sidesteps TypeScript’s function-parameter variance checks and can hide real type mismatches. Consider changing the AddRowContext API to avoid needing a cast (e.g., store an (parentItem?: unknown) => ... callback in context and provide a typed useAddRow<R>() hook that narrows it at consumption time, similar to EditableRowContext).
| import { useAddRow } from "../EditableTable/context/AddRowContext" | ||
| import { statusToChecked } from "../utils" | ||
| import { Row } from "./components/Row" |
There was a problem hiding this comment.
PR description mentions adding onAddRow to TableCustomizationProps, but the implementation wires add-row behavior through AddRowContext (no onAddRow prop on TableCustomizationProps in Table/types.ts). Please update the PR description to match the actual approach, or update the types if the intent is to expose this through TableCustomizationProps.

🚪 Why?
The
EditableTablevisualization in f0 needs an "Add row" affordance so consumers can let users insert new rows directly from the table UI — both at the root level and within nested/expandable rows. Additionally, the genericTableCollectionAPI should not be coupled to editable-table-specific props.🔑 What?
onAddRowcallback toEditableTablethat renders a ghost "Add row" button in the table footer and at the bottom of each expanded nested row. The callback receives the parent item when triggered from a nested row (undefinedat root level) and supports async functions for automatic loading state.addRowButtonLabelandnestedAddRowButtonLabelprops to customize the button text at root and nested levels, falling back to an i18n default.AddRowContextprovided byEditableTableCollection, replacing prop drilling of add-row concerns throughTableCollection -> Row -> NestedRowand keeping the generic table API clean.Table(add-row via context) andNestedRow(nested add-row via context).Screen.Recording.2026-03-03.at.13.29.04.mov