Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/collaboration-manager/src/BatchedOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export class BatchedOperation<T extends OperationType = OperationType> extends O
* Checks if operation can be added to the batch
*
* Only text operations with the same type (Insert/Delete) on the same block and data key could be added
* @todo delete operations are not being batched properly
* @param op - operation to check
*/
public canAdd(op: Operation): boolean {
Expand Down
18 changes: 13 additions & 5 deletions packages/collaboration-manager/src/CollaborationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
TextFormattedEvent, TextRemovedEvent,
TextUnformattedEvent
} from '@editorjs/model';
import type {
UndoCoreEvent,
EditorAPI,
EditorjsPlugin,
EditorjsPluginParams,
RedoCoreEvent
} from '@editorjs/sdk';
import {
CoreEventType,
type EditorAPI,
type EditorjsPlugin,
type EditorjsPluginParams,
PluginType
} from '@editorjs/sdk';
import { OTClient } from './client/index.js';
Expand Down Expand Up @@ -97,10 +101,14 @@
this.#config = config;
this.#undoRedoManager = new UndoRedoManager();

const onUndo = (): void => {
const onUndo = (e: UndoCoreEvent): void => {
e.preventDefault();

this.undo();
};
const onRedo = (): void => {
const onRedo = (e: RedoCoreEvent): void => {

Check warning on line 109 in packages/collaboration-manager/src/CollaborationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
e.preventDefault();

this.redo();
};
const onReady = (): void => {
Expand Down
13 changes: 7 additions & 6 deletions packages/collaboration-manager/test/mocks/createManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { EditorDocumentSerialized, ModelEvents } from '@editorjs/model';
import { EventType } from '@editorjs/model';
import type { EditorJSModel } from '@editorjs/model';
import { EventBus } from '@editorjs/sdk';
import type { CoreConfigValidated, DocumentAPI, EditorAPI, InsertRemoveDataParams, ModifyDataParams } from '@editorjs/sdk';
import type { CoreConfigValidated, DocumentAPI, EditorAPI, InsertRemoveDataParams, ModifyDataParams, BlocksAPI } from '@editorjs/sdk';
import { CollaborationManager } from '../../src/CollaborationManager.js';

/**
Expand All @@ -28,7 +28,7 @@ function createMockDocumentAPI(model: EditorJSModel): DocumentAPI {
modifyData({ userId, index, data }: ModifyDataParams): void {
model.modifyData(userId, index, data);
},
};
} as DocumentAPI;
}

/**
Expand All @@ -37,16 +37,17 @@ function createMockDocumentAPI(model: EditorJSModel): DocumentAPI {
* @param model - the EditorJS model instance
* @returns an object containing the manager and the eventBus used
*/
export function createManager(config: CoreConfigValidated, model: EditorJSModel): { manager: CollaborationManager;
eventBus: EventBus; } {
export function createManager(config: CoreConfigValidated, model: EditorJSModel): {
manager: CollaborationManager;
eventBus: EventBus;
} {
const eventBus = new EventBus();

const api: EditorAPI = {
document: createMockDocumentAPI(model),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
blocks: {
render: () => undefined,
} as any,
} as unknown as BlocksAPI,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
selection: {} as any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
36 changes: 34 additions & 2 deletions packages/core/src/api/DocumentAPI/DocumentAPI.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { beforeEach, describe, expect, jest } from '@jest/globals';
import type { CoreConfigValidated } from '@editorjs/sdk';
import type { CoreConfigValidated, EventBus } from '@editorjs/sdk';

jest.unstable_mockModule('@editorjs/sdk', () => ({
UndoCoreEvent: class UndoCoreEvent {
public name = 'undo';
},
RedoCoreEvent: class RedoCoreEvent {
public name = 'redo';
},
EventBus: jest.fn(),
}));

jest.unstable_mockModule('@editorjs/model', () => {
const EditorJSModel = jest.fn(() => ({
Expand All @@ -22,7 +32,13 @@ describe('DocumentAPI', () => {
// @ts-expect-error - mock object, don't need to pass any arguments
const model = new EditorJSModel();

const documentAPI = new DocumentAPI(model, {} as unknown as CoreConfigValidated);
const dispatchEvent = jest.fn();

const documentAPI = new DocumentAPI(
model,
{} as unknown as CoreConfigValidated,
{ dispatchEvent } as unknown as EventBus
);

beforeEach(() => {
jest.resetAllMocks();
Expand Down Expand Up @@ -52,4 +68,20 @@ describe('DocumentAPI', () => {
expect(data).toEqual(mockedSerializedModel);
});
});

describe('.undo()', () => {
it('should dispatch an undo core event', () => {
documentAPI.undo();

expect(dispatchEvent).toBeCalledWith(expect.objectContaining({ name: 'undo' }));
});
});

describe('.redo()', () => {
it('should dispatch an redo core event', () => {
documentAPI.redo();

expect(dispatchEvent).toBeCalledWith(expect.objectContaining({ name: 'redo' }));
});
});
});
36 changes: 33 additions & 3 deletions packages/core/src/api/DocumentAPI/DocumentAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import 'reflect-metadata';
import { type EditorDocumentSerialized, EditorJSModel, EventType, type ModelEvents } from '@editorjs/model';
import {
CoreConfigValidated,
DocumentAPI as DocumentApiInterface,
DocumentAPI as DocumentApiInterface, EventBus,
type InsertRemoveDataParams,
type ModifyDataParams
type ModifyDataParams, RedoCoreEvent, UndoCoreEvent
} from '@editorjs/sdk';
import { inject, injectable } from 'inversify';
import { TOKENS } from '../../tokens.js';
Expand All @@ -26,18 +26,26 @@ export class DocumentAPI implements DocumentApiInterface {
*/
#config: CoreConfigValidated;

/**
* Editor's event bus instance
*/
#eventBus: EventBus;

/**
* DocumentAPI constructor
* All parameters are injected through the IoC container
* @param model - Editor's Document Model instance
* @param config - Editor's config
* @param eventBus - Editor's event bus instance
*/
constructor(
model: EditorJSModel,
@inject(TOKENS.EditorConfig) config: CoreConfigValidated
@inject(TOKENS.EditorConfig) config: CoreConfigValidated,
eventBus: EventBus
) {
this.#model = model;
this.#config = config;
this.#eventBus = eventBus;
}

/**
Expand Down Expand Up @@ -91,4 +99,26 @@ export class DocumentAPI implements DocumentApiInterface {
public modifyData({ userId = this.#config.userId, index, data }: ModifyDataParams): void {
this.#model.modifyData(userId, index, data);
}

/**
* Undoes the last change in the document
*/
public undo(): void {
/**
* To enable Plugins to cancel the default undo/redo behavior,
* we have to dispatch event here instead of a direct call
*/
this.#eventBus.dispatchEvent(new UndoCoreEvent());
}

/**
* Redoes the last undone change in the document
*/
public redo(): void {
/**
* To enable Plugins to cancel the default undo/redo behavior,
* we have to dispatch event here instead of a direct call
*/
this.#eventBus.dispatchEvent(new RedoCoreEvent());
}
Comment on lines +106 to +123
}
Loading
Loading