mirror of
https://github.com/codex-team/editor.js
synced 2024-06-15 20:25:15 +02:00
feat(onchange): callback now accepts custom event (#1791)
* feat(onchange): callback now accepts custom event * Delete block-added.ts * testd updated, changelog added * Update example-dev.html * indexes added to all events * block-removed dispatching on block replacing * Update example-dev.html
This commit is contained in:
parent
1433c1a7dc
commit
4f15bbc0cb
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
### 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
|
||||
|
||||
### 2.22.3
|
||||
|
||||
- `Fix` — Tool config is passed to `prepare` method [editor-js/embed#68](https://github.com/editor-js/embed/issues/68)
|
||||
|
|
|
@ -192,8 +192,9 @@ var editor = new EditorJS({
|
|||
|
||||
/**
|
||||
* onChange callback
|
||||
* Accepts CustomEvent describing what happened
|
||||
*/
|
||||
onChange: (editorAPI, affectedBlockAPI) => {console.log('Now I know that Editor\'s content changed!')}
|
||||
onChange: (editorAPI, event) => {console.log('Now I know that Editor\'s content changed!')}
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
@ -318,8 +318,8 @@
|
|||
onReady: function(){
|
||||
saveButton.click();
|
||||
},
|
||||
onChange: function(api, block) {
|
||||
console.log('something changed', block);
|
||||
onChange: function(api, event) {
|
||||
console.log('something changed', event);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -281,8 +281,8 @@
|
|||
onReady: function(){
|
||||
saveButton.click();
|
||||
},
|
||||
onChange: function(api, block) {
|
||||
console.log('something changed', block);
|
||||
onChange: function(api, event) {
|
||||
console.log('something changed', event);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@editorjs/editorjs",
|
||||
"version": "2.22.3",
|
||||
"version": "2.23.0",
|
||||
"description": "Editor.js — Native JS, based on API and Open Source",
|
||||
"main": "dist/editor.js",
|
||||
"types": "./types/index.d.ts",
|
||||
|
@ -29,7 +29,8 @@
|
|||
"_tools:build": "git submodule foreach yarn build",
|
||||
"_tools:make": "yarn _tools:yarn && yarn _tools:build",
|
||||
"tools:update": "yarn _tools:checkout && yarn _tools:pull && yarn _tools:make",
|
||||
"test:e2e": "yarn build && cypress run"
|
||||
"test:e2e": "yarn build && cypress run",
|
||||
"test:e2e:open": "yarn build && cypress open"
|
||||
},
|
||||
"author": "CodeX",
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -14,6 +14,7 @@ import Blocks from '../blocks';
|
|||
import { BlockToolData, PasteEvent } from '../../../types';
|
||||
import { BlockTuneData } from '../../../types/block-tunes/block-tune-data';
|
||||
import BlockAPI from '../block/api';
|
||||
import { BlockMutationType } from '../../../types/events/block/mutation-type';
|
||||
|
||||
/**
|
||||
* @typedef {BlockManager} BlockManager
|
||||
|
@ -289,12 +290,24 @@ export default class BlockManager extends Module {
|
|||
tunes,
|
||||
});
|
||||
|
||||
/**
|
||||
* In case of block replacing (Converting OR from Toolbox or Shortcut on empty block OR on-paste to empty block)
|
||||
* we need to dispatch the 'block-removing' event for the replacing block
|
||||
*/
|
||||
if (replace) {
|
||||
this.blockDidMutated(BlockMutationType.Removed, this.getBlockByIndex(newIndex), {
|
||||
index: newIndex,
|
||||
});
|
||||
}
|
||||
|
||||
this._blocks.insert(newIndex, block, replace);
|
||||
|
||||
/**
|
||||
* Force call of didMutated event on Block insertion
|
||||
*/
|
||||
this.blockDidMutated(block);
|
||||
this.blockDidMutated(BlockMutationType.Added, block, {
|
||||
index: newIndex,
|
||||
});
|
||||
|
||||
if (needToFocus) {
|
||||
this.currentBlockIndex = newIndex;
|
||||
|
@ -370,7 +383,9 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Force call of didMutated event on Block insertion
|
||||
*/
|
||||
this.blockDidMutated(block);
|
||||
this.blockDidMutated(BlockMutationType.Added, block, {
|
||||
index,
|
||||
});
|
||||
|
||||
if (needToFocus) {
|
||||
this.currentBlockIndex = index;
|
||||
|
@ -445,7 +460,9 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Force call of didMutated event on Block removal
|
||||
*/
|
||||
this.blockDidMutated(blockToRemove);
|
||||
this.blockDidMutated(BlockMutationType.Removed, blockToRemove, {
|
||||
index,
|
||||
});
|
||||
|
||||
if (this.currentBlockIndex >= index) {
|
||||
this.currentBlockIndex--;
|
||||
|
@ -721,7 +738,10 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Force call of didMutated event on Block movement
|
||||
*/
|
||||
this.blockDidMutated(this.currentBlock);
|
||||
this.blockDidMutated(BlockMutationType.Moved, this.currentBlock, {
|
||||
fromIndex,
|
||||
toIndex,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -788,7 +808,11 @@ export default class BlockManager extends Module {
|
|||
BlockEvents.dragLeave(event);
|
||||
});
|
||||
|
||||
block.on('didMutated', (affectedBlock: Block) => this.blockDidMutated(affectedBlock));
|
||||
block.on('didMutated', (affectedBlock: Block) => {
|
||||
return this.blockDidMutated(BlockMutationType.Changed, affectedBlock, {
|
||||
index: this.getBlockIndex(affectedBlock),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -828,10 +852,19 @@ export default class BlockManager extends Module {
|
|||
/**
|
||||
* Block mutation callback
|
||||
*
|
||||
* @param mutationType - what happened with block
|
||||
* @param block - mutated block
|
||||
* @param details - additional data to pass with change event
|
||||
*/
|
||||
private blockDidMutated(block: Block): Block {
|
||||
this.Editor.ModificationsObserver.onChange(new BlockAPI(block));
|
||||
private blockDidMutated(mutationType: BlockMutationType, block: Block, details: Record<string, unknown> = {}): Block {
|
||||
const event = new CustomEvent(mutationType, {
|
||||
detail: {
|
||||
target: new BlockAPI(block),
|
||||
...details,
|
||||
},
|
||||
});
|
||||
|
||||
this.Editor.ModificationsObserver.onChange(event);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Module from '../__module';
|
||||
import { BlockAPI } from '../../../types';
|
||||
import * as _ from '../utils';
|
||||
|
||||
/**
|
||||
|
@ -28,13 +27,13 @@ export default class ModificationsObserver extends Module {
|
|||
/**
|
||||
* Call onChange event passed to Editor.js configuration
|
||||
*
|
||||
* @param block - changed Block
|
||||
* @param event - some of our custom change events
|
||||
*/
|
||||
public onChange(block: BlockAPI): void {
|
||||
public onChange(event: CustomEvent): void {
|
||||
if (this.disabled || !_.isFunction(this.config.onChange)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.config.onChange(this.Editor.API.methods, block);
|
||||
this.config.onChange(this.Editor.API.methods, event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,98 @@
|
|||
import Header from '../../../example/tools/header';
|
||||
import { BlockMutationType } from '../../../types/events/block/mutation-type';
|
||||
|
||||
/**
|
||||
* @todo Add checks that correct block API object is passed to onChange
|
||||
* @todo Add cases for native inputs changes
|
||||
* @todo debug onChange firing on Block Tune toggling (see below)
|
||||
*/
|
||||
describe('onChange callback', () => {
|
||||
/**
|
||||
* Creates Editor instance
|
||||
*
|
||||
* @param blocks - list of blocks to prefill the editor
|
||||
*/
|
||||
function createEditor(blocks = null): void {
|
||||
const config = {
|
||||
tools: {
|
||||
header: Header,
|
||||
},
|
||||
onChange: (): void => {
|
||||
console.log('something changed');
|
||||
onChange: (api, event): void => {
|
||||
console.log('something changed', api, event);
|
||||
},
|
||||
data: blocks ? {
|
||||
blocks,
|
||||
} : null,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
if (this && this.editorInstance) {
|
||||
this.editorInstance.destroy();
|
||||
} else {
|
||||
cy.spy(config, 'onChange').as('onChange');
|
||||
|
||||
cy.createEditor(config).as('editorInstance');
|
||||
}
|
||||
});
|
||||
|
||||
it('should fire onChange callback on block insertion', () => {
|
||||
/**
|
||||
* EditorJS API is passed as the first parameter of the onChange callback
|
||||
*/
|
||||
const EditorJSApiMock = Cypress.sinon.match.any;
|
||||
|
||||
it('should fire onChange callback with correct index on block insertion above the current (by pressing Enter at the start)', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click()
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('@onChange').should('be.called');
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Added,
|
||||
detail: {
|
||||
target: {
|
||||
name: 'paragraph'
|
||||
},
|
||||
index: 0,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback with correct index on block insertion below the current (by pressing enter at the end)', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click()
|
||||
.type('some text')
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Added,
|
||||
detail: {
|
||||
target: {
|
||||
name: 'paragraph'
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback on typing into block', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click()
|
||||
.type('some text');
|
||||
|
||||
cy.get('@onChange').should('be.called');
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Changed,
|
||||
detail: {
|
||||
index: 0
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback on block replacement', () => {
|
||||
it('should fire onChange callback on block replacement for both of blocks', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click();
|
||||
|
@ -55,21 +105,36 @@ describe('onChange callback', () => {
|
|||
.get('li.ce-toolbox__button[data-tool=header]')
|
||||
.click();
|
||||
|
||||
cy.get('@onChange').should('be.calledWithMatch', Cypress.sinon.match.any, Cypress.sinon.match({ name: 'header' }));
|
||||
cy.get('@onChange').should('be.calledTwice');
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Removed,
|
||||
detail: {
|
||||
index: 0,
|
||||
target: {
|
||||
name: 'paragraph',
|
||||
},
|
||||
},
|
||||
}));
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Added,
|
||||
detail: {
|
||||
index: 0,
|
||||
target: {
|
||||
name: 'header',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback on tune modifier', () => {
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-toolbar__plus')
|
||||
.click();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('li.ce-toolbox__button[data-tool=header]')
|
||||
.click();
|
||||
it('should fire onChange callback on tune modifying', () => {
|
||||
createEditor([
|
||||
{
|
||||
type: 'header',
|
||||
data: {
|
||||
text: 'Header block',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
|
@ -80,13 +145,30 @@ describe('onChange callback', () => {
|
|||
.click();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('span.cdx-settings-button[data-level=1]')
|
||||
.get('span.cdx-settings-button[data-level=4]')
|
||||
.click()
|
||||
/**
|
||||
* For some reason, the first click fires the mutation of removeFakeCursor only, so we need to click again.
|
||||
* Reproduced only in Cypress.
|
||||
*
|
||||
* @todo debug it later
|
||||
*/
|
||||
.click();
|
||||
|
||||
cy.get('@onChange').should('be.calledWithMatch', Cypress.sinon.match.any, Cypress.sinon.match({ name: 'header' }));
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Changed,
|
||||
detail: {
|
||||
index: 0,
|
||||
target: {
|
||||
name: 'header',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback when block is removed', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click();
|
||||
|
@ -100,10 +182,17 @@ describe('onChange callback', () => {
|
|||
.click()
|
||||
.click();
|
||||
|
||||
cy.get('@onChange').should('be.called');
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Removed,
|
||||
detail: {
|
||||
index: 0
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('should fire onChange callback when block is moved', () => {
|
||||
createEditor();
|
||||
|
||||
cy.get('[data-cy=editorjs]')
|
||||
.get('div.ce-block')
|
||||
.click()
|
||||
|
@ -122,6 +211,12 @@ describe('onChange callback', () => {
|
|||
.get('div.ce-tune-move-up')
|
||||
.click();
|
||||
|
||||
cy.get('@onChange').should('be.called');
|
||||
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||
type: BlockMutationType.Moved,
|
||||
detail: {
|
||||
fromIndex: 1,
|
||||
toIndex: 0,
|
||||
},
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
4
types/configs/editor-config.d.ts
vendored
4
types/configs/editor-config.d.ts
vendored
|
@ -90,9 +90,9 @@ export interface EditorConfig {
|
|||
/**
|
||||
* Fires when something changed in DOM
|
||||
* @param {API} api - editor.js api
|
||||
* @param block - changed block API
|
||||
* @param event - custom event describing mutation
|
||||
*/
|
||||
onChange?(api: API, block: BlockAPI): void;
|
||||
onChange?(api: API, event: CustomEvent): void;
|
||||
|
||||
/**
|
||||
* Defines default toolbar for all tools.
|
||||
|
|
24
types/events/block/mutation-type.ts
Normal file
24
types/events/block/mutation-type.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* What kind of modification happened with the Block
|
||||
*/
|
||||
export enum BlockMutationType {
|
||||
/**
|
||||
* New Block added
|
||||
*/
|
||||
Added = 'block-added',
|
||||
|
||||
/**
|
||||
* On Block deletion
|
||||
*/
|
||||
Removed = 'block-removed',
|
||||
|
||||
/**
|
||||
* Moving of a Block
|
||||
*/
|
||||
Moved = 'block-moved',
|
||||
|
||||
/**
|
||||
* Any changes inside the Block
|
||||
*/
|
||||
Changed = 'block-changed',
|
||||
}
|
Loading…
Reference in a new issue