diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 718f1a88..573ca444 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,6 +3,7 @@ ### 2.23.0 - `Improvement` — The `onChange` callback now accepts two arguments: EditorJS API and the CustomEvent with `type` and `detail` allowing to determine what happened with a Block +- `New` *Block API* — The new `dispatchChange()` method allows to manually trigger the 'onChange' callback. Useful when Tool made a state mutation that is invisible for editor core. ### 2.22.3 diff --git a/docs/api.md b/docs/api.md index ab0c9e3b..3f009185 100644 --- a/docs/api.md +++ b/docs/api.md @@ -35,6 +35,8 @@ API for certain Block methods and properties. You can access it through `editor. `validate(data: BlockToolData): Promise` — calls Tool's validate method if exists +`dispatchChange(): void` - Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback. Can be useful for block changes invisible for editor core. + ## Api object description Common API interface. diff --git a/src/components/block/api.ts b/src/components/block/api.ts index 556323ee..a5049253 100644 --- a/src/components/block/api.ts +++ b/src/components/block/api.ts @@ -116,6 +116,14 @@ function BlockAPI( validate(data: BlockToolData): Promise { return block.validate(data); }, + + /** + * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback + * Can be useful for block changes invisible for editor core. + */ + dispatchChange(): void { + block.dispatchChange(); + }, }; Object.setPrototypeOf(this, blockAPI); diff --git a/src/components/block/index.ts b/src/components/block/index.ts index 85771882..d3eabc8c 100644 --- a/src/components/block/index.ts +++ b/src/components/block/index.ts @@ -201,7 +201,7 @@ export default class Block extends EventsDispatcher { /** * Is fired when DOM mutation has been happened */ - private didMutated = _.debounce((mutations: MutationRecord[]): void => { + private didMutated = _.debounce((mutations: MutationRecord[] = []): void => { const shouldFireUpdate = !mutations.some(({ addedNodes = [], removedNodes }) => { return [...Array.from(addedNodes), ...Array.from(removedNodes)] .some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true'); @@ -702,6 +702,14 @@ export default class Block extends EventsDispatcher { this.removeInputEvents(); } + /** + * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback + * Can be useful for block changes invisible for editor core. + */ + public dispatchChange(): void{ + this.didMutated(); + } + /** * Call Tool instance destroy method */ diff --git a/test/cypress/tests/api/block.spec.ts b/test/cypress/tests/api/block.spec.ts new file mode 100644 index 00000000..09b55efb --- /dev/null +++ b/test/cypress/tests/api/block.spec.ts @@ -0,0 +1,63 @@ +import { BlockMutationType } from '../../../../types/events/block/mutation-type'; + +/** + * There will be described test cases of BlockAPI + */ +describe('BlockAPI', () => { + const firstBlock = { + id: 'bwnFX5LoX7', + type: 'paragraph', + data: { + text: 'The first block content mock.', + }, + }; + const editorDataMock = { + blocks: [ + firstBlock, + ], + }; + + /** + * EditorJS API is passed as the first parameter of the onChange callback + */ + const EditorJSApiMock = Cypress.sinon.match.any; + + beforeEach(() => { + if (this && this.editorInstance) { + this.editorInstance.destroy(); + } else { + const config = { + data: editorDataMock, + onChange: (): void => { console.log('something changed'); }, + }; + + cy.createEditor(config).as('editorInstance'); + + cy.spy(config, 'onChange').as('onChange'); + } + }); + + /** + * block.dispatchChange(); + */ + describe('.dispatchChange()', () => { + /** + * Check that blocks.dispatchChange() triggers Editor 'onChange' callback + */ + it('should trigger onChange with corresponded block', () => { + cy.get('@editorInstance').then(async (editor: any) => { + const block = editor.blocks.getById(firstBlock.id); + + block.dispatchChange(); + + cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ + type: BlockMutationType.Changed, + detail: { + index: 0, + }, + })); + }); + }); + }); + +}); diff --git a/types/api/block.d.ts b/types/api/block.d.ts index 81ba721f..c20e4622 100644 --- a/types/api/block.d.ts +++ b/types/api/block.d.ts @@ -67,4 +67,10 @@ export interface BlockAPI { * @return {Promise} */ validate(data: BlockToolData): Promise; + + /** + * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback + * Can be useful for block changes invisible for editor core. + */ + dispatchChange(): void; }