Skip to content

test(react): pin the copy/paste column-space coupling while grouped - #485

Merged
blove merged 1 commit into
mainfrom
claude/grouping-edit-paste-guards
Aug 24, 2026
Merged

test(react): pin the copy/paste column-space coupling while grouped#485
blove merged 1 commit into
mainfrom
claude/grouping-edit-paste-guards

Conversation

@blove

@blove blove commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

What I was asked to fix, and why I didn't

The scoped defect was: the derived group column is not excluded from paste's column spacemapPasteToTargets filters only ROW_SELECT_COLUMN_ID (packages/react/src/paste.ts:316), so __pretable_group__ occupies a slot no consumer column occupies.

Both predicted manifestations are real and reachable:

  1. Anchor on the group column. A data row's group cell renders as an ordinary body cell (only ROW_SELECT_COLUMN_ID is guarded in its onClick). A plain click focuses it (data-pretable-focused="true"), and a paste there sends block column 0 into __pretable_group__: rejected: ["r1:__pretable_group__=x (not-editable)"], cells: ["r1:name=y"].
  2. Displacement. A row-select ("whole row") anchor re-anchors to index 0 — which is the group column while grouped — so the block starts one column right of where the same gesture lands ungrouped, and the first clipboard value is lost.

But the inclusion is load-bearing, not an oversight. copy.ts:327 and csv.ts:523 both deliberately emit a field for the same derived column — the group label on a group row, an empty field on a data row (its definition is value: () => ""). Verified directly: a whole grouped row serializes to "\tr0a\tr0b\tr0c\tr0d". That leading empty field is what the paste slot lines up with.

Excluding the group column from paste alone therefore breaks the copy-rows → paste-rows round trip, destructively. Measured by mutating paste.ts exactly as scoped:

- expected  [ "r2:a=r0a", "r2:b=r0b", "r2:c=r0c", "r2:d=r0d" ]
+ received  [ "r2:a=",    "r2:b=r0a", "r2:c=r0b", "r2:d=r0c" ]   (+ r0d clipped)

a is silently blanked with copy's empty group field and every value shifts one column right. Today's cost is a cosmetic rejected entry naming a column the consumer can recognize (GROUP_COLUMN_ID is public API). Trading a benign rejection for a silent wrong write is the wrong direction, so I did not ship the fix.

What this PR does

Nothing pinned that coupling — all 116 existing clipboard tests pass with the group column excluded from mapPasteToTargets, which is why this was scoped as a one-line fix in the first place. This adds the missing guard.

The test round-trips a whole grouped row through serializeRangesparseTsvmapPasteToTargets on the same drawn-column array the surface passes to both, and asserts every value lands back in the column it came from. It is written against the invariant, not the current choice: it passes whether the group column is in both column spaces or neither, and fails only when the two disagree.

The real trade, left open

The two sides genuinely conflict and the resolution is a product decision, not a bug fix:

  • Internal round trip (copy rows → paste rows) wants the slot. Works correctly today.
  • External paste (whole-row selection + a block from Excel) wants it gone — today the user's first column lands in __pretable_group__ and the rest shift right.

The only arrangement that satisfies both is coherent change on both sides: stop copy/CSV emitting a standalone field for the derived group column (e.g. move the group label into the first data column), then drop the slot from paste. That changes documented copy behavior (clipboard.mdx:44-47) and would lose group labels from a select-all copy unless the label is rehomed — worth a brainstorm, not a drive-by.

Gate

pnpm build, pnpm typecheck, pnpm lint, pnpm format, pnpm test (every package ran; react 1247 passed), pnpm api:check — all exit 0.

One snag worth knowing: pnpm build failed twice with TS2307: Cannot find module '@pretable/core' from packages/renderer-dom until pnpm install relinked it — the worktree's packages/renderer-dom/node_modules had no @pretable directory at all. Not caused by this branch.

🤖 Generated with Claude Code

The derived group column sits in paste's target space: `mapPasteToTargets`
filters only `ROW_SELECT_COLUMN_ID`, so `__pretable_group__` occupies a slot,
takes the block's first value when the anchor lands at column 0, and reports
it back as a `not-editable` rejection.

That reads like an oversight and isn't. Copy and CSV both emit a field for
the same column — the group label on a group row, an empty field on a data
row — so the slot is what keeps the two sides counting across the same column
space. Dropping it from paste alone shifts every value one column right on the
way back in: a whole-row copy of `\tr0a\tr0b\tr0c\tr0d` pasted onto another
row writes `a=""` (blanking a real column), `b=r0a`, `c=r0b`, `d=r0c`, and
clips `r0d`. Silent and destructive, where today's cost is a cosmetic
rejection entry.

Nothing pinned that coupling: all 116 existing clipboard tests pass with the
group column excluded from `mapPasteToTargets`. This test round-trips a whole
grouped row through `serializeRanges` -> `parseTsv` -> `mapPasteToTargets` and
asserts every value lands back in the column it came from. It holds under
either arrangement — group column in both spaces or in neither — and fails
only when the two disagree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@blove
blove enabled auto-merge (squash) August 24, 2026 17:36
@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pretable Ignored Ignored Aug 24, 2026 5:36pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

Vercel preview ready

Preview: https://pretable-nrr810fiy-cacheplane.vercel.app
Commit: cd33c641d549073a8b7054ae283e61f056d495b2

Updated automatically by the deploy-preview job.

@blove
blove merged commit 2ad7fee into main Aug 24, 2026
20 checks passed
@blove
blove deleted the claude/grouping-edit-paste-guards branch August 24, 2026 17:51
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.

1 participant