Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/components/modules/blockManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,13 +689,15 @@ export default class BlockManager extends Module {
element = element.parentNode as HTMLElement;
}

const nodes = this._blocks.nodes,
firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`),
index = nodes.indexOf(firstLevelBlock as HTMLElement);
const firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`);

if (index >= 0) {
return this._blocks[index];
}
/**
* Resolve the Block by holder identity (as getBlockByChildNode does) instead of
* indexing into the working area's children: the working area may contain
* non-block elements (e.g. decoration nodes added by the host application),
* which would skew a child-list index against the blocks array
*/
return this.blocks.find((block) => block.holder === firstLevelBlock);
}

/**
Expand Down Expand Up @@ -732,12 +734,25 @@ export default class BlockManager extends Module {
return;
}

/**
* Resolve the Block's index by holder identity instead of indexing into the
* working area's children: the working area may contain non-block elements
* (e.g. decoration nodes added by the host application), which would skew a
* child-list index against the blocks array — selecting the wrong Block, or
* crashing on 'updateCurrentInput' when the found index is past the array end
*/
const index = this.blocks.findIndex((block) => block.holder === parentFirstLevelBlock);

if (index === -1) {
return;
}

/**
* Update current Block's index
*
* @type {number}
*/
this.currentBlockIndex = this._blocks.nodes.indexOf(parentFirstLevelBlock as HTMLElement);
this.currentBlockIndex = index;

/**
* Update current block active input
Expand Down
39 changes: 39 additions & 0 deletions test/cypress/tests/modules/Ui.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,45 @@ describe('Ui module', function () {
});
});

it('should update current block by click on block when the redactor contains non-block elements', function () {
createEditorWithTextBlocks([
'first block',
'second block',
'third block',
])
.as('editorInstance');

/**
* Insert a non-block element between blocks — host applications do this
* for visual decorations (e.g. page-break spacers in a paginated view)
*/
cy.get('[data-cy=editorjs]')
.find('.codex-editor__redactor')
.then(($redactor) => {
const spacer = document.createElement('div');

spacer.setAttribute('data-mutation-free', 'true');
$redactor[0].insertBefore(spacer, $redactor[0].lastElementChild);
});

/**
* Click the block BELOW the non-block element: a child-list index would
* be skewed by the extra element (resolving past the end of the blocks
* array and throwing on 'updateCurrentInput')
*/
cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.eq(2)
.click();

cy.get<EditorJS>('@editorInstance')
.then(async (editor) => {
const currentBlockIndex = await editor.blocks.getCurrentBlockIndex();

expect(currentBlockIndex).to.eq(2);
});
});

it('(in readonly) should update current block by click on block', function () {
createEditorWithTextBlocks([
'first block',
Expand Down