Skip to content

feat: moveBlocks behaviour when leaving empty columns (BLO-1109)#2842

Open
matthewlipski wants to merge 3 commits into
mainfrom
move-blocks-columns-error
Open

feat: moveBlocks behaviour when leaving empty columns (BLO-1109)#2842
matthewlipski wants to merge 3 commits into
mainfrom
move-blocks-columns-error

Conversation

@matthewlipski

@matthewlipski matthewlipski commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes an edge case when using moveBlocks with columns. moveBlocks first removes the target block(s), then inserts it/them above/below the previous/next block. In the removal step, fixColumns is called for all affected column lists, collapsing them or individual columns when they're empty.

This causes an error in the following case:

<column>
  <paragraph></paragraph>
  <paragraph>Paragraph</paragraph>
</column>

When moving the non-empty paragraph block up, it's first removed. This leaves the column with a single empty paragraph, which fixColumns reads as an empty column, and so collapses it. Upon re-insertion, the reference block no longer exists and an error is thrown. This also happens when the order of the 2 blocks is flipped and the non-empty one is moved down.

After reviewing this, we have decided to change the behaviour of moveBlocks to not call fixColumns. If a column is left empty after a move, it's filled with an empty paragraph.

Closes #2594

Rationale

This is a bug, but also made us realize that we want to change the behaviour of moveBlocks to not remove empty columns altogether.

Changes

See above.

Impact

N/A

Testing

Added unit tests.

Screenshots/Video

N/A

Checklist

  • Code follows the project's coding standards.
  • Unit tests covering the new feature have been added.
  • All existing tests pass.
  • The documentation has been updated to reflect the new feature

Additional Notes

N/A

Summary by CodeRabbit

  • Bug Fixes

    • Improved stability of block movement operations, ensuring moved blocks are removed and re-inserted reliably while maintaining surrounding layout structure.
    • Fixed edge cases for moving blocks within multi-column layouts, including scenarios involving empty sibling paragraphs.
  • Tests

    • Added snapshot coverage for moving blocks up/down past empty siblings inside multi-column structures to prevent regressions.

@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview Jul 6, 2026 7:44pm
blocknote-website Error Error Jul 6, 2026 7:44pm

Request Review

@matthewlipski matthewlipski requested a review from nperez0111 June 8, 2026 08:47
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR changes block moving to run removal and insertion inside a transaction while skipping intermediate column cleanup during removal. It also updates tests to cover moving blocks past empty siblings in multi-column content.

Changes

Block Move Column-Fixing Refactor

Layer / File(s) Summary
removeAndInsertBlocks column-fixing options and return type
packages/core/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts
removeAndInsertBlocks accepts an optional options.fixColumns flag and conditionally runs column-list cleanup after removals based on that flag.
moveBlocks transaction callback and deferred column fixing
packages/core/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts
moveBlocks switches to editor.transact((tr) => ...), calls removeAndInsertBlocks and insertBlocks on the same transaction, and removes the special-case columnList reference adjustment.
Test coverage for moving past empty sibling nodes
packages/xl-multi-column/src/test/commands/moveBlocks.test.ts
The move-blocks test suite adds beforeEach setup for a multi-column document and new snapshots for moveBlocksUp and moveBlocksDown around empty paragraph siblings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • TypeCellOS/BlockNote#2723: Touches the shared moveBlocks command flow and related transaction-based block movement behavior.

Suggested reviewers: nperez0111, YousefED

Poem

🐰 I hopped through columns, soft and neat,
With empty siblings at my feet.
A transaction danced, blocks went in line,
And snapshots said: “Yes, all looks fine!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly points to the moveBlocks empty-column behavior change and matches the PR's main fix.
Description check ✅ Passed The description follows the template and covers summary, rationale, changes, impact, testing, checklist, and notes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch move-blocks-columns-error

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://TypeCellOS.github.io/BlockNote/pr-preview/pr-2842/

Built to branch gh-pages at 2026-07-06 21:35 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Moves are rearrangements rather than deletions, so columns that a move
empties out are deliberately left as-is instead of being collapsed. This
keeps moves free of side effects (and reversible by moving back), matches
dragging a block out of a column, and fixes the original error: collapsing
a transiently-empty column mid-move broke the re-insertion.

Also simplifies `removeAndInsertBlocks` (plain `fixColumns` opt-out, no
`affectedColumnLists` return) and removes the columnList reference
anchoring workaround in `moveBlocks`, which guarded against mid-move
collapsing that can no longer happen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@YousefED

YousefED commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

@matthewlipski I pushed 2f1af8f on top of your fix — could you review? It changes the approach based on a policy decision:

Moves no longer collapse emptied columns at all. Instead of deferring fixColumnList until after re-insertion, moveBlocks now simply skips it: a move is a rearrangement rather than a deletion, so it shouldn't restructure the document as a side effect. This makes moves reversible (moving a block back restores the original layout), and matches what dragging a block out of a column already does (native PM drops never ran fixColumnList either). If a move empties a column, the column stays, with an empty paragraph — the user can see it and delete it deliberately. Explicit deletions (removeBlocks/replaceBlocks) still clean up as before.

Follow-on simplifications:

  • removeAndInsertBlocks keeps your fixColumns opt-out but no longer collects/returns affectedColumnLists — nothing consumes it anymore.
  • The columnList reference anchoring workaround at the top of moveBlocks is removed: it guarded against fixColumnList dissolving the reference columnList mid-move, which can no longer happen. The moveBlocksDown > Selection across columns test covers exactly that case (reference = the columnList itself) and passes through the plain path.
  • 4 snapshots re-recorded — all in multi-block-selection scenarios that empty out a source column. They now show the columnList surviving with an empty column instead of dissolving into loose paragraphs. Your two "empty sibling" regression tests pass unchanged.

Since this is now a behavior change rather than just an error fix, the PR title/description should probably be updated to say so (e.g. "moveBlocks no longer collapses emptied columns") — that's worth surfacing in the changelog.

🤖 Generated with Claude Code

@pkg-pr-new

pkg-pr-new Bot commented Jul 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2842

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2842

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2842

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2842

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2842

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2842

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2842

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2842

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2842

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2842

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2842

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2842

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2842

commit: 1de86a2

@matthewlipski

Copy link
Copy Markdown
Collaborator Author

@matthewlipski I pushed 2f1af8f on top of your fix — could you review? It changes the approach based on a policy decision:

Moves no longer collapse emptied columns at all. Instead of deferring fixColumnList until after re-insertion, moveBlocks now simply skips it: a move is a rearrangement rather than a deletion, so it shouldn't restructure the document as a side effect. This makes moves reversible (moving a block back restores the original layout), and matches what dragging a block out of a column already does (native PM drops never ran fixColumnList either). If a move empties a column, the column stays, with an empty paragraph — the user can see it and delete it deliberately. Explicit deletions (removeBlocks/replaceBlocks) still clean up as before.

Follow-on simplifications:

  • removeAndInsertBlocks keeps your fixColumns opt-out but no longer collects/returns affectedColumnLists — nothing consumes it anymore.
  • The columnList reference anchoring workaround at the top of moveBlocks is removed: it guarded against fixColumnList dissolving the reference columnList mid-move, which can no longer happen. The moveBlocksDown > Selection across columns test covers exactly that case (reference = the columnList itself) and passes through the plain path.
  • 4 snapshots re-recorded — all in multi-block-selection scenarios that empty out a source column. They now show the columnList surviving with an empty column instead of dissolving into loose paragraphs. Your two "empty sibling" regression tests pass unchanged.

Since this is now a behavior change rather than just an error fix, the PR title/description should probably be updated to say so (e.g. "moveBlocks no longer collapses emptied columns") — that's worth surfacing in the changelog.

🤖 Generated with Claude Code

I think it's a good change, don't see issues with it. The one caveat I have is that drag & drop is also a rearrangement rather than a deletion, yet drag & drop does remove empty columns. I think for logical consistency, maybe these should have the same behaviour. But I'd rather do that in a separate PR if anything.

@matthewlipski matthewlipski changed the title fix: Error using moveBlocks in columns (BLO-1109) fix: moveBlocks behaviour when leaving empty columns (BLO-1109) Jul 6, 2026
@matthewlipski matthewlipski changed the title fix: moveBlocks behaviour when leaving empty columns (BLO-1109) feat: moveBlocks behaviour when leaving empty columns (BLO-1109) Jul 6, 2026
@YousefED

YousefED commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

I think it's a good change, don't see issues with it. The one caveat I have is that drag & drop is also a rearrangement rather than a deletion, yet drag & drop does remove empty columns. I think for logical consistency, maybe these should have the same behaviour. But I'd rather do that in a separate PR if anything.

Good point. @virgile-dev do you have an opinion on this? (It would make dragging the last block out of a column not remove that column as it is now (but keep an empty block around)

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.

Multi-columns errors when moving blocks

2 participants