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 @@ -18,6 +18,7 @@ const BulletSelector = '* > span > span[style*=mso-special-format]';
const MsOfficeSpecialFormat = 'mso-special-format';
const CssStyleKey = 'style';
const MsoSpecialFormatRegex = /mso-special-format:\s*([^;]*)/;
const TableElementTags = new Set(['TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TD', 'TH']);

const clearListItemStyles = (format: ContentModelListItemLevelFormat): void => {
delete format.textAlign;
Expand Down Expand Up @@ -52,7 +53,9 @@ export function processPastedContentFromPowerPoint(
if (style.includes(MsOfficeSpecialFormat) && context.listFormat.levels.length > 0) {
return;
}
const bulletElement = element.querySelector(BulletSelector) as HTMLElement;
const bulletElement = !TableElementTags.has(element.tagName)
? (element.querySelector(BulletSelector) as HTMLElement)
: null;
if (bulletElement) {
const {
depth,
Expand Down Expand Up @@ -95,6 +98,13 @@ export function processPastedContentFromPowerPoint(
}
clearListItemStyles(listItem.levels[listItem.levels.length - 1].format);
clearListItemStyles(listItem.format);

// When the list is inside a table cell, remove the font size from the
// list marker so it does not inherit the list's font size. Otherwise a
// large marker may be rendered outside of the table cell.
if (element.closest('td,th')) {
delete listItem.formatHolder.format.fontSize;
}
});
} else {
context.defaultElementProcessors.element?.(group, element, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import * as moveChildNodes from 'roosterjs-content-model-dom/lib/domUtils/moveChildNodes';
import { createDefaultDomToModelContext } from '../../TestHelper';
import { createDomToModelContext, domToContentModel } from 'roosterjs-content-model-dom';
import { processPastedContentFromPowerPoint } from '../../../lib/paste/PowerPoint/processPastedContentFromPowerPoint';
import type { BeforePasteEvent, ClipboardData, DOMCreator } from 'roosterjs-content-model-types';
import type {
BeforePasteEvent,
ClipboardData,
ContentModelListItem,
DOMCreator,
} from 'roosterjs-content-model-types';

const getPasteEvent = (): BeforePasteEvent => {
return {
Expand Down Expand Up @@ -120,3 +126,88 @@ describe('processPastedContentFromPowerPointTest |', () => {
expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled();
});
});

describe('processPastedContentFromPowerPoint list handling |', () => {
const domCreator: DOMCreator = {
htmlToDOM: (html: string) => new DOMParser().parseFromString(html, 'text/html'),
};

const bulletHtml =
"<span style='font-size:7.0pt'>" +
"<span style='mso-special-format:bullet;font-family:Arial'>\u2022</span>" +
'</span>';

function convert(html: string) {
const ev = getPasteEvent();
ev.clipboardData.html = html;
ev.clipboardData.text = 'text';

processPastedContentFromPowerPoint(ev, domCreator);

const container = document.createElement('div');
container.innerHTML = html;

const context = createDomToModelContext(undefined, ev.domToModelOption);

return domToContentModel(container, context);
}

it('Converts a PowerPoint bullet div into a list item', () => {
const model = convert(
"<div style='margin-top:10.0pt;direction:ltr;text-align:left'>" +
bulletHtml +
"<span style='font-size:7.0pt;color:black'>Item</span>" +
'</div>'
);

expect(model.blocks.length).toBe(1);
expect(model.blocks[0].blockType).toBe('BlockGroup');
expect((model.blocks[0] as ContentModelListItem).blockGroupType).toBe('ListItem');
});

it('Keeps the list marker font size when the list is not inside a table', () => {
const model = convert(
"<div style='margin-top:10.0pt;direction:ltr;text-align:left;font-size:7.0pt'>" +
bulletHtml +
"<span style='font-size:7.0pt;color:black'>Item</span>" +
'</div>'
);

const listItem = model.blocks[0] as ContentModelListItem;

expect(listItem.blockGroupType).toBe('ListItem');
expect(listItem.formatHolder.format.fontSize).toBe('7pt');
});

it('Removes the list marker font size when the list is inside a table', () => {
const model = convert(
'<table><tbody><tr><td>' +
"<div style='margin-top:10.0pt;direction:ltr;text-align:left;font-size:7.0pt'>" +
bulletHtml +
"<span style='font-size:7.0pt;color:black'>Item</span>" +
'</div>' +
'</td></tr></tbody></table>'
);

const table = model.blocks[0] as any;
const listItem = table.rows[0].cells[0].blocks[0] as ContentModelListItem;

expect(model.blocks[0].blockType).toBe('Table');
expect(listItem.blockGroupType).toBe('ListItem');
expect(listItem.formatHolder.format.fontSize).toBeUndefined();
});

it('Does not convert a table element into a list when it contains a bullet', () => {
const model = convert(
'<table><tbody><tr><td>' +
"<div style='margin-top:10.0pt;direction:ltr;text-align:left'>" +
bulletHtml +
"<span style='font-size:7.0pt;color:black'>Item</span>" +
'</div>' +
'</td></tr></tbody></table>'
);

expect(model.blocks.length).toBe(1);
expect(model.blocks[0].blockType).toBe('Table');
});
});
Loading