From dfb07ec4e41850d410ff07d0cc6e093be7669f5f Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 20 Feb 2023 00:59:10 +0200 Subject: [PATCH] fix(tests): fire mutation event on tune activation (#2281) * fix(onchange): fire mutation event if there are other changes along with mutation-free nodes * update header dependency * use node 16 for windows * fix onChange firing by manual dispatchChange() call * eslint * use node 16 for ci tests * Update CHANGELOG.md --- .github/workflows/cypress.yml | 9 +++++ docs/CHANGELOG.md | 1 + package.json | 2 +- src/components/block/index.ts | 52 +++++++++++++++++++++++----- test/cypress/tests/api/tools.spec.ts | 4 +-- test/cypress/tests/onchange.spec.ts | 9 +---- yarn.lock | 14 ++++++-- 7 files changed, 69 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index ee311b35..b7607a99 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -7,6 +7,9 @@ jobs: image: cypress/browsers:node14.17.0-chrome88-ff89 options: --user 1001 steps: + - uses: actions/setup-node@v3 + with: + node-version: 16 - uses: actions/checkout@v2 - run: yarn ci:pull_paragraph - uses: cypress-io/github-action@v2 @@ -17,6 +20,9 @@ jobs: chrome: runs-on: ubuntu-latest steps: + - uses: actions/setup-node@v3 + with: + node-version: 16 - uses: actions/checkout@v2 - run: yarn ci:pull_paragraph - uses: cypress-io/github-action@v2 @@ -27,6 +33,9 @@ jobs: edge: runs-on: windows-latest steps: + - uses: actions/setup-node@v3 + with: + node-version: 16 - uses: actions/checkout@v2 - run: yarn ci:pull_paragraph - uses: cypress-io/github-action@v2 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6da828b7..2c4960d1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,7 @@ - `Refactoring` — Popover class refactored. - `Improvement` — *Toolbox* — Number of `close()` method calls optimized. +- `Improvement` — The `onChange` callback won't be triggered only if all mutations contain nodes with the `data-mutation-free` attributes. ### 2.26.5 diff --git a/package.json b/package.json index 2ed6dba1..ee527a07 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@cypress/webpack-preprocessor": "^5.6.0", "@editorjs/code": "^2.7.0", "@editorjs/delimiter": "^1.2.0", - "@editorjs/header": "^2.6.1", + "@editorjs/header": "^2.7.0", "@editorjs/simple-image": "^1.4.1", "@types/node": "^14.14.35", "@types/webpack": "^4.41.12", diff --git a/src/components/block/index.ts b/src/components/block/index.ts index 8bed7fce..f7632936 100644 --- a/src/components/block/index.ts +++ b/src/components/block/index.ts @@ -202,17 +202,53 @@ export default class Block extends EventsDispatcher { /** * Is fired when DOM mutation has been happened + * + * mutationsOrInputEvent — actual changes + * - MutationRecord[] - any DOM change + * - InputEvent — change + * - undefined — manual triggering of block.dispatchChange() */ - 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'); + private didMutated = _.debounce((mutationsOrInputEvent: MutationRecord[] | InputEvent = undefined): void => { + /** + * We won't fire a Block mutation event if mutation contain only nodes marked with 'data-mutation-free' attributes + */ + let shouldFireUpdate; + + if (mutationsOrInputEvent === undefined) { + shouldFireUpdate = true; + } else if (mutationsOrInputEvent instanceof InputEvent) { + shouldFireUpdate = true; + } else { + /** + * Update from 2023, Feb 17: + * Changed mutationsOrInputEvent.some() to mutationsOrInputEvent.every() + * since there could be a real mutations same-time with mutation-free changes, + * for example when Block Tune change: block is changing along with FakeCursor (mutation-free) removing + * — we should fire 'didMutated' event in that case + */ + const everyRecordIsMutationFree = mutationsOrInputEvent.length > 0 && mutationsOrInputEvent.every((record) => { + const { addedNodes, removedNodes } = record; + const changedNodes = [ + ...Array.from(addedNodes), + ...Array.from(removedNodes), + ]; + + return changedNodes.some((node) => { + if ($.isElement(node) === false) { + return false; + } + + return (node as HTMLElement).dataset.mutationFree === 'true'; + }); }); + if (everyRecordIsMutationFree) { + shouldFireUpdate = false; + } else { + shouldFireUpdate = true; + } + } + /** * In case some mutation free elements are added or removed, do not trigger didMutated event */ diff --git a/test/cypress/tests/api/tools.spec.ts b/test/cypress/tests/api/tools.spec.ts index ce326cbc..38073fff 100644 --- a/test/cypress/tests/api/tools.spec.ts +++ b/test/cypress/tests/api/tools.spec.ts @@ -512,7 +512,7 @@ describe('Editor Tools Api', () => { /** config specified handled tag */ public static get pasteConfig(): PasteConfig { return { - tags: ['img'], // only tag name specified. Attributes should be sanitized + tags: [ 'img' ], // only tag name specified. Attributes should be sanitized }; } @@ -564,7 +564,7 @@ describe('Editor Tools Api', () => { /** config specified handled tag */ public static get pasteConfig(): PasteConfig { return { - tags: ['img'], // only tag name specified. Attributes should be sanitized + tags: [ 'img' ], // only tag name specified. Attributes should be sanitized }; } diff --git a/test/cypress/tests/onchange.spec.ts b/test/cypress/tests/onchange.spec.ts index 77d46ab0..9b8a0262 100644 --- a/test/cypress/tests/onchange.spec.ts +++ b/test/cypress/tests/onchange.spec.ts @@ -221,14 +221,7 @@ describe('onChange callback', () => { .click(); cy.get('[data-cy=editorjs]') - .get('span.cdx-settings-button[data-level=4]') - .click() - /** - * For some reason, the first click fires the mutation of removeFakeCursor only, so we need to click again. - * Reproduced only in Cypress. - * - * @todo debug it later - */ + .get('.ce-settings .ce-popover-item:nth-child(4)') .click(); cy.get('@onChange').should('be.calledWithMatch', EditorJSApiMock, Cypress.sinon.match({ diff --git a/yarn.lock b/yarn.lock index b07a1487..ab06518e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -857,6 +857,11 @@ resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.1.0.tgz#a02885fe8699f69902d05b077b5f1cd48a2ca6b9" integrity sha512-jW1fWnwtWzcP4FBGsaodbJY3s1ZaRU+IJy1pvJ7ygNQxkQinybJcwXoyt0a5mWwu/4w30A42EWhCrZn8lp4fdw== +"@codexteam/icons@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.5.tgz#d17f39b6a0497c6439f57dd42711817a3dd3679c" + integrity sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA== + "@codexteam/shortcuts@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@codexteam/shortcuts/-/shortcuts-1.2.0.tgz#b8dd7396962b0bd845a5c8f8f19bc6119b520e19" @@ -944,9 +949,12 @@ version "1.2.0" resolved "https://registry.yarnpkg.com/@editorjs/delimiter/-/delimiter-1.2.0.tgz#5075f1a3e68765cfb6aec8694b316d81e2b41607" -"@editorjs/header@^2.6.1": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.6.2.tgz#523b6dda72ff882e53f64325840ee7bfc68ee6b7" +"@editorjs/header@^2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@editorjs/header/-/header-2.7.0.tgz#755d104a9210a8e2d9ccf22b175b2a93bdbb2330" + integrity sha512-4fGKGe2ZYblVqR/P/iw5ieG00uXInFgNMftBMqJRYcB2hUPD30kuu7Sn6eJDcLXoKUMOeqi8Z2AlUxYAmvw7zQ== + dependencies: + "@codexteam/icons" "^0.0.5" "@editorjs/simple-image@^1.4.1": version "1.4.1"