Merge branch 'next' into feature/merge-block-of-different-types

This commit is contained in:
Guillaume Leon 2024-01-21 20:46:38 +01:00 committed by GitHub
commit d4afe8cca6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 145 additions and 2 deletions

View file

@ -17,6 +17,8 @@
- `Fix` — Caret losing on Mobile Devices when adding a block via Toolbox or via Backspace at the beginning of a Block
- `Improvement` — Now you can set focus via arrows/Tab to "contentless" (decorative) blocks like Delimiter which have no inputs.
- `Improvement` — Inline Toolbar sometimes opened in an incorrect position. Now it will be aligned by the left side of the selected text. And won't overflow the right side of the text column.
- `Improvement` - Now the `data-mutation-free` supports deep nesting, so you can mark some element with it to prevent the onChange call caused by child element mutating
- `Improvement` - Now the `data-mutation-free` also allows to skip "characterData" mutations (eg. text content change)
- `Refactoring``ce-block--focused` class toggling removed as unused.
### 2.28.2

View file

@ -899,10 +899,13 @@ export default class Block extends EventsDispatcher<BlockEvents> {
return changedNodes.some((node) => {
if (!$.isElement(node)) {
return false;
/**
* "characterData" mutation record has Text node as a target, so we need to get parent element to check it for mutation-free attribute
*/
node = node.parentElement;
}
return (node as HTMLElement).dataset.mutationFree === 'true';
return node && (node as HTMLElement).closest('[data-mutation-free="true"]') !== null;
});
});

View file

@ -544,6 +544,144 @@ describe('onChange callback', () => {
});
});
it('should not be fired when mutation happened in a child of element with the "data-mutation-free" mark', () => {
/**
* Mock for tool wrapper which we will mutate in a test
*/
const toolWrapper = document.createElement('div');
const toolChild = document.createElement('div');
toolWrapper.appendChild(toolChild);
/**
* Mark it as mutation-free
*/
toolWrapper.dataset.mutationFree = 'true';
/**
* Mock of tool with data-mutation-free attribute
*/
class ToolWithMutationFreeAttribute {
/**
* Simply return mocked element
*/
public render(): HTMLElement {
return toolWrapper;
}
/**
* Saving logic is not necessary for this test
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
public save(): void {}
}
const editorConfig = {
tools: {
testTool: ToolWithMutationFreeAttribute,
},
onChange: (api, event): void => {
console.log('something changed', event);
},
data: {
blocks: [
{
type: 'testTool',
data: {},
},
],
},
};
cy.spy(editorConfig, 'onChange').as('onChange');
cy.createEditor(editorConfig).as('editorInstance');
/**
* Emulate tool's internal attribute mutation
*/
cy.wait(100).then(() => {
toolChild.setAttribute('some-changed-attr', 'some-new-value');
});
/**
* Check that onChange callback was not called
*/
cy.wait(500).then(() => {
cy.get('@onChange').should('have.callCount', 0);
});
});
it('should not be fired when "characterData" mutation happened in a child of element with the "data-mutation-free" mark', () => {
/**
* Mock for tool wrapper which we will mutate in a test
*/
const toolWrapper = document.createElement('div');
const toolChild = document.createElement('div');
toolChild.setAttribute('data-cy', 'tool-child');
toolChild.setAttribute('contenteditable', 'true');
toolWrapper.appendChild(toolChild);
/**
* Mark it as mutation-free
*/
toolWrapper.dataset.mutationFree = 'true';
/**
* Mock of tool with data-mutation-free attribute
*/
class ToolWithMutationFreeAttribute {
/**
* Simply return mocked element
*/
public render(): HTMLElement {
return toolWrapper;
}
/**
* Saving logic is not necessary for this test
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
public save(): void {}
}
const editorConfig = {
tools: {
testTool: ToolWithMutationFreeAttribute,
},
onChange: function (api, event) {
console.log('something changed!!!!!!!!', event);
},
data: {
blocks: [
{
type: 'testTool',
data: {},
},
],
},
};
cy.spy(editorConfig, 'onChange').as('onChange');
cy.createEditor(editorConfig).as('editorInstance');
/**
* Emulate tool's child-element text typing
*/
cy.get('[data-cy=editorjs')
.get('[data-cy=tool-child]')
.click()
.type('some text');
/**
* Check that onChange callback was not called
*/
cy.wait(500).then(() => {
cy.get('@onChange').should('have.callCount', 0);
});
});
it('should be called on blocks.clear() with removed and added blocks', () => {
createEditor([
{