feat(table): adopt glide-data-grid — spreadsheet-grade selection, copy-as-CSV, editing - #206
Merged
Merged
Conversation
…rom #205) Swap the hand-rolled table internals for @glideapps/glide-data-grid 6.0.3 (canvas-rendered, first-class rectangular + multi-range selection). Port the RFC 4180 serializer and its round-trip suite from feat/table-editing @ cf191ff, and keep the BigInt-safe comparator / filtering helpers in tableView.ts — Glide does not sort or filter for us. New dependency-free tableSelection.ts serializes row / column / rectangular selections into CSV through the same writer.
CsvTable runs on DataEditorCore: source-row identity for row selection, single-column numeric-aware sort state, 150ms debounced search with a count chip, header-click column selection, and a window copy interceptor that replaces Glide's native TSV with RFC 4180 CSV of the selection. jsdom cannot exercise canvas, so tests swap DataEditorCore for a mock that drives the component's real handlers; canvas gestures get browser evidence instead of faked unit tests.
Input-pane edits commit through onCellCommit: each batched Glide edit splices its exact row span out of the raw CSV and feeds the shared input handler, so raw text and table view never disagree and PR #198's dirty/revert/discard semantics stay intact. Output table remains strictly read-only. Changelog entry 9 announces the rebuilt table; dist rebuilt and committed.
Permalink, pane-state, paste-routing, and output-editing suites render the real App, so they import the shared glide mock and assert on row data (canvas headers render uppercase) instead of raw header text.
…w rows The stacked mobile layout can hand the table pane less height than the grid's natural size (the split container shrinks; master's DOM table overflowed visibly in the same case). Floor the wrapper at the header plus up to 12 rows via minHeight — flex-1 still fills taller panes and Glide scrolls internally — so a fresh 375px load paints the table instead of a header sliver.
Two real-grid-only editor defects the jsdom mock could not catch:
1. Glide mounts its cell-editor overlay through document.getElementById('portal'),
which did not exist — every activation logged "Cannot open Data Grid overlay
editor, because portal not found". Added <div id="portal"> as the last child
of app/index.html.
2. DataEditorCore builds no cell-renderer map on its own, so the overlay shell
mounted with no text editor inside. Pass renderers={AllCellRenderers}.
Verified live on the dev server: two-click activation opens the portal
textarea, typing + Enter commits through the guarded reconversion, and Raw
view reflects the edit. Full suite 415 green, lint/tsc clean, dist rebuilt,
verify-seo.sh passed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The CSV table's selection model is the product's core editing surface, and David's bar is spreadsheet-grade: drag-select a rectangular range, extend it with Shift+Arrow, hold multi-range selections, copy any selection as clean CSV, and fill. The hand-rolled DOM table (PR #204) and the react-data-grid interim plan both fall short — RDG's range selection is partial (no shift+click extension, no keyboard ranges, no complete selection model; adazzle issues #1133/#2260). This PR moves the table to
@glideapps/glide-data-grid6.0.3 (MIT, canvas-rendered, React), where rectangular and multi-range selection are the library's core, per the amended spec (art_Z56fIBHg, snapshot d63dd7a0).What
Data layer (ported from PR #205 @ cf191ff, not rebuilt):
app/src/lib/csvTable.ts—serializeCsvTable(headers, rows): RFC 4180 writer that quotes and escapes any cell containing comma, quote, CR, or LF, plus exact byte-span row splices for edits, with the full round-trip test suite against the parser.app/src/lib/tableView.ts— keeps what Glide does not do for us: view/source index mapping, debounced filtering, and the BigInt-safe comparator (16+ digit integers sort exactly, not throughNumber).app/src/lib/tableSelection.ts(new) — serializes any selection shape (rectangular ranges, multi-range, whole rows, whole columns) to CSV through the same writer; ranges take precedence, the source-row gutter is excluded, and the table's delimiter is preserved.Component (
CsvTable.tsx, shared by both instances):onCellCommit→ the shared input handler → PR feat(output): editable JSON output with guarded regeneration #198 guarded reconversion. Raw text and table view can never disagree.onCellsEdited, no editing affordances.N of Mcount chip, ×/Esc clear, empty-filter state with a clear action, feeding filtered rows to the grid.Test strategy for a canvas grid: jsdom cannot exercise canvas, so
DataEditorCoreis swapped forapp/src/test/glideDataEditorMock.tsx, which renders the same data through the component's real view logic and re-exposes the interaction callbacks as DOM buttons. Everything that is our code (toolbar, filtering, sorting state, selection bookkeeping, selection→CSV serialization, edit-commit splices, output read-only) is unit-tested — 414 tests green, including the 10k-row count chip and the App-level paste/edit routing suites. Genuine canvas gestures are validated with browser dogfood evidence below, not faked.Visual density (spec gate): values read from master's
CsvTable.tsxand applied through Glide's theme: 24px rows, 26px headers, 8px horizontal cell padding, 12px cell text / 11px semibold monospace uppercase headers, 110px minimum data columns, 40px source-row gutter, right-aligned row numbers, monospace numeric cells. Side-by-side before/after screenshots are attached in the QA evidence.Accessibility (honest): the canvas grid has weaker screen-reader semantics than the DOM ARIA table it replaces — no per-cell
role="gridcell"traversal. Keyboard navigation, focus management, andaria-rowcounton the grid surface are wired; per-cell ARIA was deliberately not invented. This is a real trade-off of the library swap, priced for the selection model David required.Prerender: the canvas renders nothing in prerendered HTML by design;
npm run build+ prerender +verify-seo.shpass (40,446 bytes of prerendered markup), and first paint shows the table without layout flash (PR #203 semantics).Removed: the hand-rolled DOM grid internals and their DOM-specific selection/sort tests. PR #204's UX contracts survive, reimplemented through the library.
How to Review
app/src/lib/tableSelection.ts(selection→CSV semantics) and the edit-commit splice inCsvTable.tsx(sorted batched edits → byte-span splices → guarded reconversion) — that's where correctness lives.app/src/test/glideDataEditorMock.tsxis test infrastructure, not product code; it restates the consumed prop subset strongly and casts once at the mock boundary.Test Evidence
Visual density parity: desktop 1440px and 375px before/after vs master (24px rows, 26px header, muted uppercase headers, 40px gutter, monospace numerics)




Hard-bar gestures: drag-rect selection, Ctrl+C copy, Shift+Arrow extension, Ctrl+click multi-range, fill-handle drag, raw source updated
Hard-bar gestures: drag-rect selection, Ctrl+C copy, Shift+Arrow extension, Ctrl+click multi-range, fill-handle drag, raw source updated — recording
Cell edit commits through guarded reconversion; Raw view reflects the edit
Cell edit commits through guarded reconversion; Raw view reflects the edit — recording
Search filter, sort, column selection, row selection, copy-as-CSV flow
Search filter, sort, column selection, row selection, copy-as-CSV flow — recording
tsc -b --noEmit, production build with committedapp/dist,verify-seo.sh— all green.🔗 Obvious Project · 🧵 Obvious Thread