mirror of
https://github.com/codex-team/editor.js
synced 2024-06-20 22:55:15 +02:00
fix(renderer): handle empty array as data.blocks (#2454)
This commit is contained in:
parent
59c8d28da5
commit
0369ff5827
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 2.29.0
|
||||||
|
|
||||||
|
- `Fix` — Passing an empty array via initial data or `blocks.render()` won't break the editor
|
||||||
|
|
||||||
### 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 to 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 to access a Block's element by id.
|
||||||
|
|
|
@ -323,7 +323,7 @@ export default class Blocks {
|
||||||
* @param {number} index — Block index
|
* @param {number} index — Block index
|
||||||
* @returns {Block}
|
* @returns {Block}
|
||||||
*/
|
*/
|
||||||
public get(index: number): Block {
|
public get(index: number): Block | undefined {
|
||||||
return this.blocks[index];
|
return this.blocks[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -587,13 +587,28 @@ export default class BlockManager extends Module {
|
||||||
return this.insert({ data });
|
return this.insert({ data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Block by passed index
|
||||||
|
*
|
||||||
|
* If we pass -1 as index, the last block will be returned
|
||||||
|
* There shouldn't be a case when there is no blocks at all — at least one always should exist
|
||||||
|
*/
|
||||||
|
public getBlockByIndex(index: -1): Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Block by passed index.
|
||||||
|
*
|
||||||
|
* Could return undefined if there is no block with such index
|
||||||
|
*/
|
||||||
|
public getBlockByIndex(index: number): Block | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns Block by passed index
|
* Returns Block by passed index
|
||||||
*
|
*
|
||||||
* @param {number} index - index to get. -1 to get last
|
* @param {number} index - index to get. -1 to get last
|
||||||
* @returns {Block}
|
* @returns {Block}
|
||||||
*/
|
*/
|
||||||
public getBlockByIndex(index): Block {
|
public getBlockByIndex(index: number): Block | undefined {
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
index = this._blocks.length - 1;
|
index = this._blocks.length - 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ export default class Renderer extends Module {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const { Tools, BlockManager } = this.Editor;
|
const { Tools, BlockManager } = this.Editor;
|
||||||
|
|
||||||
|
if (blocksData.length === 0) {
|
||||||
|
BlockManager.insert();
|
||||||
|
} else {
|
||||||
/**
|
/**
|
||||||
* Create Blocks instances
|
* Create Blocks instances
|
||||||
*/
|
*/
|
||||||
|
@ -65,6 +68,7 @@ export default class Renderer extends Module {
|
||||||
* Insert batch of Blocks
|
* Insert batch of Blocks
|
||||||
*/
|
*/
|
||||||
BlockManager.insertMany(blocks);
|
BlockManager.insertMany(blocks);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait till browser will render inserted Blocks and resolve a promise
|
* Wait till browser will render inserted Blocks and resolve a promise
|
||||||
|
|
|
@ -694,17 +694,10 @@ export default class UI extends Module<UINodes> {
|
||||||
* - otherwise, add a new empty Block and set a Caret to that
|
* - otherwise, add a new empty Block and set a Caret to that
|
||||||
*/
|
*/
|
||||||
private redactorClicked(event: MouseEvent): void {
|
private redactorClicked(event: MouseEvent): void {
|
||||||
const { BlockSelection } = this.Editor;
|
|
||||||
|
|
||||||
if (!Selection.isCollapsed) {
|
if (!Selection.isCollapsed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopPropagation = (): void => {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
event.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* case when user clicks on anchor element
|
* case when user clicks on anchor element
|
||||||
* if it is clicked via ctrl key, then we open new window with url
|
* if it is clicked via ctrl key, then we open new window with url
|
||||||
|
@ -713,7 +706,8 @@ export default class UI extends Module<UINodes> {
|
||||||
const ctrlKey = event.metaKey || event.ctrlKey;
|
const ctrlKey = event.metaKey || event.ctrlKey;
|
||||||
|
|
||||||
if ($.isAnchor(element) && ctrlKey) {
|
if ($.isAnchor(element) && ctrlKey) {
|
||||||
stopPropagation();
|
event.stopImmediatePropagation();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
const href = element.getAttribute('href');
|
const href = element.getAttribute('href');
|
||||||
const validUrl = _.getValidUrl(href);
|
const validUrl = _.getValidUrl(href);
|
||||||
|
@ -723,10 +717,22 @@ export default class UI extends Module<UINodes> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.processBottomZoneClick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user clicks on the Editor's bottom zone:
|
||||||
|
* - set caret to the last block
|
||||||
|
* - or add new empty block
|
||||||
|
*
|
||||||
|
* @param event - click event
|
||||||
|
*/
|
||||||
|
private processBottomZoneClick(event: MouseEvent): void {
|
||||||
const lastBlock = this.Editor.BlockManager.getBlockByIndex(-1);
|
const lastBlock = this.Editor.BlockManager.getBlockByIndex(-1);
|
||||||
|
|
||||||
const lastBlockBottomCoord = $.offset(lastBlock.holder).bottom;
|
const lastBlockBottomCoord = $.offset(lastBlock.holder).bottom;
|
||||||
const clickedCoord = event.pageY;
|
const clickedCoord = event.pageY;
|
||||||
|
const { BlockSelection } = this.Editor;
|
||||||
const isClickedBottom = event.target instanceof Element &&
|
const isClickedBottom = event.target instanceof Element &&
|
||||||
event.target.isEqualNode(this.nodes.redactor) &&
|
event.target.isEqualNode(this.nodes.redactor) &&
|
||||||
/**
|
/**
|
||||||
|
@ -740,7 +746,8 @@ export default class UI extends Module<UINodes> {
|
||||||
lastBlockBottomCoord < clickedCoord;
|
lastBlockBottomCoord < clickedCoord;
|
||||||
|
|
||||||
if (isClickedBottom) {
|
if (isClickedBottom) {
|
||||||
stopPropagation();
|
event.stopImmediatePropagation();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
const { BlockManager, Caret, Toolbar } = this.Editor;
|
const { BlockManager, Caret, Toolbar } = this.Editor;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import ToolMock from '../../fixtures/tools/ToolMock';
|
import ToolMock from '../../fixtures/tools/ToolMock';
|
||||||
|
import type EditorJS from '../../../../types/index';
|
||||||
|
|
||||||
describe('Renderer module', function () {
|
describe('Renderer module', function () {
|
||||||
it('should not cause onChange firing during initial rendering', function () {
|
it('should not cause onChange firing during initial rendering', function () {
|
||||||
|
@ -146,4 +147,33 @@ describe('Renderer module', function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should insert default empty block when [] passed as data.blocks', function () {
|
||||||
|
cy.createEditor({
|
||||||
|
data: {
|
||||||
|
blocks: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.as('editorInstance');
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.find('.ce-block')
|
||||||
|
.should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert default empty block when [] passed via blocks.render() API', function () {
|
||||||
|
cy.createEditor({})
|
||||||
|
.as('editorInstance');
|
||||||
|
|
||||||
|
cy.get<EditorJS>('@editorInstance')
|
||||||
|
.then((editor) => {
|
||||||
|
editor.blocks.render({
|
||||||
|
blocks: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.find('.ce-block')
|
||||||
|
.should('have.length', 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue