Fire onChange event for native inputs

This commit is contained in:
Georgy Berezhnoy 2021-12-05 04:24:14 +03:00
parent ff91466b14
commit 43af5a9fa1
No known key found for this signature in database
GPG key ID: F72152EC600B4FAE
5 changed files with 49 additions and 16 deletions

View file

@ -50,6 +50,7 @@
"DOMRect": true, "DOMRect": true,
"ClientRect": true, "ClientRect": true,
"ArrayLike": true, "ArrayLike": true,
"InputEvent": true,
"unknown": true "unknown": true
} }
} }

View file

@ -50,6 +50,7 @@
"@codexteam/shortcuts": "^1.1.1", "@codexteam/shortcuts": "^1.1.1",
"@cypress/code-coverage": "^3.9.2", "@cypress/code-coverage": "^3.9.2",
"@cypress/webpack-preprocessor": "^5.6.0", "@cypress/webpack-preprocessor": "^5.6.0",
"@editorjs/code": "^2.7.0",
"@editorjs/header": "^2.6.1", "@editorjs/header": "^2.6.1",
"@editorjs/simple-image": "^1.4.1", "@editorjs/simple-image": "^1.4.1",
"@types/node": "^14.14.35", "@types/node": "^14.14.35",

View file

@ -52,7 +52,7 @@ interface BlockConstructorOptions {
/** /**
* Tunes data for current Block * Tunes data for current Block
*/ */
tunesData: {[name: string]: BlockTuneData}; tunesData: { [name: string]: BlockTuneData };
} }
/** /**
@ -98,7 +98,7 @@ export default class Block extends EventsDispatcher<BlockEvents> {
* *
* @returns {{wrapper: string, content: string}} * @returns {{wrapper: string, content: string}}
*/ */
public static get CSS(): {[name: string]: string} { public static get CSS(): { [name: string]: string } {
return { return {
wrapper: 'ce-block', wrapper: 'ce-block',
wrapperStretched: 'ce-block--stretched', wrapperStretched: 'ce-block--stretched',
@ -170,7 +170,7 @@ export default class Block extends EventsDispatcher<BlockEvents> {
* If there is saved data for Tune which is not available at the moment, * If there is saved data for Tune which is not available at the moment,
* we will store it here and provide back on save so data is not lost * we will store it here and provide back on save so data is not lost
*/ */
private unavailableTunesData: {[name: string]: BlockTuneData} = {}; private unavailableTunesData: { [name: string]: BlockTuneData } = {};
/** /**
* Editor`s API module * Editor`s API module
@ -201,11 +201,15 @@ export default class Block extends EventsDispatcher<BlockEvents> {
/** /**
* Is fired when DOM mutation has been happened * Is fired when DOM mutation has been happened
*/ */
private didMutated = _.debounce((mutations: MutationRecord[] = []): void => { private didMutated = _.debounce((mutationsOrInputEvent: MutationRecord[] | InputEvent = []): void => {
const shouldFireUpdate = !mutations.some(({ addedNodes = [], removedNodes }) => { const shouldFireUpdate = mutationsOrInputEvent instanceof InputEvent ||
return [...Array.from(addedNodes), ...Array.from(removedNodes)] !mutationsOrInputEvent.some(({
.some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true'); addedNodes = [],
}); removedNodes,
}) => {
return [...Array.from(addedNodes), ...Array.from(removedNodes)]
.some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true');
});
/** /**
* In case some mutation free elements are added or removed, do not trigger didMutated event * In case some mutation free elements are added or removed, do not trigger didMutated event
@ -575,9 +579,9 @@ export default class Block extends EventsDispatcher<BlockEvents> {
* *
* @returns {object} * @returns {object}
*/ */
public async save(): Promise<void|SavedData> { public async save(): Promise<void | SavedData> {
const extractedBlock = await this.toolInstance.save(this.pluginsContent as HTMLElement); const extractedBlock = await this.toolInstance.save(this.pluginsContent as HTMLElement);
const tunesData: {[name: string]: BlockTuneData} = this.unavailableTunesData; const tunesData: { [name: string]: BlockTuneData } = this.unavailableTunesData;
[ [
...this.tunesInstances.entries(), ...this.tunesInstances.entries(),
@ -706,7 +710,7 @@ export default class Block extends EventsDispatcher<BlockEvents> {
* Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback
* Can be useful for block changes invisible for editor core. * Can be useful for block changes invisible for editor core.
*/ */
public dispatchChange(): void{ public dispatchChange(): void {
this.didMutated(); this.didMutated();
} }
@ -775,7 +779,7 @@ export default class Block extends EventsDispatcher<BlockEvents> {
* @param tunesData - current Block tunes data * @param tunesData - current Block tunes data
* @private * @private
*/ */
private composeTunes(tunesData: {[name: string]: BlockTuneData}): void { private composeTunes(tunesData: { [name: string]: BlockTuneData }): void {
Array.from(this.tunes.values()).forEach((tune) => { Array.from(this.tunes.values()).forEach((tune) => {
const collection = tune.isInternal ? this.defaultTunesInstances : this.tunesInstances; const collection = tune.isInternal ? this.defaultTunesInstances : this.tunesInstances;

View file

@ -1,4 +1,5 @@
import Header from '@editorjs/header'; import Header from '@editorjs/header';
import Code from '@editorjs/code';
import { BlockMutationType } from '../../../types/events/block/mutation-type'; import { BlockMutationType } from '../../../types/events/block/mutation-type';
/** /**
@ -16,6 +17,7 @@ describe('onChange callback', () => {
const config = { const config = {
tools: { tools: {
header: Header, header: Header,
code: Code,
}, },
onChange: (api, event): void => { onChange: (api, event): void => {
console.log('something changed', api, event); console.log('something changed', api, event);
@ -47,7 +49,7 @@ describe('onChange callback', () => {
type: BlockMutationType.Added, type: BlockMutationType.Added,
detail: { detail: {
target: { target: {
name: 'paragraph' name: 'paragraph',
}, },
index: 0, index: 0,
}, },
@ -67,7 +69,7 @@ describe('onChange callback', () => {
type: BlockMutationType.Added, type: BlockMutationType.Added,
detail: { detail: {
target: { target: {
name: 'paragraph' name: 'paragraph',
}, },
index: 1, index: 1,
}, },
@ -85,7 +87,7 @@ describe('onChange callback', () => {
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
type: BlockMutationType.Changed, type: BlockMutationType.Changed,
detail: { detail: {
index: 0 index: 0,
}, },
})); }));
}); });
@ -185,7 +187,7 @@ describe('onChange callback', () => {
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
type: BlockMutationType.Removed, type: BlockMutationType.Removed,
detail: { detail: {
index: 0 index: 0,
}, },
})); }));
}); });
@ -219,4 +221,24 @@ describe('onChange callback', () => {
}, },
})); }));
}); });
it('should fire onChange if something changed inside native input', () => {
createEditor([ {
type: 'code',
data: {
code: '',
},
} ]);
cy.get('[data-cy=editorjs')
.get('textarea')
.type('Some input to the textarea');
cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({
type: BlockMutationType.Changed,
detail: {
index: 0,
},
}));
});
}); });

View file

@ -1423,6 +1423,11 @@
debug "^3.1.0" debug "^3.1.0"
lodash.once "^4.1.1" lodash.once "^4.1.1"
"@editorjs/code@^2.7.0":
version "2.7.0"
resolved "https://registry.yarnpkg.com/@editorjs/code/-/code-2.7.0.tgz#0a21de9ac15e4533605ffcc80969513ab2142ac5"
integrity sha512-gXtTce915fHp3H9i4IqhTxEDbbkT2heFfYiW/bhFHsCmZDpyGzfZxi94kmrEqDmbxXjV49ZZ6GZbR26If13KJw==
"@editorjs/header@^2.6.1": "@editorjs/header@^2.6.1":
version "2.6.1" version "2.6.1"
resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.6.1.tgz#454a46e4dbb32ae3aa1db4d22b0ddf2cc36c3134" resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.6.1.tgz#454a46e4dbb32ae3aa1db4d22b0ddf2cc36c3134"