diff --git a/.eslintrc b/.eslintrc index 6704e3b7..9d39bb96 100644 --- a/.eslintrc +++ b/.eslintrc @@ -50,6 +50,7 @@ "DOMRect": true, "ClientRect": true, "ArrayLike": true, + "InputEvent": true, "unknown": true } } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e9a4f930..a3919422 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -21,6 +21,7 @@ - `Refactoring` — Toolbox became a standalone class instead of a Module. It can be accessed only through the Toolbar module. - `Refactoring` — CI flow optimized. - `Fix` - Recognize async `onPaste` handlers in tools [#1803](https://github.com/codex-team/editor.js/issues/1803). +- `Fix` — Fire onChange event for native inputs [#1750](https://github.com/codex-team/editor.js/issues/1750) ### 2.22.3 diff --git a/package.json b/package.json index 886cc9fc..59bef858 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@codexteam/shortcuts": "^1.1.1", "@cypress/code-coverage": "^3.9.2", "@cypress/webpack-preprocessor": "^5.6.0", + "@editorjs/code": "^2.7.0", "@editorjs/header": "^2.6.1", "@editorjs/simple-image": "^1.4.1", "@types/node": "^14.14.35", diff --git a/src/components/block/index.ts b/src/components/block/index.ts index d3eabc8c..9a9f9851 100644 --- a/src/components/block/index.ts +++ b/src/components/block/index.ts @@ -52,7 +52,7 @@ interface BlockConstructorOptions { /** * Tunes data for current Block */ - tunesData: {[name: string]: BlockTuneData}; + tunesData: { [name: string]: BlockTuneData }; } /** @@ -98,7 +98,7 @@ export default class Block extends EventsDispatcher { * * @returns {{wrapper: string, content: string}} */ - public static get CSS(): {[name: string]: string} { + public static get CSS(): { [name: string]: string } { return { wrapper: 'ce-block', wrapperStretched: 'ce-block--stretched', @@ -170,7 +170,7 @@ export default class Block extends EventsDispatcher { * 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 */ - private unavailableTunesData: {[name: string]: BlockTuneData} = {}; + private unavailableTunesData: { [name: string]: BlockTuneData } = {}; /** * Editor`s API module @@ -201,11 +201,15 @@ export default class Block extends EventsDispatcher { /** * Is fired when DOM mutation has been happened */ - private didMutated = _.debounce((mutations: MutationRecord[] = []): void => { - const shouldFireUpdate = !mutations.some(({ addedNodes = [], removedNodes }) => { - return [...Array.from(addedNodes), ...Array.from(removedNodes)] - .some(node => $.isElement(node) && (node as HTMLElement).dataset.mutationFree === 'true'); - }); + private didMutated = _.debounce((mutationsOrInputEvent: MutationRecord[] | InputEvent = []): void => { + const shouldFireUpdate = mutationsOrInputEvent instanceof InputEvent || + !mutationsOrInputEvent.some(({ + 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 @@ -575,9 +579,9 @@ export default class Block extends EventsDispatcher { * * @returns {object} */ - public async save(): Promise { + public async save(): Promise { 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(), @@ -706,7 +710,7 @@ export default class Block extends EventsDispatcher { * 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. */ - public dispatchChange(): void{ + public dispatchChange(): void { this.didMutated(); } @@ -775,7 +779,7 @@ export default class Block extends EventsDispatcher { * @param tunesData - current Block tunes data * @private */ - private composeTunes(tunesData: {[name: string]: BlockTuneData}): void { + private composeTunes(tunesData: { [name: string]: BlockTuneData }): void { Array.from(this.tunes.values()).forEach((tune) => { const collection = tune.isInternal ? this.defaultTunesInstances : this.tunesInstances; diff --git a/test/cypress/tests/onchange.spec.ts b/test/cypress/tests/onchange.spec.ts index 8d410b96..3397c5a6 100644 --- a/test/cypress/tests/onchange.spec.ts +++ b/test/cypress/tests/onchange.spec.ts @@ -1,4 +1,5 @@ import Header from '@editorjs/header'; +import Code from '@editorjs/code'; import { BlockMutationType } from '../../../types/events/block/mutation-type'; /** @@ -16,6 +17,7 @@ describe('onChange callback', () => { const config = { tools: { header: Header, + code: Code, }, onChange: (api, event): void => { console.log('something changed', api, event); @@ -47,7 +49,7 @@ describe('onChange callback', () => { type: BlockMutationType.Added, detail: { target: { - name: 'paragraph' + name: 'paragraph', }, index: 0, }, @@ -67,7 +69,7 @@ describe('onChange callback', () => { type: BlockMutationType.Added, detail: { target: { - name: 'paragraph' + name: 'paragraph', }, index: 1, }, @@ -85,7 +87,7 @@ describe('onChange callback', () => { cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ type: BlockMutationType.Changed, detail: { - index: 0 + index: 0, }, })); }); @@ -185,7 +187,7 @@ describe('onChange callback', () => { cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ type: BlockMutationType.Removed, 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, + }, + })); + }); }); diff --git a/yarn.lock b/yarn.lock index 5839f153..94541399 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1423,6 +1423,11 @@ debug "^3.1.0" 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": version "2.6.1" resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.6.1.tgz#454a46e4dbb32ae3aa1db4d22b0ddf2cc36c3134"