mirror of
https://github.com/codex-team/editor.js
synced 2024-05-18 14:26:48 +02:00
chore(api): blocks.update(id, data) method improved (#2443)
* add custom Chai assertion "be.calledWithBatchedEvents" for testing onchange * chore(api): blocks.update(id, data) method improved - `blocks.update(id, data)` now can accept partial data object — it will update only passed properties, others will remain the same. - `blocks.update(id, data)` now will trigger onChange with only `block-change` event. - `blocks.update(id, data)` will return a promise with BlockAPI object of changed block. * fix tests * Update blocks.cy.ts
This commit is contained in:
parent
b39996616c
commit
922dfd8741
|
@ -3,8 +3,8 @@
|
||||||
### 2.28.0
|
### 2.28.0
|
||||||
|
|
||||||
- `New` - Block ids now displayed in DOM via a data-id attribute. Could be useful for plugins that want access a Block's element by id.
|
- `New` - Block ids now displayed in DOM via a data-id attribute. Could be useful for plugins that want access a Block's element by id.
|
||||||
- `New` - The `blocks.convert(blockId, newType)` API method added. It allows to convert existed Block to a Block of another type.
|
- `New` - The `blocks.convert(blockId, newType)` API method added. It allows to convert existing Block to a Block of another type.
|
||||||
- `New` - The `blocks.insertMany()` API method added. It allows to insert several Blocks to specified index.
|
- `New` - The `blocks.insertMany()` API method added. It allows to insert several Blocks to the specified index.
|
||||||
- `Improvement` - The Delete keydown at the end of the Block will now work opposite a Backspace at the start. Next Block will be removed (if empty) or merged with the current one.
|
- `Improvement` - The Delete keydown at the end of the Block will now work opposite a Backspace at the start. Next Block will be removed (if empty) or merged with the current one.
|
||||||
- `Improvement` - The Delete keydown will work like a Backspace when several Blocks are selected.
|
- `Improvement` - The Delete keydown will work like a Backspace when several Blocks are selected.
|
||||||
- `Improvement` - If we have two empty Blocks, and press Backspace at the start of the second one, the previous will be removed instead of current.
|
- `Improvement` - If we have two empty Blocks, and press Backspace at the start of the second one, the previous will be removed instead of current.
|
||||||
|
@ -17,7 +17,11 @@
|
||||||
- `Improvement` - "I'm ready" log removed
|
- `Improvement` - "I'm ready" log removed
|
||||||
- `Improvement` - The stub-block style simplified.
|
- `Improvement` - The stub-block style simplified.
|
||||||
- `Improvement` - If some Block's tool will throw an error during construction, we will show Stub block instead of skipping it during render
|
- `Improvement` - If some Block's tool will throw an error during construction, we will show Stub block instead of skipping it during render
|
||||||
- `Improvement` - Call of `blocks.clear()` now will trigger onChange will "block-removed" event for all removed blocks.
|
- `Improvement` - Call of `blocks.clear()` now will trigger onChange with "block-removed" event for all removed blocks.
|
||||||
|
- `Improvement` - `BlockMutationType` and `BlockMutationEvent` types exported
|
||||||
|
- `Improvement` - `blocks.update(id, data)` now can accept partial data object — it will update only passed properties, others will remain the same.
|
||||||
|
- `Improvement` - `blocks.update(id, data)` now will trigger onChange with only `block-change` event.
|
||||||
|
- `Improvement` - `blocks.update(id, data)` will return a promise with BlockAPI object of changed block.
|
||||||
|
|
||||||
### 2.27.2
|
### 2.27.2
|
||||||
|
|
||||||
|
|
|
@ -220,6 +220,24 @@ export default class Blocks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces block under passed index with passed block
|
||||||
|
*
|
||||||
|
* @param index - index of existed block
|
||||||
|
* @param block - new block
|
||||||
|
*/
|
||||||
|
public replace(index: number, block: Block): void {
|
||||||
|
if (this.blocks[index] === undefined) {
|
||||||
|
throw Error('Incorrect index');
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevBlock = this.blocks[index];
|
||||||
|
|
||||||
|
prevBlock.holder.replaceWith(block.holder);
|
||||||
|
|
||||||
|
this.blocks[index] = block;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts several blocks at once
|
* Inserts several blocks at once
|
||||||
*
|
*
|
||||||
|
|
|
@ -297,26 +297,19 @@ export default class BlocksAPI extends Module {
|
||||||
* @param id - id of the block to update
|
* @param id - id of the block to update
|
||||||
* @param data - the new data
|
* @param data - the new data
|
||||||
*/
|
*/
|
||||||
public update = (id: string, data: BlockToolData): void => {
|
public update = async (id: string, data: Partial<BlockToolData>): Promise<BlockAPIInterface> => {
|
||||||
const { BlockManager } = this.Editor;
|
const { BlockManager } = this.Editor;
|
||||||
const block = BlockManager.getBlockById(id);
|
const block = BlockManager.getBlockById(id);
|
||||||
|
|
||||||
if (!block) {
|
if (block === undefined) {
|
||||||
_.log('blocks.update(): Block with passed id was not found', 'warn');
|
throw new Error(`Block with id "${id}" not found`);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockIndex = BlockManager.getBlockIndex(block);
|
const updatedBlock = await BlockManager.update(block, data);
|
||||||
|
|
||||||
BlockManager.insert({
|
// we cast to any because our BlockAPI has no "new" signature
|
||||||
id: block.id,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
tool: block.name,
|
return new (BlockAPI as any)(updatedBlock);
|
||||||
data,
|
|
||||||
index: blockIndex,
|
|
||||||
replace: true,
|
|
||||||
tunes: block.tunes,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -333,6 +333,36 @@ export default class BlockManager extends Module {
|
||||||
this._blocks.insertMany(blocks, index);
|
this._blocks.insertMany(blocks, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Block data.
|
||||||
|
*
|
||||||
|
* Currently we don't have an 'update' method in the Tools API, so we just create a new block with the same id and type
|
||||||
|
* Should not trigger 'block-removed' or 'block-added' events
|
||||||
|
*
|
||||||
|
* @param block - block to update
|
||||||
|
* @param data - new data
|
||||||
|
*/
|
||||||
|
public async update(block: Block, data: Partial<BlockToolData>): Promise<Block> {
|
||||||
|
const existingData = await block.data;
|
||||||
|
|
||||||
|
const newBlock = this.composeBlock({
|
||||||
|
id: block.id,
|
||||||
|
tool: block.name,
|
||||||
|
data: Object.assign({}, existingData, data),
|
||||||
|
tunes: block.tunes,
|
||||||
|
});
|
||||||
|
|
||||||
|
const blockIndex = this.getBlockIndex(block);
|
||||||
|
|
||||||
|
this._blocks.replace(blockIndex, newBlock);
|
||||||
|
|
||||||
|
this.blockDidMutated(BlockChangedMutationType, newBlock, {
|
||||||
|
index: blockIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
return newBlock;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace passed Block with the new one with specified Tool and data
|
* Replace passed Block with the new one with specified Tool and data
|
||||||
*
|
*
|
||||||
|
|
16
test/cypress/fixtures/types/PartialBlockMutationEvent.ts
Normal file
16
test/cypress/fixtures/types/PartialBlockMutationEvent.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { BlockMutationEvent, BlockMutationType } from '../../../../types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplified version of the BlockMutationEvent with optional fields that could be used in tests
|
||||||
|
*/
|
||||||
|
export default interface PartialBlockMutationEvent {
|
||||||
|
/**
|
||||||
|
* Event type
|
||||||
|
*/
|
||||||
|
type?: BlockMutationType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details with partial properties
|
||||||
|
*/
|
||||||
|
detail?: Partial<BlockMutationEvent['detail']>
|
||||||
|
}
|
59
test/cypress/support/e2e.ts
Normal file
59
test/cypress/support/e2e.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
/* global chai */
|
||||||
|
// because this file is imported from cypress/support/e2e.js
|
||||||
|
// that means all other spec files will have this assertion plugin
|
||||||
|
// available to them because the supportFile is bundled and served
|
||||||
|
// prior to any spec files loading
|
||||||
|
|
||||||
|
import PartialBlockMutationEvent from '../fixtures/types/PartialBlockMutationEvent';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chai plugin for checking if passed onChange method is called with an array of passed events
|
||||||
|
*
|
||||||
|
* @param _chai - Chai instance
|
||||||
|
*/
|
||||||
|
const beCalledWithBatchedEvents = (_chai): void => {
|
||||||
|
/**
|
||||||
|
* Check if passed onChange method is called with an array of passed events
|
||||||
|
*
|
||||||
|
* @param expectedEvents - batched events to check
|
||||||
|
*/
|
||||||
|
function assertToBeCalledWithBatchedEvents(expectedEvents: PartialBlockMutationEvent[]): void {
|
||||||
|
/**
|
||||||
|
* EditorJS API is passed as the first parameter of the onChange callback
|
||||||
|
*/
|
||||||
|
const EditorJSApiMock = Cypress.sinon.match.any;
|
||||||
|
const $onChange = this._obj;
|
||||||
|
|
||||||
|
this.assert(
|
||||||
|
$onChange.calledOnce,
|
||||||
|
'expected #{this} to be called once',
|
||||||
|
'expected #{this} to not be called once'
|
||||||
|
);
|
||||||
|
|
||||||
|
this.assert(
|
||||||
|
$onChange.calledWithMatch(
|
||||||
|
EditorJSApiMock,
|
||||||
|
Cypress.sinon.match((events: PartialBlockMutationEvent[]) => {
|
||||||
|
expect(events).to.be.an('array');
|
||||||
|
|
||||||
|
return events.every((event, index) => {
|
||||||
|
const eventToCheck = expectedEvents[index];
|
||||||
|
|
||||||
|
return expect(event).to.containSubset(eventToCheck);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
|
'expected #{this} to be called with #{exp}, but it was called with #{act}',
|
||||||
|
'expected #{this} to not be called with #{exp}, but it was called with #{act} ',
|
||||||
|
expectedEvents
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_chai.Assertion.addMethod('calledWithBatchedEvents', assertToBeCalledWithBatchedEvents);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* registers our assertion function "beCalledWithBatchedEvents" with Chai
|
||||||
|
*/
|
||||||
|
chai.use(beCalledWithBatchedEvents);
|
29
test/cypress/support/index.d.ts
vendored
29
test/cypress/support/index.d.ts
vendored
|
@ -1,9 +1,9 @@
|
||||||
// in cypress/support/index.d.ts
|
|
||||||
// load type definitions that come with Cypress module
|
// load type definitions that come with Cypress module
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
import type { EditorConfig, OutputData } from './../../../types/index';
|
import type { EditorConfig, OutputData } from './../../../types/index';
|
||||||
import type EditorJS from '../../../types/index'
|
import type EditorJS from '../../../types/index'
|
||||||
|
import PartialBlockMutationEvent from '../fixtures/types/PartialBlockMutationEvent';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Cypress {
|
namespace Cypress {
|
||||||
|
@ -65,6 +65,22 @@ declare global {
|
||||||
interface ApplicationWindow {
|
interface ApplicationWindow {
|
||||||
EditorJS: typeof EditorJS
|
EditorJS: typeof EditorJS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends Cypress assertion Chainer interface with the new assertion methods
|
||||||
|
*/
|
||||||
|
interface Chainer<Subject> {
|
||||||
|
/**
|
||||||
|
* Custom Chai assertion that checks if given onChange method is called with an array of passed events
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* cy.get('@onChange').should('be.calledWithBatchedEvents', [{ type: 'block-added', detail: { index: 0 }}])
|
||||||
|
* expect(onChange).to.be.calledWithBatchedEvents([{ type: 'block-added', detail: { index: 0 }}])
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
(chainer: 'be.calledWithBatchedEvents', expectedEvents: PartialBlockMutationEvent[]): Chainable<Subject>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,6 +92,17 @@ declare global {
|
||||||
* "containSubset" object properties matcher
|
* "containSubset" object properties matcher
|
||||||
*/
|
*/
|
||||||
containSubset(subset: any): Assertion;
|
containSubset(subset: any): Assertion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Chai assertion that checks if given onChange method is called with an array of passed events
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```
|
||||||
|
* cy.get('@onChange').should('be.calledWithBatchedEvents', [{ type: 'block-added', detail: { index: 0 }}])
|
||||||
|
* expect(onChange).to.be.calledWithBatchedEvents([{ type: 'block-added', detail: { index: 0 }}])
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
calledWithBatchedEvents(expectedEvents: PartialBlockMutationEvent[]): Assertion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,11 @@ installLogsCollector();
|
||||||
*/
|
*/
|
||||||
import './commands';
|
import './commands';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File with custom assertions
|
||||||
|
*/
|
||||||
|
import './e2e';
|
||||||
|
|
||||||
import chaiSubset from 'chai-subset';
|
import chaiSubset from 'chai-subset';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -114,13 +114,17 @@ describe('api.blocks', () => {
|
||||||
text: 'Updated text',
|
text: 'Updated text',
|
||||||
};
|
};
|
||||||
|
|
||||||
editor.blocks.update(idToUpdate, newBlockData);
|
editor.blocks.update(idToUpdate, newBlockData)
|
||||||
|
.catch(error => {
|
||||||
cy.get('[data-cy=editorjs]')
|
expect(error.message).to.be.eq(`Block with id "${idToUpdate}" not found`);
|
||||||
.get('div.ce-block')
|
})
|
||||||
.invoke('text')
|
.finally(() => {
|
||||||
.then(blockText => {
|
cy.get('[data-cy=editorjs]')
|
||||||
expect(blockText).to.be.eq(firstBlock.data.text);
|
.get('div.ce-block')
|
||||||
|
.invoke('text')
|
||||||
|
.then(blockText => {
|
||||||
|
expect(blockText).to.be.eq(firstBlock.data.text);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -161,7 +165,7 @@ describe('api.blocks', () => {
|
||||||
{
|
{
|
||||||
type: 'paragraph',
|
type: 'paragraph',
|
||||||
data: { text: 'first block' },
|
data: { text: 'first block' },
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}).then((editor) => {
|
}).then((editor) => {
|
||||||
|
|
|
@ -7,32 +7,11 @@ import { BlockRemovedMutationType } from '../../../types/events/block/BlockRemov
|
||||||
import { BlockMovedMutationType } from '../../../types/events/block/BlockMoved';
|
import { BlockMovedMutationType } from '../../../types/events/block/BlockMoved';
|
||||||
import type EditorJS from '../../../types/index';
|
import type EditorJS from '../../../types/index';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EditorJS API is passed as the first parameter of the onChange callback
|
* EditorJS API is passed as the first parameter of the onChange callback
|
||||||
*/
|
*/
|
||||||
const EditorJSApiMock = Cypress.sinon.match.any;
|
const EditorJSApiMock = Cypress.sinon.match.any;
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if passed onChange method is called with an array of passed events
|
|
||||||
*
|
|
||||||
* @param $onChange - editor onChange spy
|
|
||||||
* @param expectedEvents - batched events to check
|
|
||||||
*/
|
|
||||||
function beCalledWithBatchedEvents($onChange, expectedEvents): void {
|
|
||||||
expect($onChange).to.be.calledOnce;
|
|
||||||
expect($onChange).to.be.calledWithMatch(
|
|
||||||
EditorJSApiMock,
|
|
||||||
Cypress.sinon.match((events) => {
|
|
||||||
return events.every((event, index) => {
|
|
||||||
const eventToCheck = expectedEvents[index];
|
|
||||||
|
|
||||||
return expect(event).to.containSubset(eventToCheck);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Add checks that correct block API object is passed to onChange
|
* @todo Add checks that correct block API object is passed to onChange
|
||||||
* @todo Add cases for native inputs changes
|
* @todo Add cases for native inputs changes
|
||||||
|
@ -105,22 +84,20 @@ describe('onChange callback', () => {
|
||||||
.type('change')
|
.type('change')
|
||||||
.type('{enter}');
|
.type('{enter}');
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
{
|
||||||
{
|
type: BlockChangedMutationType,
|
||||||
type: BlockChangedMutationType,
|
detail: {
|
||||||
detail: {
|
index: 0,
|
||||||
index: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: BlockAddedMutationType,
|
{
|
||||||
detail: {
|
type: BlockAddedMutationType,
|
||||||
index: 1,
|
detail: {
|
||||||
},
|
index: 1,
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter out similar events on batching', () => {
|
it('should filter out similar events on batching', () => {
|
||||||
|
@ -243,37 +220,35 @@ describe('onChange callback', () => {
|
||||||
.get('div.ce-popover-item[data-item-name=delimiter]')
|
.get('div.ce-popover-item[data-item-name=delimiter]')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
detail: {
|
||||||
detail: {
|
index: 0,
|
||||||
index: 0,
|
target: {
|
||||||
target: {
|
name: 'paragraph',
|
||||||
name: 'paragraph',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: BlockAddedMutationType,
|
{
|
||||||
detail: {
|
type: BlockAddedMutationType,
|
||||||
index: 0,
|
detail: {
|
||||||
target: {
|
index: 0,
|
||||||
name: 'delimiter',
|
target: {
|
||||||
},
|
name: 'delimiter',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: BlockAddedMutationType,
|
{
|
||||||
detail: {
|
type: BlockAddedMutationType,
|
||||||
index: 1,
|
detail: {
|
||||||
target: {
|
index: 1,
|
||||||
name: 'paragraph',
|
target: {
|
||||||
},
|
name: 'paragraph',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be fired on block replacement for both of blocks', () => {
|
it('should be fired on block replacement for both of blocks', () => {
|
||||||
|
@ -291,28 +266,26 @@ describe('onChange callback', () => {
|
||||||
.get('div.ce-popover-item[data-item-name=header]')
|
.get('div.ce-popover-item[data-item-name=header]')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
detail: {
|
||||||
detail: {
|
index: 0,
|
||||||
index: 0,
|
target: {
|
||||||
target: {
|
name: 'paragraph',
|
||||||
name: 'paragraph',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: BlockAddedMutationType,
|
{
|
||||||
detail: {
|
type: BlockAddedMutationType,
|
||||||
index: 0,
|
detail: {
|
||||||
target: {
|
index: 0,
|
||||||
name: 'header',
|
target: {
|
||||||
},
|
name: 'header',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be fired on tune modifying', () => {
|
it('should be fired on tune modifying', () => {
|
||||||
|
@ -375,28 +348,26 @@ describe('onChange callback', () => {
|
||||||
.get('div[data-item-name=delete]')
|
.get('div[data-item-name=delete]')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
/**
|
||||||
/**
|
* "block-removed" fired since we have deleted a block
|
||||||
* "block-removed" fired since we have deleted a block
|
*/
|
||||||
*/
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
detail: {
|
||||||
detail: {
|
index: 0,
|
||||||
index: 0,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
/**
|
},
|
||||||
* "block-added" fired since we have deleted the last block, so the new one is created
|
/**
|
||||||
*/
|
* "block-added" fired since we have deleted the last block, so the new one is created
|
||||||
{
|
*/
|
||||||
type: BlockAddedMutationType,
|
{
|
||||||
detail: {
|
type: BlockAddedMutationType,
|
||||||
index: 0,
|
detail: {
|
||||||
},
|
index: 0,
|
||||||
},
|
},
|
||||||
]);
|
},
|
||||||
});
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be fired when block is moved', () => {
|
it('should be fired when block is moved', () => {
|
||||||
|
@ -594,19 +565,17 @@ describe('onChange callback', () => {
|
||||||
cy.wrap(editor.blocks.clear());
|
cy.wrap(editor.blocks.clear());
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
},
|
||||||
},
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
},
|
||||||
},
|
{
|
||||||
{
|
type: BlockAddedMutationType,
|
||||||
type: BlockAddedMutationType,
|
},
|
||||||
},
|
]);
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be called on blocks.render() on non-empty editor with removed blocks', () => {
|
it('should be called on blocks.render() on non-empty editor with removed blocks', () => {
|
||||||
|
@ -639,15 +608,52 @@ describe('onChange callback', () => {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('@onChange').should(($callback) => {
|
cy.get('@onChange').should('be.calledWithBatchedEvents', [
|
||||||
return beCalledWithBatchedEvents($callback, [
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
},
|
||||||
},
|
{
|
||||||
{
|
type: BlockRemovedMutationType,
|
||||||
type: BlockRemovedMutationType,
|
},
|
||||||
},
|
]);
|
||||||
]);
|
});
|
||||||
});
|
|
||||||
|
it('should be called on blocks.update() with "block-changed" event', () => {
|
||||||
|
const block = {
|
||||||
|
id: 'bwnFX5LoX7',
|
||||||
|
type: 'paragraph',
|
||||||
|
data: {
|
||||||
|
text: 'The first block mock.',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const config = {
|
||||||
|
data: {
|
||||||
|
blocks: [
|
||||||
|
block,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
onChange: (api, event): void => {
|
||||||
|
console.log('something changed', event);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cy.spy(config, 'onChange').as('onChange');
|
||||||
|
|
||||||
|
cy.createEditor(config)
|
||||||
|
.then((editor) => {
|
||||||
|
editor.blocks.update(block.id, {
|
||||||
|
text: 'Updated text',
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
|
||||||
|
type: BlockChangedMutationType,
|
||||||
|
detail: {
|
||||||
|
index: 0,
|
||||||
|
target: {
|
||||||
|
id: block.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
4
types/api/blocks.d.ts
vendored
4
types/api/blocks.d.ts
vendored
|
@ -134,9 +134,9 @@ export interface Blocks {
|
||||||
* Updates block data by id
|
* Updates block data by id
|
||||||
*
|
*
|
||||||
* @param id - id of the block to update
|
* @param id - id of the block to update
|
||||||
* @param data - the new data
|
* @param data - the new data. Can be partial.
|
||||||
*/
|
*/
|
||||||
update(id: string, data: BlockToolData): void;
|
update(id: string, data: Partial<BlockToolData>): Promise<BlockAPI>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts block to another type. Both blocks should provide the conversionConfig.
|
* Converts block to another type. Both blocks should provide the conversionConfig.
|
||||||
|
|
4
types/index.d.ts
vendored
4
types/index.d.ts
vendored
|
@ -31,7 +31,7 @@ import {
|
||||||
} from './api';
|
} from './api';
|
||||||
|
|
||||||
import { OutputData } from './data-formats';
|
import { OutputData } from './data-formats';
|
||||||
import { BlockMutationEventMap } from './events/block';
|
import { BlockMutationEvent, BlockMutationEventMap, BlockMutationType } from './events/block';
|
||||||
import { BlockAddedMutationType, BlockAddedEvent } from './events/block/BlockAdded';
|
import { BlockAddedMutationType, BlockAddedEvent } from './events/block/BlockAdded';
|
||||||
import { BlockChangedMutationType, BlockChangedEvent } from './events/block/BlockChanged';
|
import { BlockChangedMutationType, BlockChangedEvent } from './events/block/BlockChanged';
|
||||||
import { BlockMovedMutationType, BlockMovedEvent } from './events/block/BlockMoved';
|
import { BlockMovedMutationType, BlockMovedEvent } from './events/block/BlockMoved';
|
||||||
|
@ -85,6 +85,8 @@ export { OutputData, OutputBlockData} from './data-formats/output-data';
|
||||||
export { BlockId } from './data-formats/block-id';
|
export { BlockId } from './data-formats/block-id';
|
||||||
export { BlockAPI } from './api'
|
export { BlockAPI } from './api'
|
||||||
export {
|
export {
|
||||||
|
BlockMutationType,
|
||||||
|
BlockMutationEvent,
|
||||||
BlockMutationEventMap,
|
BlockMutationEventMap,
|
||||||
BlockAddedMutationType,
|
BlockAddedMutationType,
|
||||||
BlockAddedEvent,
|
BlockAddedEvent,
|
||||||
|
|
Loading…
Reference in a new issue