Sapporta

React grid framework

Sapporta Grid

Editable, hierarchical data is rendered in React by Sapporta Grid. Levels, columns, editing rules, and interactions are defined in a schema. Grid operations are handled beneath the rendered surface by a JavaScript runtime.

Schema and data-source state are rendered directly by the default components. Individual renderers and editors can be replaced by the application. The same grid operations are available to tests, scripts, and other view layers through the runtime, without rendering the React components.

01 A schema with project and task levels ProjectGrid.tsx
const schema = gridSchema({
  rootLevel: "projects",
  levels: {
    projects: level({
      columns: [text("project"), select("status"), date("due")],
      children: { tasks: ({ row }) => taskSource(row.id) },
    }),
    tasks: level({
      columns: [text("task", { edit: true }), select("state"), text("assignee")],
    }),
  },
});

const runtime = createGridRuntime({
  schema,
  dataSource: projectSource,
});

<GridRuntimeProvider runtime={runtime}>
  <GridLevel path={runtime.root.path} />
</GridRuntimeProvider>
02 The rendered project grid Local demo data

Project expansion, task editing, sorting, and draft creation are available in the example below. Each operation is connected to the controls.

/grid?sort=project:asc
Status Owner Due
Ledger import Doing Maya 18 Jul
Invoice editor Ready Jasim 22 Jul
Keyboard navigation Open Noah 29 Jul

Levels and data

Hierarchical grids and level data sources

Each kind of grid is described by a level. Project columns and a project endpoint belong to the project level. A task level with different columns, editing rules, and an independent endpoint can be loaded by expanding a project. Focus, selection, expansion, and editing are maintained across the visible hierarchy.

Local memory or an existing remote API can supply data to each level through its own data source. Loading, ready, refreshing, and failure states are published inside the requesting level. The project grid therefore remains visible when a task request fails.

Data adapter A remote data-source adapter
const projectSource = remoteGridDataSource({
  async load({ sort, filters }) {
    const response = await api.projects.list({ sort, filters });
    return response.items.map(toGridRow);
  },
  async write({ rowId, columnId, value }) {
    return api.projects.update(rowId, { [columnId]: value });
  },
});

Editing and persistence

Cell editing and persistence

Editable cells open in place. A value can be applied directly to an object by a local source. The same commit can be translated into the application’s update call by a remote source. Confirmed or rejected values are returned by that call. Saving and failure feedback appears in the row while the update is in progress.

New records begin as draft rows. The nonblank condition, validation, and commit timing are controlled by the application. Entered values remain in the row for correction and retry if creation fails. This editable state is created locally by the Add a project control in the example.

level.drafts.add("new-task", { title: "" });
level.drafts.setCell("new-task", "title", "Review import");
await level.drafts.commit("new-task");

Query state

Local and remote query state

Sortable columns support ascending and descending order. Filters, sorting, refetching, and pagination can be run in memory or passed through a remote adapter. The visible grid becomes a linkable application view when its query state is connected to the browser URL. The example URL changes as the filter above is edited.

Keyboard operation

Keyboard interaction presets

Cell movement and range extension are assigned by a spreadsheet-style preset. Record movement and Space key selection are assigned by row-oriented presets. One coherent keyboard model is selected for the application, without page-local key handlers.

KeyVisible result in cell-grid mode
Arrow keysMove the active cell.
Shift + ArrowExtend the selected cell range.
Enter or F2Start editing an editable cell.
Tab / Shift + TabMove to the next or previous grid target.
Ctrl/Cmd + HomeMove to the first grid target.
Page Up / Page DownMove ten rows up or down.

Runtime and rendering

The JavaScript runtime and React renderer

Row loading, level expansion, sorting, filtering, editing, creation, selection, and row operations are exposed by a core JavaScript object. Its state is rendered by the supplied React primitives. The same public API can be operated directly by an integration test or a script.

Column presets and a default stylesheet are included with Sapporta Grid. The public DOM and state attributes can be styled with application CSS. Custom cells and editors can also be supplied while focus, selection, copy, and editing behavior remain under runtime control.

Runtime test A runtime operation without React
const runtime = createGridRuntime({ schema, dataSource });
const orderId = makeRowId(runtime.root.path, "O1");

await runtime.root.data.query?.sort?.set([
  { colId: "amount", direction: "asc" },
]);
runtime.root.toggleExpand(orderId);
runtime.root.writeCell({ rowId: orderId, colId: "amount" }, 125);