Skip to content

Commit 9c0fc48

Browse files
authored
feat(block-api): the new dispatchChange() method (#1794)
The new `dispatchChange()` method allows to manually trigger the 'onChange' callback. Useful when Tool made a state mutation that is invisible for editor core.
1 parent 4f15bbc commit 9c0fc48

File tree

6 files changed

+89
-1
lines changed

6 files changed

+89
-1
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### 2.23.0
44

55
- `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
6+
- `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.
67

78
### 2.22.3
89

docs/api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ API for certain Block methods and properties. You can access it through `editor.
3535

3636
`validate(data: BlockToolData): Promise<boolean>` — calls Tool's validate method if exists
3737

38+
`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.
39+
3840
## Api object description
3941

4042
Common API interface.

src/components/block/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ function BlockAPI(
116116
validate(data: BlockToolData): Promise<boolean> {
117117
return block.validate(data);
118118
},
119+
120+
/**
121+
* Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback
122+
* Can be useful for block changes invisible for editor core.
123+
*/
124+
dispatchChange(): void {
125+
block.dispatchChange();
126+
},
119127
};
120128

121129
Object.setPrototypeOf(this, blockAPI);

src/components/block/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export default class Block extends EventsDispatcher<BlockEvents> {
201201
/**
202202
* Is fired when DOM mutation has been happened
203203
*/
204-
private didMutated = _.debounce((mutations: MutationRecord[]): void => {
204+
private didMutated = _.debounce((mutations: MutationRecord[] = []): void => {
205205
const shouldFireUpdate = !mutations.some(({ addedNodes = [], removedNodes }) => {
206206
return [...Array.from(addedNodes), ...Array.from(removedNodes)]
207207
.some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true');
@@ -702,6 +702,14 @@ export default class Block extends EventsDispatcher<BlockEvents> {
702702
this.removeInputEvents();
703703
}
704704

705+
/**
706+
* Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback
707+
* Can be useful for block changes invisible for editor core.
708+
*/
709+
public dispatchChange(): void{
710+
this.didMutated();
711+
}
712+
705713
/**
706714
* Call Tool instance destroy method
707715
*/
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { BlockMutationType } from '../../../../types/events/block/mutation-type';
2+
3+
/**
4+
* There will be described test cases of BlockAPI
5+
*/
6+
describe('BlockAPI', () => {
7+
const firstBlock = {
8+
id: 'bwnFX5LoX7',
9+
type: 'paragraph',
10+
data: {
11+
text: 'The first block content mock.',
12+
},
13+
};
14+
const editorDataMock = {
15+
blocks: [
16+
firstBlock,
17+
],
18+
};
19+
20+
/**
21+
* EditorJS API is passed as the first parameter of the onChange callback
22+
*/
23+
const EditorJSApiMock = Cypress.sinon.match.any;
24+
25+
beforeEach(() => {
26+
if (this && this.editorInstance) {
27+
this.editorInstance.destroy();
28+
} else {
29+
const config = {
30+
data: editorDataMock,
31+
onChange: (): void => { console.log('something changed'); },
32+
};
33+
34+
cy.createEditor(config).as('editorInstance');
35+
36+
cy.spy(config, 'onChange').as('onChange');
37+
}
38+
});
39+
40+
/**
41+
* block.dispatchChange();
42+
*/
43+
describe('.dispatchChange()', () => {
44+
/**
45+
* Check that blocks.dispatchChange() triggers Editor 'onChange' callback
46+
*/
47+
it('should trigger onChange with corresponded block', () => {
48+
cy.get('@editorInstance').then(async (editor: any) => {
49+
const block = editor.blocks.getById(firstBlock.id);
50+
51+
block.dispatchChange();
52+
53+
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
54+
type: BlockMutationType.Changed,
55+
detail: {
56+
index: 0,
57+
},
58+
}));
59+
});
60+
});
61+
});
62+
63+
});

types/api/block.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,10 @@ export interface BlockAPI {
6767
* @return {Promise<boolean>}
6868
*/
6969
validate(data: BlockToolData): Promise<boolean>;
70+
71+
/**
72+
* Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback
73+
* Can be useful for block changes invisible for editor core.
74+
*/
75+
dispatchChange(): void;
7076
}

0 commit comments

Comments
 (0)