Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0d5817e
Fix inputs and values attachments to be data-first
gohabereg Apr 17, 2026
c5ff274
Merge branch 'main' of github.com:editor-js/document-model into fix/i…
gohabereg Apr 17, 2026
e4c246d
Load adapters as plugin
gohabereg Apr 26, 2026
40a6409
Merge branch 'main' of github.com:editor-js/document-model into fix/i…
gohabereg Apr 26, 2026
abe46db
Fix tests
gohabereg Apr 27, 2026
40c4284
Review comments
gohabereg Apr 27, 2026
ba5077d
(try to) fix test run for core package
gohabereg Apr 27, 2026
e424775
Run base branch coverage manually
gohabereg Apr 27, 2026
5990de3
Fix unit-tests action: use ArtiomTr with pre-generated coverage files
gohabereg Apr 27, 2026
208c95d
Try just PR branch tests
gohabereg Apr 28, 2026
0996eac
Try clear before build
gohabereg Apr 28, 2026
8958a51
Try no cache
gohabereg Apr 28, 2026
0ea33bc
Use latest node
gohabereg Apr 28, 2026
d265bd3
Review comments resolved
gohabereg Apr 29, 2026
7fe438f
Resolve review comments
gohabereg May 2, 2026
0ad4ae1
Add destoryBlockToolAdapter method to Adapter plugin
gohabereg May 3, 2026
9eb123a
Block ID implementation
gohabereg May 3, 2026
bb7fc7c
Fix lint
gohabereg May 3, 2026
f6702e4
Fix tests
gohabereg May 3, 2026
b4cbdfa
Fix build
gohabereg May 3, 2026
a5fa55f
Remove userId from getBlockSerialized method
gohabereg May 3, 2026
8235392
Fix call in the block manager
gohabereg May 3, 2026
d696d9f
Review comments
gohabereg May 5, 2026
e727e7e
Merge branch 'main' of github.com:editor-js/document-model into featu…
gohabereg May 7, 2026
ab47b6d
Cover uncovered lines
gohabereg May 7, 2026
0c4e32b
Merge branch 'main' into feature/block-id
gohabereg May 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 45 additions & 45 deletions packages/collaboration-manager/src/CollaborationManager.spec.ts

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions packages/core/src/api/BlocksAPI.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,11 @@ describe('BlocksAPI integration (real model, mocked DOM adapters)', () => {
blocksAPI.render({
identifier: 'new-doc',
blocks: [
{ name: 'header',
data: {} },
{
id: 'mock',
name: 'header',
data: {},
},
],
properties: {},
});
Expand Down Expand Up @@ -375,10 +378,16 @@ describe('BlocksAPI integration (real model, mocked DOM adapters)', () => {
blocksAPI.render({
identifier: 'doc-2',
blocks: [
{ name: 'header',
data: {} },
{ name: 'list',
data: {} },
{
id: 'mock-header',
name: 'header',
data: {},
},
{
id: 'mock-list',
name: 'list',
data: {},
},
],
properties: {},
});
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/api/BlocksAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BlocksManager } from '../components/BlockManager.js';
import { BlockToolData } from '@editorjs/editorjs';
import { CoreConfigValidated } from '@editorjs/sdk';
import { BlocksAPI as BlocksApiInterface } from '@editorjs/sdk';
import { type BlockNodeSerialized, EditorDocumentSerialized } from '@editorjs/model';
import { type BlockNodeInit, type EditorDocumentSerialized } from '@editorjs/model';

/**
* Blocks API
Expand Down Expand Up @@ -80,7 +80,7 @@ export class BlocksAPI implements BlocksApiInterface {
* @param blocks - array of blocks to insert
* @param [index] - index to insert blocks at. If undefined, inserts at the end
*/
public insertMany(blocks: BlockNodeSerialized[], index?: number): void {
public insertMany(blocks: BlockNodeInit[], index?: number): void {
return this.#blocksManager.insertMany(blocks, index);
}

Expand Down
41 changes: 16 additions & 25 deletions packages/core/src/components/BlockManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jest.unstable_mockModule('@editorjs/model', () => {
initializeDocument: jest.fn(),
clearBlocks: jest.fn(),
getCaret: jest.fn(),
getBlockSerialized: jest.fn(),
get length() {
return BLOCKS_COUNT;
},
Expand Down Expand Up @@ -222,6 +223,7 @@ describe('BlocksManager (unit, mocked deps)', () => {
identifier: 'doc',
blocks: [
{
id: 'mock',
name: 'x',
data: {}
}
Expand Down Expand Up @@ -255,20 +257,24 @@ describe('BlocksManager (unit, mocked deps)', () => {

expect(model.removeBlock).toHaveBeenCalledWith(USER_ID, 0);
});

it('should call model.getCaret with the configured userId to resolve current block', () => {
Comment thread
neSpecc marked this conversation as resolved.
// @ts-expect-error - mock return value does not need full Caret shape
model.getCaret = jest.fn(() => ({ index: { blockIndex: 2 } }));

blocksManager.deleteBlock();

expect(model.getCaret).toHaveBeenCalledWith(USER_ID);
expect(model.removeBlock).toHaveBeenCalledWith(USER_ID, 2);
});
});

describe('.move()', () => {
it('should call removeBlock and addBlock when moving current block forward', () => {
// @ts-expect-error - need to assign read only property to mock it
model.serialized = {
blocks: [
{ name: 'a' },
{ name: 'b' },
{ name: 'c' }
]
};
// @ts-expect-error - mock return value does not need full Caret shape
model.getCaret = jest.fn(() => ({ index: { blockIndex: 0 } }));
// @ts-expect-error - mock return value does not need full BlockNodeSerialized shape
model.getBlockSerialized = jest.fn(() => ({ name: 'a' }));

blocksManager.move(2);

Expand All @@ -283,14 +289,8 @@ describe('BlocksManager (unit, mocked deps)', () => {
});

it('should pass toIndex directly when toIndex is less than fromIndex', () => {
// @ts-expect-error - need to assign read only property to mock it
model.serialized = {
blocks: [
{ name: 'a' },
{ name: 'b' },
{ name: 'c' }
]
};
// @ts-expect-error - mock return value does not need full BlockNodeSerialized shape
model.getBlockSerialized = jest.fn(() => ({ name: 'c' }));

blocksManager.move(0, 2);

Expand All @@ -299,15 +299,6 @@ describe('BlocksManager (unit, mocked deps)', () => {
});

it('should do nothing when toIndex equals fromIndex', () => {
// @ts-expect-error - need to assign read only property to mock it
model.serialized = {
blocks: [
{ name: 'a' },
{ name: 'b' },
{ name: 'c' }
]
};

blocksManager.move(1, 1);

expect(model.removeBlock).not.toHaveBeenCalled();
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/components/BlockManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
type BlockNodeSerialized,
type BlockNodeInit,
type EditorDocumentSerialized,
EditorJSModel
} from '@editorjs/model';
Expand All @@ -17,7 +17,10 @@ import {
* Parameters for the BlocksManager.insert() method
*/
interface InsertBlockParameters {
// id?: string;
/**
* Block ID
*/
id?: string;
/**
* Block tool name to insert
*/
Expand Down Expand Up @@ -111,7 +114,7 @@ export class BlocksManager {
* @param parameters.replace - flag indicates if block at index should be replaced
*/
public insert({
// id = undefined,
id = undefined,
type = this.#config.defaultBlock,
data = {},
index,
Expand All @@ -131,6 +134,7 @@ export class BlocksManager {

this.#model.addBlock(this.#config.userId, {
...data,
id,
name: type,
}, newIndex);

Expand All @@ -146,7 +150,7 @@ export class BlocksManager {
* @param blocks - array of blocks to insert
* @param [index] - index to insert blocks at. If undefined, inserts at the end
*/
public insertMany(blocks: BlockNodeSerialized[], index: number = this.#model.length): void {
public insertMany(blocks: BlockNodeInit[], index: number = this.#model.length): void {
blocks.forEach((block, i) => this.#model.addBlock(this.#config.userId, block, index + i));
}

Expand Down Expand Up @@ -197,7 +201,7 @@ export class BlocksManager {
return;
}

const block = this.#model.serialized.blocks[fromIndex];
const block = this.#model.getBlockSerialized(fromIndex);

this.#model.removeBlock(this.#config.userId, fromIndex);
this.#model.addBlock(this.#config.userId, block, toIndex);
Expand Down
59 changes: 41 additions & 18 deletions packages/core/src/components/BlockRenderer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable jsdoc/require-jsdoc, @stylistic/comma-dangle,@typescript-eslint/naming-convention */
import { beforeEach, jest } from '@jest/globals';
import type { BlockToolFacade, EditorJSAdapterPlugin } from '@editorjs/sdk';
import type { Index } from '@editorjs/model';
import type { BlockId, Index } from '@editorjs/model';

const USER_ID = 'user';

Expand Down Expand Up @@ -45,6 +45,7 @@ jest.unstable_mockModule('@editorjs/model', () => {
BlockAddedEvent,
BlockRemovedEvent,
EventType,
createBlockId: (str: string) => str,
};
});

Expand Down Expand Up @@ -114,8 +115,11 @@ describe('BlockRenderer (unit, mocked deps)', () => {

const event = new BlockAddedEvent(
{ blockIndex: 0 } as Index,
{ name: 'tool',
data: {} },
{
name: 'tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand All @@ -132,8 +136,11 @@ describe('BlockRenderer (unit, mocked deps)', () => {
it('should throw when blockIndex is undefined', async () => {
const event = new BlockAddedEvent(
{} as Index,
{ name: 'tool',
data: {} },
{
name: 'tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand All @@ -149,8 +156,11 @@ describe('BlockRenderer (unit, mocked deps)', () => {

const event = new BlockAddedEvent(
{ blockIndex: 0 } as Index,
{ name: 'missing-tool',
data: {} },
{
name: 'missing-tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand All @@ -174,8 +184,11 @@ describe('BlockRenderer (unit, mocked deps)', () => {

const event = new BlockAddedEvent(
{ blockIndex: 0 } as Index,
{ name: 'tool',
data: {} },
{
name: 'tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand All @@ -194,8 +207,11 @@ describe('BlockRenderer (unit, mocked deps)', () => {
it('should dispatch BlockRemovedCoreEvent via EventBus', () => {
const event = new BlockRemovedEvent(
{ blockIndex: 1 } as Index,
{ name: 'tool',
data: {} },
{
name: 'tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand All @@ -204,24 +220,31 @@ describe('BlockRenderer (unit, mocked deps)', () => {
expect(eventBus.dispatchEvent).toHaveBeenCalled();
});

it('should call destroyBlockToolAdapter with the block index', () => {
it('should call destroyBlockToolAdapter with the block id', () => {
const blockId = 'test-block-id' as unknown as BlockId;
const event = new BlockRemovedEvent(
{ blockIndex: 2 } as Index,
{ name: 'tool',
data: {} },
{ blockIndex: 1 } as Index,
{
name: 'tool',
id: blockId,
data: {}
},
USER_ID,
);

void changedListener(event);

expect(adapter.destroyBlockToolAdapter).toHaveBeenCalledWith(2);
expect(adapter.destroyBlockToolAdapter).toHaveBeenCalledWith(blockId);
});

it('should throw when blockIndex is undefined', () => {
const event = new BlockRemovedEvent(
{} as Index,
{ name: 'tool',
data: {} },
{
name: 'tool',
id: 'test-block-id' as BlockId,
data: {}
},
USER_ID,
);

Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/components/BlockRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
BlockAddedEvent,
BlockRemovedEvent,
createBlockId,
EditorJSModel,
EventType,
ModelEvents
Expand Down Expand Up @@ -110,7 +111,7 @@ export class BlockRenderer {
throw new Error(`[BlockRenderer] Block Tool ${data.name} not found`);
}

const blockToolAdapter = this.#adapter.createBlockToolAdapter(index.blockIndex, tool.name);
const blockToolAdapter = this.#adapter.createBlockToolAdapter(createBlockId(data.id), tool.name);

const block = tool.create({
adapter: blockToolAdapter,
Expand Down Expand Up @@ -146,7 +147,7 @@ export class BlockRenderer {
throw new Error('[BlockRenderer] Block index should be defined. Probably something wrong with the Editor Model. Please, report this issue');
}

this.#adapter.destroyBlockToolAdapter(index.blockIndex);
this.#adapter.destroyBlockToolAdapter(createBlockId(data.id));

this.#eventBus.dispatchEvent(new BlockRemovedCoreEvent({
tool: data.name,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/SelectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { inject, injectable } from 'inversify';
import { TOKENS } from '../tokens.js';
import { InlineToolFormatData } from '@editorjs/sdk';
import ToolsManager from '../tools/ToolsManager';
import ToolsManager from '../tools/ToolsManager.js';

/**
* SelectionManager responsible for handling selection changes and applying inline tools formatting
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/composeDataFromVersion2.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { OutputData } from '@editorjs/editorjs';
import type { InlineFragment } from '@editorjs/model';
import { createInlineToolData, createInlineToolName, TextNode, ValueNode, type BlockNodeSerialized } from '@editorjs/model';
import { createInlineToolData, createInlineToolName, TextNode, ValueNode, type BlockNodeInit } from '@editorjs/model';

/**
* Removes HTML tags from the input string
Expand Down Expand Up @@ -92,7 +92,7 @@ export function composeDataFromVersion2(data: OutputData): {
/**
* The child BlockNodes of the EditorDocument
*/
blocks: BlockNodeSerialized[];
blocks: BlockNodeInit[];
} {
return {
blocks: data.blocks.map((block) => {
Expand Down
Loading
Loading