Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export function mergePasteContent(
containsBlockElements,
} = eventResult;

const shouldScrollCaretIntoView = !isImageOnlyFragment(fragment);

editor.formatContentModel(
(model, context) => {
if (!isFirstPaste && clipboardData.modelBeforePaste) {
Expand Down Expand Up @@ -80,7 +82,7 @@ export function mergePasteContent(
{
changeSource: ChangeSource.Paste,
getChangeData: () => clipboardData,
scrollCaretIntoView: true,
scrollCaretIntoView: shouldScrollCaretIntoView,
apiName: 'paste',
}
);
Expand Down Expand Up @@ -137,3 +139,12 @@ function getLastSegmentFormat(pasteModel: ContentModelDocument): ContentModelSeg

return {};
}

function isImageOnlyFragment(pasteFragment: DocumentFragment): boolean {
const images = pasteFragment.querySelectorAll('img');
return (
images.length === 1 &&
pasteFragment.childNodes.length === 1 &&
pasteFragment.textContent?.trim() === ''
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,18 @@ describe('mergePasteContent', () => {
let editor: IEditor;
let mockedClipboard: ClipboardData;
let mockedDOMHelper: DOMHelper;
let formatOptions: FormatContentModelOptions | undefined;

beforeEach(() => {
formatResult = undefined;
context = undefined;
mockedClipboard = 'CLIPBOARD' as any;
formatOptions = undefined;
mockedClipboard = {
fragment: {
childNodes: [],
length: 0,
},
} as any;

formatContentModel = jasmine
.createSpy('formatContentModel')
Expand All @@ -49,6 +56,7 @@ describe('mergePasteContent', () => {
deletedEntities: [],
newImages: [],
};
formatOptions = options;
formatResult = callback(sourceModel, context);

const changedData = options.getChangeData!();
Expand Down Expand Up @@ -175,6 +183,7 @@ describe('mergePasteContent', () => {
pasteType: 'normal',
domToModelOption: { additionalAllowedTags: [] },
clipboardData: mockedClipboard,
fragment: document.createDocumentFragment(),
} as any;

mergePasteContent(editor, eventResult, true);
Expand Down Expand Up @@ -282,6 +291,7 @@ describe('mergePasteContent', () => {
domToModelOption: { additionalAllowedTags: [] },
customizedMerge,
clipboardData: mockedClipboard,
fragment: document.createDocumentFragment(),
} as any;

mergePasteContent(editor, eventResult, true);
Expand All @@ -304,6 +314,7 @@ describe('mergePasteContent', () => {
pasteType: 'mergeFormat',
domToModelOption: { additionalAllowedTags: [] },
clipboardData: mockedClipboard,
fragment: document.createDocumentFragment(),
} as any;

mergePasteContent(editor, eventResult, true);
Expand Down Expand Up @@ -379,7 +390,7 @@ describe('mergePasteContent', () => {

const mockedDomToModelOptions = 'OPTION1' as any;
const mockedDefaultDomToModelOptions = 'OPTIONS3' as any;
const mockedFragment = 'FRAGMENT' as any;
const mockedFragment = document.createDocumentFragment();

(editor as any).getEnvironment = () => ({
domToModelSettings: {
Expand Down Expand Up @@ -452,6 +463,7 @@ describe('mergePasteContent', () => {
domToModelOption: { additionalAllowedTags: [] },
clipboardData: mockedClipboard,
containsBlockElements: true,
fragment: document.createDocumentFragment(),
} as any;

mergePasteContent(editor, eventResult, true);
Expand Down Expand Up @@ -2043,4 +2055,100 @@ describe('mergePasteContent', () => {
});
expect(cloneModelSpy).toHaveBeenCalledTimes(1);
});

describe('scrollCaretIntoView based on paste fragment', () => {
function runTest(fragment: DocumentFragment, expectedScrollCaretIntoView: boolean) {
spyOn(mergeModelFile, 'mergeModel').and.callThrough();
sourceModel = createContentModelDocument();
const para = createParagraph();
para.segments.push(createSelectionMarker());
sourceModel.blocks.push(para);

mergePasteContent(
editor,
<any>{
fragment,
domToModelOption: <any>{},
pasteType: 'normal',
clipboardData: mockedClipboard,
},
true
);

expect(formatOptions).toBeDefined();
expect(formatOptions!.scrollCaretIntoView).toBe(expectedScrollCaretIntoView);
}

function createFragment(...nodes: Node[]): DocumentFragment {
const fragment = document.createDocumentFragment();
nodes.forEach(node => fragment.appendChild(node));
return fragment;
}

it('should not scroll caret into view when fragment is a single image', () => {
const img = document.createElement('img');
img.src = 'test.png';

runTest(createFragment(img), false);
});

it('should not scroll caret into view when fragment is a span wrapping an image', () => {
const span = document.createElement('span');
span.appendChild(document.createElement('img'));

runTest(createFragment(span), false);
});

it('should not scroll caret into view when fragment is a div wrapping an image', () => {
const div = document.createElement('div');
div.appendChild(document.createElement('img'));

runTest(createFragment(div), false);
});

it('should scroll caret into view when fragment is a single text node', () => {
runTest(createFragment(document.createTextNode('text')), true);
});

it('should scroll caret into view when fragment is a paragraph', () => {
const p = document.createElement('p');
p.textContent = 'text';

runTest(createFragment(p), true);
});

it('should scroll caret into view when fragment has multiple children', () => {
const img1 = document.createElement('img');
const img2 = document.createElement('img');

runTest(createFragment(img1, img2), true);
});

it('should scroll caret into view when span wraps multiple children', () => {
const span = document.createElement('span');
span.appendChild(document.createElement('img'));
span.appendChild(document.createElement('img'));

runTest(createFragment(span), true);
});

it('should scroll caret into view when span wraps a non-image element', () => {
const span = document.createElement('span');
span.appendChild(document.createElement('b'));

runTest(createFragment(span), true);
});

it('should scroll caret into view when span wraps a text node', () => {
const span = document.createElement('span');
span.appendChild(document.createTextNode('text'));
runTest(createFragment(span), true);
});

it('should not scroll caret into view when fragment is an anchor wrapping an image', () => {
const anchor = document.createElement('a');
anchor.appendChild(document.createElement('img'));
runTest(createFragment(anchor), false);
});
});
});
Loading