From 9c1ba740058a8ea8f05b3c61f356e1506c3c7d39 Mon Sep 17 00:00:00 2001 From: ZainnQureshii <43629888+ZainnQureshii@users.noreply.github.com> Date: Sat, 14 Nov 2026 13:57:36 +0100 Subject: [PATCH] fix(blocks): trigger onChange for insertMany Emit a block-added mutation for each block inserted by BlockManager.insertMany and keep the modifications observer disabled until the initial render finishes, so loading a document stays silent. Fixes #2874 Co-Authored-By: Claude Opus 5.5 --- docs/CHANGELOG.md | 1 + src/components/modules/blockManager.ts | 8 +++ .../modules/modificationsObserver.ts | 2 +- test/cypress/tests/onchange.cy.ts | 55 +++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 480a12c7f..16c08a0e8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,7 @@ - `Improvement` - Removed "Narrow" ("Thin") mode. On non-mobile viewports, Plus and Block Tunes controls now use a responsive action rail beside the editor content. - `Improvement` - `paste` config supports filter functions for tag-based substitution - `Fix` - "Convert to" is not shown in the Inline Toolbar when there are no tools to convert to +- `Fix` - `blocks.insertMany()` now triggers `onChange` with a `block-added` event for each inserted block ### 2.31.7 diff --git a/src/components/modules/blockManager.ts b/src/components/modules/blockManager.ts index be8e1e247..31a867498 100644 --- a/src/components/modules/blockManager.ts +++ b/src/components/modules/blockManager.ts @@ -331,6 +331,14 @@ export default class BlockManager extends Module { */ public insertMany(blocks: Block[], index = 0): void { this._blocks.insertMany(blocks, index); + + const firstBlockIndex = this.getBlockIndex(blocks[0]); + + blocks.forEach((block, blockIndex) => { + this.blockDidMutated(BlockAddedMutationType, block, { + index: firstBlockIndex + blockIndex, + }); + }); } /** diff --git a/src/components/modules/modificationsObserver.ts b/src/components/modules/modificationsObserver.ts index 6185d7d5f..9fe1d9b07 100644 --- a/src/components/modules/modificationsObserver.ts +++ b/src/components/modules/modificationsObserver.ts @@ -18,7 +18,7 @@ export default class ModificationsObserver extends Module { /** * Flag shows onChange event is disabled */ - private disabled = false; + private disabled = true; /** * Blocks wrapper mutation observer instance diff --git a/test/cypress/tests/onchange.cy.ts b/test/cypress/tests/onchange.cy.ts index 1bf78dcb0..dbebaa1ba 100644 --- a/test/cypress/tests/onchange.cy.ts +++ b/test/cypress/tests/onchange.cy.ts @@ -1111,6 +1111,61 @@ describe('onChange callback', () => { })); }); + it('should batch block-added events with their targets and indices on insertMany', () => { + cy.createEditor({ + onChange: cy.stub().as('onChange'), + data: { + blocks: [ + { + type: 'paragraph', + data: { text: 'First block' }, + }, + { + type: 'paragraph', + data: { text: 'Last block' }, + }, + ], + }, + }).as('editorInstance'); + + cy.wait(modificationsObserverBatchTimeout); + cy.get('@onChange').should('not.be.called'); + + cy.get('@editorInstance').then(editor => { + editor.blocks.insertMany([ + { + id: 'bulk-first', + type: 'paragraph', + data: { text: 'Inserted first' }, + }, + { + id: 'bulk-second', + type: 'paragraph', + data: { text: 'Inserted second' }, + }, + ], 1); + }); + + cy.get('@onChange').should('be.calledWithBatchedEvents', [ + { + type: BlockAddedMutationType, + detail: { + index: 1, + target: { id: 'bulk-first' }, + }, + }, + { + type: BlockAddedMutationType, + detail: { + index: 2, + target: { id: 'bulk-second' }, + }, + }, + ]); + cy.get('@onChange').its('firstCall.args.1') + .should('have.length', 2); + }); + it('should not be called when editor is initialized with readOnly mode', () => { const config = { readOnly: true,