diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bce98d8e9..eacada6dd 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 2.30.6 + +– `Fix` – Fix the display of ‘Convert To’ near blocks that do not have the ‘conversionConfig.export’ rule specified + ### 2.30.5 – `Fix` – Fix exported types diff --git a/src/components/utils/blocks.ts b/src/components/utils/blocks.ts index 471bb8647..fb564223d 100644 --- a/src/components/utils/blocks.ts +++ b/src/components/utils/blocks.ts @@ -51,6 +51,15 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools const savedData = await block.save() as SavedData; const blockData = savedData.data; + /** + * Checking that the block's tool has an «export» rule + */ + const blockTool = allBlockTools.find((tool) => tool.name === block.name); + + if (blockTool !== undefined && !isToolConvertable(blockTool, 'export')) { + return []; + } + return allBlockTools.reduce((result, tool) => { /** * Skip tools without «import» rule specified @@ -59,12 +68,19 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools return result; } + /** + * Skip tools that does not specify toolbox + */ + if (tool.toolbox === undefined) { + return result; + } + /** Filter out invalid toolbox entries */ const actualToolboxItems = tool.toolbox.filter((toolboxItem) => { /** * Skip items that don't pass 'toolbox' property or do not have an icon */ - if (isEmpty(toolboxItem) || !toolboxItem.icon) { + if (isEmpty(toolboxItem) || toolboxItem.icon === undefined) { return false; } @@ -86,10 +102,10 @@ export async function getConvertibleToolsForBlock(block: BlockAPI, allBlockTools result.push({ ...tool, toolbox: actualToolboxItems, - }); + } as BlockToolAdapter); return result; - }, []); + }, [] as BlockToolAdapter[]); } diff --git a/test/cypress/fixtures/tools/ToolWithoutConversionExport.ts b/test/cypress/fixtures/tools/ToolWithoutConversionExport.ts new file mode 100644 index 000000000..77f43f988 --- /dev/null +++ b/test/cypress/fixtures/tools/ToolWithoutConversionExport.ts @@ -0,0 +1,23 @@ +import type { ConversionConfig } from '@/types/configs/conversion-config'; +import ToolMock from './ToolMock'; + +/** + * This tool has a conversionConfig, but it doesn't have export property. + * + * That means that tool can be created from string, but can't be converted to string. + */ +export class ToolWithoutConversionExport extends ToolMock { + /** + * Rules specified how our Tool can be converted to/from other Tool. + */ + public static get conversionConfig(): ConversionConfig { + return { + import: 'text', // this tool can be created from string + + /** + * Here is no "export" property, so this tool can't be converted to string + */ + // export: (data) => data.text, + }; + } +} diff --git a/test/cypress/tests/ui/BlockTunes.cy.ts b/test/cypress/tests/ui/BlockTunes.cy.ts index b5f39c076..43d7e0e5f 100644 --- a/test/cypress/tests/ui/BlockTunes.cy.ts +++ b/test/cypress/tests/ui/BlockTunes.cy.ts @@ -1,8 +1,8 @@ import { selectionChangeDebounceTimeout } from '../../../../src/components/constants'; import Header from '@editorjs/header'; -import type { ToolboxConfig } from '../../../../types'; +import type { ConversionConfig, ToolboxConfig } from '../../../../types'; import type { MenuConfig } from '../../../../types/tools'; - +import { ToolWithoutConversionExport } from '../../fixtures/tools/ToolWithoutConversionExport'; describe('BlockTunes', function () { describe('Search', () => { @@ -185,6 +185,39 @@ describe('BlockTunes', function () { .should('not.exist'); }); + it('should not display the ConvertTo control if block has no conversionConfig.export specified', () => { + cy.createEditor({ + tools: { + testTool: ToolWithoutConversionExport, + }, + data: { + blocks: [ + { + type: 'testTool', + data: { + text: 'Some text', + }, + }, + ], + }, + }).as('editorInstance'); + + cy.get('@editorInstance') + .get('[data-cy=editorjs]') + .find('.ce-block') + .click(); + + cy.get('@editorInstance') + .get('[data-cy=editorjs]') + .find('.ce-toolbar__settings-btn') + .click(); + + cy.get('@editorInstance') + .get('[data-cy=editorjs]') + .find('.ce-popover-item[data-item-name=convert-to]') + .should('not.exist'); + }); + it('should not display tool with the same data in "Convert to" menu', () => { /** * Tool with several toolbox entries configured @@ -193,9 +226,10 @@ describe('BlockTunes', function () { /** * Tool is convertable */ - public static get conversionConfig(): { import: string } { + public static get conversionConfig(): ConversionConfig { return { import: 'text', + export: 'text', }; } diff --git a/test/cypress/tests/utils/flipper.cy.ts b/test/cypress/tests/utils/flipper.cy.ts index 09be68153..114a38e1e 100644 --- a/test/cypress/tests/utils/flipper.cy.ts +++ b/test/cypress/tests/utils/flipper.cy.ts @@ -87,9 +87,9 @@ describe('Flipper', () => { .trigger('keydown', { keyCode: ARROW_DOWN_KEY_CODE }); /** - * Check whether we focus the Move Up Tune or not + * Check whether we focus the Delete Tune or not */ - cy.get('[data-item-name="move-up"]') + cy.get('[data-item-name="delete"]') .should('have.class', 'ce-popover-item--focused'); cy.get('[data-cy=editorjs]') diff --git a/types/tools/block-tool.d.ts b/types/tools/block-tool.d.ts index ae02161b5..ddf478968 100644 --- a/types/tools/block-tool.d.ts +++ b/types/tools/block-tool.d.ts @@ -1,6 +1,6 @@ import { ConversionConfig, PasteConfig, SanitizerConfig } from '../configs'; import { BlockToolData } from './block-tool-data'; -import { BaseTool, BaseToolConstructable } from './tool'; +import { BaseTool, BaseToolConstructable, BaseToolConstructorOptions } from './tool'; import { ToolConfig } from './tool-config'; import { API, BlockAPI, ToolboxConfig } from '../index'; import { PasteEvent } from './paste-events'; @@ -83,10 +83,8 @@ export interface BlockTool extends BaseTool { /** * Describe constructor parameters */ -export interface BlockToolConstructorOptions { - api: API; +export interface BlockToolConstructorOptions extends BaseToolConstructorOptions { data: BlockToolData; - config: ToolConfig; block: BlockAPI; readOnly: boolean; } diff --git a/types/tools/tool.d.ts b/types/tools/tool.d.ts index 184000eba..17aa0f2d9 100644 --- a/types/tools/tool.d.ts +++ b/types/tools/tool.d.ts @@ -9,15 +9,27 @@ import {MenuConfig} from './menu-config'; export interface BaseTool { /** * Tool`s render method - * - * For Inline Tools may return either HTMLElement (deprecated) or {@link MenuConfig} + * + * For Inline Tools may return either HTMLElement (deprecated) or {@link MenuConfig} * @see https://editorjs.io/menu-config - * + * * For Block Tools returns tool`s wrapper html element */ render(): RenderReturnType | Promise; } +export interface BaseToolConstructorOptions { + /** + * Editor.js API + */ + api: API; + + /** + * Tool configuration + */ + config?: ToolConfig; +} + export interface BaseToolConstructable { /** * Define Tool type as Inline @@ -35,11 +47,6 @@ export interface BaseToolConstructable { */ title?: string; - /** - * Describe constructor parameters - */ - new (config: {api: API, config?: ToolConfig}): BaseTool; - /** * Tool`s prepare method. Can be async * @param data