deps(TypeScript) - upgrade to v5, upgrade ts-loader, fix types error, fix pasteConfig getter wrapper (#2322)

* deps: upgrade typescript to v5, upgrade ts-loader to support newest TS

* Fix (??) type of `pasteConfig`

TypeScript 4.9 found something is wrong with this code, but it's unclear (to me) which line is wrong. This PR is a guess, do with it what you will.

In paste.ts there's a check to see if `pasteConfig === false`:
https://github.com/codex-team/editor.js/blob/next/src/components/modules/paste.ts#L287

However, this getter never returns false because if the LHS of the `||` is `false`, `{ }` is returned instead.

It seems like this meant to be `??` instead so that if `this.constructable[InternalBlockToolSettings.PasteConfig]` was `undefined` (missing), then `{}` would be returned instead. But maybe you meant `false` here - I don't know.

* feat: create alias for PasteConfig, fix lint

* fix: problems with types

* test: add case for disabling preventing default behavior of paste event handler, add cases for pasteConfig getter in BlockTool wrapper

* chore: upgrade CHANGELOG.md

* fix: interface naming convention

* chore: apply CHANGELOG.md suggestion

* refactor: create custom Editor instance inside test case

* fix: remove editor instance destroy after PR feedback

---------

Co-authored-by: Ryan Cavanaugh <RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
Ilya Maroz 2023-04-02 16:52:42 +01:00 committed by GitHub
parent c18011595a
commit 75379c66a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 24 deletions

View file

@ -7,8 +7,10 @@
- `Improvement` — The `onChange` callback won't be triggered only if all mutations contain nodes with the `data-mutation-free` attributes. - `Improvement` — The `onChange` callback won't be triggered only if all mutations contain nodes with the `data-mutation-free` attributes.
- `Fix` — Resolve compiler error from importing the BlockToolData type. - `Fix` — Resolve compiler error from importing the BlockToolData type.
- `Fix` — Resolved a problem when document was being scrolled to the beginning after moving up a Block above the viewport. - `Fix` — Resolved a problem when document was being scrolled to the beginning after moving up a Block above the viewport.
- `Improvement` — Package size reduced by removing redundant files - `Improvement` — Package size reduced by removing redundant files.
- `Fix`- Several bugs caused by random browser extensions - `Fix`- Several bugs caused by random browser extensions.
- `Improvement`*Dependencies* — Upgrade TypeScript to v5.
- `Fix`*ToolsAPI*`pasteConfig` getter with `false` value could be used to disable paste handling by Editor.js core. Could be useful if your tool has its own paste handler.
### 2.26.5 ### 2.26.5

View file

@ -83,9 +83,9 @@
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"stylelint": "^13.3.3", "stylelint": "^13.3.3",
"terser-webpack-plugin": "^2.3.6", "terser-webpack-plugin": "^2.3.6",
"ts-loader": "^7.0.1", "ts-loader": "^8.4.0",
"tslint": "^6.1.1", "tslint": "^6.1.1",
"typescript": "3.8.3", "typescript": "^5.0.2",
"webpack": "^4.43.0", "webpack": "^4.43.0",
"webpack-cli": "^3.3.11" "webpack-cli": "^3.3.11"
}, },

View file

@ -346,6 +346,10 @@ export default class Paste extends Module {
* @param tool - BlockTool object * @param tool - BlockTool object
*/ */
private getTagsConfig(tool: BlockTool): void { private getTagsConfig(tool: BlockTool): void {
if (tool.pasteConfig === false) {
return;
}
const tagsOrSanitizeConfigs = tool.pasteConfig.tags || []; const tagsOrSanitizeConfigs = tool.pasteConfig.tags || [];
const toolTags = []; const toolTags = [];
@ -387,6 +391,10 @@ export default class Paste extends Module {
* @param tool - BlockTool object * @param tool - BlockTool object
*/ */
private getFilesConfig(tool: BlockTool): void { private getFilesConfig(tool: BlockTool): void {
if (tool.pasteConfig === false) {
return;
}
const { files = {} } = tool.pasteConfig; const { files = {} } = tool.pasteConfig;
let { extensions, mimeTypes } = files; let { extensions, mimeTypes } = files;
@ -428,7 +436,11 @@ export default class Paste extends Module {
* @param tool - BlockTool object * @param tool - BlockTool object
*/ */
private getPatternsConfig(tool: BlockTool): void { private getPatternsConfig(tool: BlockTool): void {
if (!tool.pasteConfig.patterns || _.isEmpty(tool.pasteConfig.patterns)) { if (
tool.pasteConfig === false ||
!tool.pasteConfig.patterns ||
_.isEmpty(tool.pasteConfig.patterns)
) {
return; return;
} }
@ -602,7 +614,10 @@ export default class Paste extends Module {
break; break;
} }
const { tags: tagsOrSanitizeConfigs } = tool.pasteConfig; /**
* Returns empty array if there is no paste config
*/
const { tags: tagsOrSanitizeConfigs } = tool.pasteConfig || { tags: [] };
/** /**
* Reduce the tags or sanitize configs to a single array of sanitize config. * Reduce the tags or sanitize configs to a single array of sanitize config.

View file

@ -158,7 +158,7 @@ export default class BlockTool extends BaseTool<IBlockTool> {
* Returns Tool paste configuration * Returns Tool paste configuration
*/ */
public get pasteConfig(): PasteConfig { public get pasteConfig(): PasteConfig {
return this.constructable[InternalBlockToolSettings.PasteConfig] || {}; return this.constructable[InternalBlockToolSettings.PasteConfig] ?? {};
} }
/** /**

View file

@ -1,6 +1,8 @@
import Header from '@editorjs/header'; import Header from '@editorjs/header';
import Image from '@editorjs/simple-image'; import Image from '@editorjs/simple-image';
import * as _ from '../../../src/components/utils'; import * as _ from '../../../src/components/utils';
import EditorJS, { BlockTool, BlockToolData } from '../../../types';
import $ from '../../../src/components/dom';
describe('Copy pasting from Editor', function () { describe('Copy pasting from Editor', function () {
beforeEach(function () { beforeEach(function () {
@ -13,7 +15,7 @@ describe('Copy pasting from Editor', function () {
}); });
afterEach(function () { afterEach(function () {
if (this.editorInstance) { if (this.editorInstance && this.editorInstance.destroy) {
this.editorInstance.destroy(); this.editorInstance.destroy();
} }
}); });
@ -139,6 +141,72 @@ describe('Copy pasting from Editor', function () {
.get('img', { timeout: 10000 }) .get('img', { timeout: 10000 })
.should('have.attr', 'src', 'https://codex.so/public/app/img/external/codex2x.png'); .should('have.attr', 'src', 'https://codex.so/public/app/img/external/codex2x.png');
}); });
it('should not prevent default behaviour if block\'s paste config equals false', function () {
/**
* Destroy default Editor to render custom one with different tools
*/
cy.get('@editorInstance')
.then((editorInstance: unknown) => (editorInstance as EditorJS).destroy());
const onPasteStub = cy.stub().as('onPaste');
/**
* Tool with disabled preventing default behavior of onPaste event
*/
class BlockToolWithPasteHandler implements BlockTool {
public static pasteConfig = false;
/**
* Render block
*/
public render(): HTMLElement {
const block = $.make('div', 'ce-block-with-disabled-prevent-default', {
contentEditable: 'true',
});
block.addEventListener('paste', onPasteStub);
return block;
}
/**
* Save data method
*/
public save(): BlockToolData {
return {};
}
}
cy.createEditor({
tools: {
blockToolWithPasteHandler: BlockToolWithPasteHandler,
},
}).as('editorInstanceWithBlockToolWithPasteHandler');
cy.get('@editorInstanceWithBlockToolWithPasteHandler')
.render({
blocks: [
{
type: 'blockToolWithPasteHandler',
data: {},
},
],
});
cy.get('@editorInstanceWithBlockToolWithPasteHandler')
.get('div.ce-block-with-disabled-prevent-default')
.click()
.paste({
// eslint-disable-next-line @typescript-eslint/naming-convention
'text/plain': 'Hello',
});
cy.get('@onPaste')
.should('have.been.calledWithMatch', {
defaultPrevented: false,
});
});
}); });
context('copying', function () { context('copying', function () {

View file

@ -253,10 +253,36 @@ describe('BlockTool', () => {
expect(tool.conversionConfig).to.be.deep.eq(options.constructable.conversionConfig); expect(tool.conversionConfig).to.be.deep.eq(options.constructable.conversionConfig);
}); });
it('.pasteConfig should return correct value', () => { describe('.pasteConfig', () => {
const tool = new BlockTool(options as any); it('should return correct value', () => {
const tool = new BlockTool(options as any);
expect(tool.pasteConfig).to.be.deep.eq(options.constructable.pasteConfig); expect(tool.pasteConfig).to.be.deep.eq(options.constructable.pasteConfig);
});
it('should return false if `false` value was provided', () => {
const optionsWithDisabledPaste = {
...options,
constructable: class extends (options.constructable as any) {
public static pasteConfig = false;
},
};
const tool = new BlockTool(optionsWithDisabledPaste as any);
expect(tool.pasteConfig).to.be.deep.eq(optionsWithDisabledPaste.constructable.pasteConfig);
});
it('should return empty object if getter isn\'t provided', () => {
const optionsWithoutPasteConfig = {
...options,
constructable: class extends (options.constructable as any) {
public static pasteConfig = undefined;
},
};
const tool = new BlockTool(optionsWithoutPasteConfig as any);
expect(tool.pasteConfig).to.be.deep.eq({});
});
}); });
context('.enabledInlineTools', () => { context('.enabledInlineTools', () => {

View file

@ -1,9 +1,9 @@
import { SanitizerConfig } from "./sanitizer-config"; import { SanitizerConfig } from './sanitizer-config';
/** /**
* Tool onPaste configuration object * Tool onPaste configuration object
*/ */
export interface PasteConfig { interface PasteConfigSpecified {
/** /**
* Array of tags Tool can substitute. * Array of tags Tool can substitute.
* *
@ -22,7 +22,7 @@ export interface PasteConfig {
* Object of string patterns Tool can substitute. * Object of string patterns Tool can substitute.
* Key is your internal key and value is RegExp * Key is your internal key and value is RegExp
* *
* @type {{[key: string]: Regexp}} * @type {{[key: string]: RegExp}}
*/ */
patterns?: {[key: string]: RegExp}; patterns?: {[key: string]: RegExp};
@ -31,3 +31,8 @@ export interface PasteConfig {
*/ */
files?: {extensions?: string[], mimeTypes?: string[]}; files?: {extensions?: string[], mimeTypes?: string[]};
} }
/**
* Alias for PasteConfig with false
*/
export type PasteConfig = PasteConfigSpecified | false;

View file

@ -4799,7 +4799,7 @@ loader-runner@^2.4.0:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
version "1.4.2" version "1.4.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
dependencies: dependencies:
@ -7492,15 +7492,16 @@ trough@^1.0.0:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
ts-loader@^7.0.1: ts-loader@^8.4.0:
version "7.0.5" version "8.4.0"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.4.0.tgz#e845ea0f38d140bdc3d7d60293ca18d12ff2720f"
integrity sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==
dependencies: dependencies:
chalk "^2.3.0" chalk "^4.1.0"
enhanced-resolve "^4.0.0" enhanced-resolve "^4.0.0"
loader-utils "^1.0.2" loader-utils "^2.0.0"
micromatch "^4.0.0" micromatch "^4.0.0"
semver "^6.0.0" semver "^7.3.4"
tsconfig-paths@^3.14.1: tsconfig-paths@^3.14.1:
version "3.14.1" version "3.14.1"
@ -7595,9 +7596,10 @@ typedarray@^0.0.6:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
typescript@3.8.3: typescript@^5.0.2:
version "3.8.3" version "5.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5"
integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==
unbox-primitive@^1.0.2: unbox-primitive@^1.0.2:
version "1.0.2" version "1.0.2"