Skip to content

Commit

Permalink
Standalone Editor: Provide a DOMHelper to allow access DOM tree (#2363)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong authored Jan 25, 2024
1 parent c16ba1a commit 9c69ea0
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { toArray } from 'roosterjs-content-model-dom';
import type { DOMHelper } from 'roosterjs-content-model-types';

class DOMHelperImpl implements DOMHelper {
constructor(private contentDiv: HTMLElement) {}

queryElements(selector: string): HTMLElement[] {
return toArray(this.contentDiv.querySelectorAll(selector)) as HTMLElement[];
}
}

/**
* @internal Create new instance of DOMHelper
*/
export function createDOMHelper(contentDiv: HTMLElement): DOMHelper {
return new DOMHelperImpl(contentDiv);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
ContentModelSegmentFormat,
DarkColorHandler,
DOMEventRecord,
DOMHelper,
DOMSelection,
DomToModelOption,
EditorEnvironment,
Expand Down Expand Up @@ -158,6 +159,13 @@ export class StandaloneEditor implements IStandaloneEditor {
return this.getCore().format.pendingFormat?.format ?? null;
}

/**
* Get a DOM Helper object to help access DOM tree in editor
*/
getDOMHelper(): DOMHelper {
return this.getCore().domHelper;
}

/**
* Add a single undo snapshot to undo stack
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createDarkColorHandler } from './DarkColorHandlerImpl';
import { createDOMHelper } from './DOMHelperImpl';
import { createStandaloneEditorCorePlugins } from '../corePlugin/createStandaloneEditorCorePlugins';
import { standaloneCoreApiMap } from './standaloneCoreApiMap';
import {
Expand Down Expand Up @@ -49,6 +50,7 @@ export function createStandaloneEditorCore(
trustedHTMLHandler: options.trustedHTMLHandler || defaultTrustHtmlHandler,
domToModelSettings: createDomToModelSettings(options),
modelToDomSettings: createModelToDomSettings(options),
domHelper: createDOMHelper(contentDiv),
...getPluginState(corePlugins),
disposeErrorHandler: options.disposeErrorHandler,
zoomScale: (options.zoomScale ?? -1) > 0 ? options.zoomScale! : 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createDOMHelper } from '../../lib/editor/DOMHelperImpl';

describe('DOMHelperImpl', () => {
it('queryElements', () => {
const mockedResult = ['RESULT'] as any;
const querySelectorAllSpy = jasmine
.createSpy('querySelectorAll')
.and.returnValue(mockedResult);
const mockedDiv: HTMLElement = {
querySelectorAll: querySelectorAllSpy,
} as any;
const mockedSelector = 'SELECTOR';
const domHelper = createDOMHelper(mockedDiv);

const result = domHelper.queryElements(mockedSelector);

expect(result).toEqual(mockedResult);
expect(querySelectorAllSpy).toHaveBeenCalledWith(mockedSelector);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,31 @@ describe('StandaloneEditor', () => {
expect(() => editor.takeSnapshot()).toThrow();
});

it('getDOMHelper', () => {
const div = document.createElement('div');
const mockedDOMHelper = 'DOMHELPER' as any;
const resetSpy = jasmine.createSpy('reset');
const mockedCore = {
plugins: [],
domHelper: mockedDOMHelper,
darkColorHandler: {
updateKnownColor: updateKnownColorSpy,
reset: resetSpy,
},
} as any;

createEditorCoreSpy.and.returnValue(mockedCore);

const editor = new StandaloneEditor(div);
const domHelper = editor.getDOMHelper();

expect(domHelper).toBe(mockedDOMHelper);

editor.dispose();
expect(resetSpy).toHaveBeenCalledWith();
expect(() => editor.takeSnapshot()).toThrow();
});

it('restoreSnapshot', () => {
const div = document.createElement('div');
const mockedSnapshot = 'SNAPSHOT' as any;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as createDefaultSettings from '../../lib/editor/createStandaloneEditorDefaultSettings';
import * as createStandaloneEditorCorePlugins from '../../lib/corePlugin/createStandaloneEditorCorePlugins';
import * as DarkColorHandlerImpl from '../../lib/editor/DarkColorHandlerImpl';
import * as DOMHelperImpl from '../../lib/editor/DOMHelperImpl';
import { standaloneCoreApiMap } from '../../lib/editor/standaloneCoreApiMap';
import { StandaloneEditorCore, StandaloneEditorOptions } from 'roosterjs-content-model-types';
import {
Expand Down Expand Up @@ -37,6 +38,7 @@ describe('createEditorCore', () => {
const mockedDarkColorHandler = 'DARKCOLOR' as any;
const mockedDomToModelSettings = 'DOMTOMODEL' as any;
const mockedModelToDomSettings = 'MODELTODOM' as any;
const mockedDOMHelper = 'DOMHELPER' as any;

beforeEach(() => {
spyOn(
Expand All @@ -52,6 +54,7 @@ describe('createEditorCore', () => {
spyOn(createDefaultSettings, 'createModelToDomSettings').and.returnValue(
mockedModelToDomSettings
);
spyOn(DOMHelperImpl, 'createDOMHelper').and.returnValue(mockedDOMHelper);
});

function runTest(
Expand Down Expand Up @@ -92,6 +95,7 @@ describe('createEditorCore', () => {
entity: 'entity' as any,
selection: 'selection' as any,
undo: 'undo' as any,
domHelper: mockedDOMHelper,
disposeErrorHandler: undefined,
zoomScale: 1,
...additionalResult,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DOMHelper } from '../parameter/DOMHelper';
import type { PluginEventData, PluginEventFromType } from '../event/PluginEventData';
import type { PluginEventType } from '../event/PluginEventType';
import type { PasteType } from '../enum/PasteType';
Expand Down Expand Up @@ -90,6 +91,11 @@ export interface IStandaloneEditor {
*/
isDisposed(): boolean;

/**
* Get a DOM Helper object to help access DOM tree in editor
*/
getDOMHelper(): DOMHelper;

/**
* Get document which contains this editor
* @returns The HTML document which contains this editor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DOMHelper } from '../parameter/DOMHelper';
import type { PluginEvent } from '../event/PluginEvent';
import type { PluginState } from '../pluginState/PluginState';
import type { EditorPlugin } from './EditorPlugin';
Expand Down Expand Up @@ -340,6 +341,11 @@ export interface StandaloneEditorCore extends PluginState {
*/
readonly trustedHTMLHandler: TrustedHTMLHandler;

/**
* A helper class to provide DOM access APIs
*/
readonly domHelper: DOMHelper;

/**
* A callback to be invoked when any exception is thrown during disposing editor
* @param plugin The plugin that causes exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ export { AnnounceData, KnownAnnounceStrings } from './parameter/AnnounceData';
export { TrustedHTMLHandler } from './parameter/TrustedHTMLHandler';
export { Rect } from './parameter/Rect';
export { ValueSanitizer } from './parameter/ValueSanitizer';
export { DOMHelper } from './parameter/DOMHelper';

export { BasePluginEvent, BasePluginDomEvent } from './event/BasePluginEvent';
export { BeforeCutCopyEvent } from './event/BeforeCutCopyEvent';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* A helper class to provide DOM access APIs
*/
export interface DOMHelper {
/**
* Query HTML elements in editor by tag name.
* Be careful of this function since it will also return element under entity.
* @param tag Tag name of the element to query
* @returns HTML Element array of the query result
*/
queryElements<TTag extends keyof HTMLElementTagNameMap>(
tag: TTag
): HTMLElementTagNameMap[TTag][];

/**
* Query HTML elements in editor by a selector string
* Be careful of this function since it will also return element under entity.
* @param selector Selector string to query
* @returns HTML Element array of the query result
*/
queryElements(selector: string): HTMLElement[];
}

0 comments on commit 9c69ea0

Please sign in to comment.