From 9e834708923fb7289354558ae397370f1f992e37 Mon Sep 17 00:00:00 2001 From: AndresCT-98 Date: Thu, 30 May 2024 10:06:21 -0600 Subject: [PATCH 1/3] rename and export --- .../lib/tableEdit/editors/TableEditor.ts | 5 +- .../tableEdit/editors/features/CellResizer.ts | 48 +++++++++++++------ .../editors/features/TableInserter.ts | 6 ++- .../editors/features/TableResizer.ts | 47 +++++++++++------- 4 files changed, 74 insertions(+), 32 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/TableEditor.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/TableEditor.ts index cb29f02c87e..c3554089fef 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/TableEditor.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/TableEditor.ts @@ -100,7 +100,10 @@ export class TableEditor { .some(feature => feature?.div == node); } - onMouseMove(x: number, y: number) { + /** + * public only for testing purposes + */ + public onMouseMove(x: number, y: number) { // Get whole table rect const tableRect = normalizeRect(this.table.getBoundingClientRect()); diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts index 759025c1493..fe1040305da 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts @@ -45,18 +45,18 @@ export function createCellResizer( (anchorContainer || document.body).appendChild(div); - const context: DragAndDropContext = { editor, td, table, isRTL, zoomScale, onStart }; + const context: cellResizerContext = { editor, td, table, isRTL, zoomScale, onStart }; const setPosition = isHorizontal ? setHorizontalPosition : setVerticalPosition; setPosition(context, div); - const handler: DragAndDropHandler = { + const handler: DragAndDropHandler = { onDragStart, // Horizontal modifies row height, vertical modifies column width onDragging: isHorizontal ? onDraggingHorizontal : onDraggingVertical, onDragEnd: onEnd, }; - const featureHandler = new DragAndDropHelper( + const featureHandler = new DragAndDropHelper( div, context, setPosition, @@ -68,7 +68,11 @@ export function createCellResizer( return { node: td, div, featureHandler }; } -interface DragAndDropContext { +/** + * @internal + * Exported for testing + */ +export interface cellResizerContext { editor: IEditor; td: HTMLTableCellElement; table: HTMLTableElement; @@ -77,7 +81,11 @@ interface DragAndDropContext { onStart: () => void; } -interface DragAndDropInitValue { +/** + * @internal + * Exported for testing + */ +export interface cellResizerInitValue { cmTable: ContentModelTable | undefined; anchorColumn: number | undefined; anchorRow: number | undefined; @@ -85,7 +93,11 @@ interface DragAndDropInitValue { allWidths: number[]; } -function onDragStart(context: DragAndDropContext, event: MouseEvent): DragAndDropInitValue { +/** + * @internal + * Exported for testing + */ +export function onDragStart(context: cellResizerContext, event: MouseEvent): cellResizerInitValue { const { td, onStart } = context; const rect = normalizeRect(td.getBoundingClientRect()); @@ -131,10 +143,14 @@ function onDragStart(context: DragAndDropContext, event: MouseEvent): DragAndDro } } -function onDraggingHorizontal( - context: DragAndDropContext, +/** + * @internal + * Exported for testing + */ +export function onDraggingHorizontal( + context: cellResizerContext, event: MouseEvent, - initValue: DragAndDropInitValue, + initValue: cellResizerInitValue, deltaX: number, deltaY: number ) { @@ -162,10 +178,14 @@ function onDraggingHorizontal( } } -function onDraggingVertical( - context: DragAndDropContext, +/** + * @internal + * Exported for testing + */ +export function onDraggingVertical( + context: cellResizerContext, event: MouseEvent, - initValue: DragAndDropInitValue, + initValue: cellResizerInitValue, deltaX: number ) { const { table, isRTL } = context; @@ -211,7 +231,7 @@ function onDraggingVertical( } } -function setHorizontalPosition(context: DragAndDropContext, trigger: HTMLElement) { +function setHorizontalPosition(context: cellResizerContext, trigger: HTMLElement) { const { td } = context; const rect = normalizeRect(td.getBoundingClientRect()); if (rect) { @@ -223,7 +243,7 @@ function setHorizontalPosition(context: DragAndDropContext, trigger: HTMLElement } } -function setVerticalPosition(context: DragAndDropContext, trigger: HTMLElement) { +function setVerticalPosition(context: cellResizerContext, trigger: HTMLElement) { const { td, isRTL } = context; const rect = normalizeRect(td.getBoundingClientRect()); if (rect) { diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableInserter.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableInserter.ts index 6e0dfeeb63d..b0d77d3d0f7 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableInserter.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableInserter.ts @@ -92,7 +92,11 @@ export function createTableInserter( return null; } -class TableInsertHandler implements Disposable { +/** + * @internal + * Exported for test only + */ +export class TableInsertHandler implements Disposable { private disposer: undefined | (() => void); constructor( private div: HTMLDivElement, diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts index 9e256cad719..d252ac50f46 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts @@ -49,7 +49,7 @@ export function createTableResizer( (anchorContainer || document.body).appendChild(div); - const context: DragAndDropContext = { + const context: TableResizerContext = { isRTL, table, zoomScale, @@ -79,14 +79,14 @@ export function createTableResizer( return { node: table, div, featureHandler }; } -class TableResizer extends DragAndDropHelper { +class TableResizer extends DragAndDropHelper { private disposer: undefined | (() => void); constructor( trigger: HTMLElement, - context: DragAndDropContext, - onSubmit: (context: DragAndDropContext, trigger: HTMLElement) => void, - handler: DragAndDropHandler, + context: TableResizerContext, + onSubmit: (context: TableResizerContext, trigger: HTMLElement) => void, + handler: DragAndDropHandler, zoomScale: number, forceMobile?: boolean, onTableEditorCreated?: OnTableEditorCreatedCallback @@ -102,7 +102,7 @@ class TableResizer extends DragAndDropHelper Date: Thu, 30 May 2024 12:23:11 -0600 Subject: [PATCH 2/3] fix tests --- .../editors/features/TableResizer.ts | 8 + .../test/tableEdit/TableEditTestHelper.ts | 40 +- .../test/tableEdit/cellResizerTest.ts | 361 ++++++++------ .../test/tableEdit/tableData.ts | 20 +- .../test/tableEdit/tableEditPluginTest.ts | 28 ++ .../test/tableEdit/tableEditorTest.ts | 156 +++--- .../test/tableEdit/tableInserterTest.ts | 181 ++++--- .../test/tableEdit/tableResizerTest.ts | 445 +++++++++++------- 8 files changed, 737 insertions(+), 502 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts index d252ac50f46..088e1d7ceb8 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/TableResizer.ts @@ -102,6 +102,10 @@ class TableResizer extends DragAndDropHelper = {}; - const attachDomEvent = jasmine - .createSpy('attachDomEvent') - .and.callFake((core: EditorCore, eventMap: Record>) => { - getObjectKeys(eventMap || {}).forEach(key => { - const eventname = key as keyof HTMLElementEventMap; - const { beforeDispatch } = eventMap[key]; - const onEvent = (event: HTMLElementEventMap[typeof eventname]) => { - beforeDispatch && beforeDispatch(event); - }; - handler[eventname] = onEvent; - }); - return () => { - handler = {}; - }; - }); - - const coreApiOverride = { - attachDomEvent, - }; const editor = TestHelper.initEditor( TEST_ID, [plugin], undefined, - coreApiOverride, + undefined, anchorContainerSelector ); plugin.initialize(editor); - return { editor, plugin, handler }; + return { editor, plugin }; } /** @@ -62,7 +38,11 @@ export function beforeTableTest( * @param plugin The plugin to be disposed * @param TEST_ID The id of the editor div */ -export function afterTableTest(editor: IEditor, plugin: TableEditPlugin, TEST_ID: string) { +export function afterTableTest( + editor: IEditor, + plugin: TableEditor | TableEditPlugin, + TEST_ID: string +) { plugin.dispose(); !editor.isDisposed() && editor.dispose(); TestHelper.removeElement(TEST_ID); diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts index d1a47fbc6b5..86729a98162 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts @@ -1,157 +1,252 @@ -import { ContentModelTable, DOMEventHandlerFunction, IEditor } from 'roosterjs-content-model-types'; +import { ContentModelTable, EditorOptions, IEditor } from 'roosterjs-content-model-types'; +import { Editor } from 'roosterjs-content-model-core'; +import { getCMTableFromTable } from '../../lib/tableEdit/editors/utils/getTableFromContentModel'; +import { getCurrentTable } from './TableEditTestHelper'; import { getModelTable } from './tableData'; import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin'; import { - afterTableTest, - beforeTableTest, - getCellRect, - getCurrentTable, - initialize, - moveAndResize, -} from './TableEditTestHelper'; + cellResizerContext, + cellResizerInitValue, + onDragStart, + onDraggingHorizontal, + onDraggingVertical, +} from '../../lib/tableEdit/editors/features/CellResizer'; describe('Cell Resizer tests', () => { let editor: IEditor; - let plugin: TableEditPlugin; - const TEST_ID = 'cellResizerTest'; - let handler: Record; + let id = 'tableCellResizerContainerId'; + let targetId = 'tableCellResizerTestId'; + let tableEdit: TableEditPlugin; + let node: HTMLDivElement; + const cmTable: ContentModelTable = getModelTable(targetId); beforeEach(() => { - const setup = beforeTableTest(TEST_ID); - editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; + document.body.innerHTML = ''; + node = document.createElement('div'); + node.id = id; + document.body.insertBefore(node, document.body.childNodes[0]); + tableEdit = new TableEditPlugin(); + + let options: EditorOptions = { + plugins: [tableEdit], + initialModel: { + blockGroupType: 'Document', + blocks: [{ ...cmTable }], + format: {}, + }, + }; + + editor = new Editor(node, options); }); afterEach(() => { - afterTableTest(editor, plugin, TEST_ID); + editor.dispose(); + const div = document.getElementById(id); + div?.parentNode?.removeChild(div); + node.parentElement?.removeChild(node); }); - /************************ Resizing row related tests ************************/ - - function resizeRowTest( - table: ContentModelTable, - growth: number, - cellRow: number, - cellColumn: number - ) { - initialize(editor, table); - const delta = 50 * growth; - const cellRect = getCellRect(editor, cellRow, cellColumn); - const targetPos: number = cellRect.bottom + delta; - - const beforeHeight = getCurrentTable(editor).rows[cellRow].getBoundingClientRect().height; - moveAndResize( - { x: cellRect.left + cellRect.width / 2, y: cellRect.bottom }, - { x: cellRect.left + cellRect.width / 2, y: targetPos }, - 'horizontal', - editor, - handler, - TEST_ID - ); - const afterHeight = getCurrentTable(editor).rows[cellRow].getBoundingClientRect().height; - - growth > 0 - ? expect(afterHeight).toBeGreaterThan(beforeHeight) - : expect(afterHeight).toBeLessThan(beforeHeight); - } - - it('increases the height of the first row', () => { - resizeRowTest(getModelTable(), 1, 0, 0); - }); - - it('increases the height of the last row', () => { - const MODEL_TABLE = getModelTable(); - resizeRowTest(MODEL_TABLE, 1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); - }); + it('Resize - onDragStart', () => { + //Arrange + node.style.height = '500px'; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } - it('decreases the height of the first row', () => { - resizeRowTest(getModelTable(), -1, 0, 0); + const targetTd = (target as HTMLTableElement).rows[0].cells[0]; + + const onStartSpy = jasmine.createSpy('onStart'); + const context: cellResizerContext = { + editor: editor, + td: targetTd as HTMLTableCellElement, + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: onStartSpy, + }; + const editorCMTable = getCMTableFromTable(editor, target as HTMLTableElement); + + //Act + const initvalue = onDragStart(context, {} as MouseEvent); + + //Assert + expect(onStartSpy).toHaveBeenCalled(); + expect(initvalue.cmTable).toEqual(editorCMTable); + expect(initvalue.allWidths).toEqual(editorCMTable.widths); + expect(initvalue.anchorColumn).toEqual(0); + expect(initvalue.anchorRow).toEqual(0); + expect(initvalue.anchorRowHeight).toEqual(editorCMTable.rows[0].height); }); - it('decreases the height of the last row', () => { - const MODEL_TABLE = getModelTable(); - resizeRowTest(MODEL_TABLE, -1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); - }); + describe('Resize - onDragging', () => { + /************************ Resizing row related tests ************************/ + + function resizeRowTest(growth: number, cellRow: number, cellColumn: number) { + //Arrange + node.style.height = '500px'; + node.style.width = '500px'; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } + + const initValue: cellResizerInitValue = { + cmTable: cmTable, + anchorColumn: cellColumn, + anchorRow: cellRow, + anchorRowHeight: cmTable.rows[cellRow].height, + allWidths: cmTable.widths, + }; + + const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn]; + + const onStartSpy = jasmine.createSpy('onStart'); + const context: cellResizerContext = { + editor: editor, + td: targetTd as HTMLTableCellElement, + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: onStartSpy, + }; + const delta = 10 * growth; + const beforeHeight = getCurrentTable(editor).rows[cellRow].getBoundingClientRect() + .height; + + //Act + const dragHResult = onDraggingHorizontal( + context, + {} as MouseEvent, + initValue, + 0, + delta + ); + + //Assert + const afterHeight = getCurrentTable(editor).rows[cellRow].getBoundingClientRect() + .height; + expect(dragHResult).toBeTrue(); + growth > 0 + ? expect(afterHeight).toBeGreaterThan(beforeHeight) + : expect(afterHeight).toBeLessThan(beforeHeight); + } - /************************ Resizing column related tests ************************/ - - function resizeColumnTest( - table: ContentModelTable, - direction: number, - cellRow: number, - cellColumn: number - ) { - initialize(editor, table); - const delta = 20 * direction; - const cellRect = getCellRect(editor, cellRow, cellColumn); - const targetPos: number = cellRect.right + delta; - - const beforeWidth = getCurrentTable(editor).rows[cellRow].cells[ - cellColumn - ].getBoundingClientRect().width; - const beforeNextWidth = - cellColumn < table.widths.length - 1 - ? getCurrentTable(editor).rows[cellRow].cells[ - cellColumn + 1 - ].getBoundingClientRect().width - : undefined; - - moveAndResize( - { x: cellRect.right, y: cellRect.top + cellRect.height / 2 }, - { x: targetPos, y: cellRect.top + cellRect.height / 2 }, - 'vertical', - editor, - handler, - TEST_ID - ); - - const afterWidth = getCurrentTable(editor).rows[cellRow].cells[ - cellColumn - ].getBoundingClientRect().width; - const afterNextWidth = - cellColumn < table.widths.length - 1 - ? getCurrentTable(editor).rows[cellRow].cells[ - cellColumn + 1 - ].getBoundingClientRect().width - : undefined; - - direction > 0 - ? expect(afterWidth).toBeGreaterThan(beforeWidth) - : expect(afterWidth).toBeLessThan(beforeWidth); - - if (beforeNextWidth && afterNextWidth) { - direction > 0 - ? expect(afterNextWidth).toBeLessThan(beforeNextWidth) - : expect(afterNextWidth).toBeGreaterThan(beforeNextWidth); + it('increases the height of the first row', () => { + resizeRowTest(1, 0, 0); + }); + + it('increases the height of the last row', () => { + const MODEL_TABLE = cmTable; + resizeRowTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); + }); + + it('decreases the height of the first row', () => { + resizeRowTest(-1, 0, 0); + }); + + it('decreases the height of the last row', () => { + const MODEL_TABLE = cmTable; + resizeRowTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); + }); + + /************************ Resizing column related tests ************************/ + + function resizeColumnTest(growth: number, cellRow: number, cellColumn: number) { + //Arrange + node.style.height = '500px'; + node.style.width = '500px'; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } + + const initValue: cellResizerInitValue = { + cmTable: cmTable, + anchorColumn: cellColumn, + anchorRow: cellRow, + anchorRowHeight: cmTable.rows[cellRow].height, + allWidths: cmTable.widths, + }; + + const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn]; + + const onStartSpy = jasmine.createSpy('onStart'); + const context: cellResizerContext = { + editor: editor, + td: targetTd as HTMLTableCellElement, + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: onStartSpy, + }; + const delta = 10 * growth; + const beforeWidth = getCurrentTable(editor).rows[cellRow].cells[ + cellColumn + ].getBoundingClientRect().width; + const beforeNextWidth = + cellColumn < cmTable.widths.length - 1 + ? getCurrentTable(editor).rows[cellRow].cells[ + cellColumn + 1 + ].getBoundingClientRect().width + : undefined; + + //Act + const dragVResult = onDraggingVertical(context, {} as MouseEvent, initValue, delta); + + //Assert + const afterWidth = getCurrentTable(editor).rows[cellRow].cells[ + cellColumn + ].getBoundingClientRect().width; + const afterNextWidth = + cellColumn < cmTable.widths.length - 1 + ? getCurrentTable(editor).rows[cellRow].cells[ + cellColumn + 1 + ].getBoundingClientRect().width + : undefined; + expect(dragVResult).toBeTrue(); + growth > 0 + ? expect(afterWidth).toBeGreaterThan(beforeWidth) + : expect(afterWidth).toBeLessThan(beforeWidth); + + if (beforeNextWidth && afterNextWidth) { + growth > 0 + ? expect(afterNextWidth).toBeLessThan(beforeNextWidth) + : expect(afterNextWidth).toBeGreaterThan(beforeNextWidth); + } } - } - it('increases the width of the first column', () => { - resizeColumnTest(getModelTable(), 1, 0, 0); - }); + it('increases the width of the first column', () => { + resizeColumnTest(1, 0, 0); + }); - it('increases the width of the last column', () => { - const MODEL_TABLE = getModelTable(); - resizeColumnTest( - MODEL_TABLE, - 1, - MODEL_TABLE.rows.length - 1, - MODEL_TABLE.widths.length - 1 - ); - }); + it('increases the width of the last column', () => { + const MODEL_TABLE = cmTable; + resizeColumnTest(1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); + }); - it('decreases the width of the first column', () => { - resizeColumnTest(getModelTable(), -1, 0, 0); - }); + it('decreases the width of the first column', () => { + resizeColumnTest(-1, 0, 0); + }); - it('decreases the width of the last column', () => { - const MODEL_TABLE = getModelTable(); - resizeColumnTest( - MODEL_TABLE, - -1, - MODEL_TABLE.rows.length - 1, - MODEL_TABLE.widths.length - 1 - ); + it('decreases the width of the last column', () => { + const MODEL_TABLE = cmTable; + resizeColumnTest(-1, MODEL_TABLE.rows.length - 1, MODEL_TABLE.widths.length - 1); + }); }); }); diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/tableData.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/tableData.ts index 802f58c1713..09e97fe36e5 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/tableData.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/tableData.ts @@ -12,7 +12,7 @@ export const DEFAULT_TABLE_MERGED = /** * Regular 3 x 3 Table */ -export function getModelTable(): ContentModelTable { +export function getModelTable(id?: string): ContentModelTable { /* * —————————————— * | a1 | b1 | c1 | @@ -233,7 +233,7 @@ export function getModelTable(): ContentModelTable { ], }, ], - format: {}, + format: id ? { id: id } : {}, widths: [50, 50, 50], dataset: {}, }; @@ -242,7 +242,7 @@ export function getModelTable(): ContentModelTable { /** * 3 x 3 Table with merged central column */ -export function getMergedCenterColumnTable(): ContentModelTable { +export function getMergedCenterColumnTable(id?: string): ContentModelTable { /* * —————————————— * | a1 | | c1 | @@ -439,7 +439,7 @@ export function getMergedCenterColumnTable(): ContentModelTable { ], }, ], - format: {}, + format: id ? { id: id } : {}, widths: [50, 50, 50], dataset: {}, }; @@ -448,7 +448,7 @@ export function getMergedCenterColumnTable(): ContentModelTable { /** * 3 x 3 Table with merged central row */ -export function getMergedCenterRowTable(): ContentModelTable { +export function getMergedCenterRowTable(id?: string): ContentModelTable { /* * —————————————— * | a1 | b1 | c1 | @@ -645,7 +645,7 @@ export function getMergedCenterRowTable(): ContentModelTable { ], }, ], - format: {}, + format: id ? { id: id } : {}, widths: [50, 50, 50], dataset: {}, }; @@ -654,7 +654,7 @@ export function getMergedCenterRowTable(): ContentModelTable { /** * 2 x 2 Table with merged top row */ -export function getMergedTopRowTable(): ContentModelTable { +export function getMergedTopRowTable(id?: string): ContentModelTable { /* * ————————— * | a1 | @@ -750,7 +750,7 @@ export function getMergedTopRowTable(): ContentModelTable { ], }, ], - format: {}, + format: id ? { id: id } : {}, widths: [50, 50], dataset: {}, }; @@ -759,7 +759,7 @@ export function getMergedTopRowTable(): ContentModelTable { /** * 2 x 2 Table with merged first column */ -export function getMergedFirstColumnTable(): ContentModelTable { +export function getMergedFirstColumnTable(id?: string): ContentModelTable { /* * ————————— * | a1 | b1 | @@ -855,7 +855,7 @@ export function getMergedFirstColumnTable(): ContentModelTable { ], }, ], - format: {}, + format: id ? { id: id } : {}, widths: [50, 50], dataset: {}, }; diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditPluginTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditPluginTest.ts index f9981981a22..df3a3aca3b6 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditPluginTest.ts @@ -45,6 +45,34 @@ describe('TableEditPlugin', () => { document.body = document.createElement('body'); }); + it('onPluginEvent - input', () => { + const disposerSpy = spyOn(plugin, 'setTableEditor').and.callThrough(); + plugin.onPluginEvent({ eventType: 'input', rawEvent: null }); + expect(disposerSpy).toHaveBeenCalledWith(null); + }); + + it('onPluginEvent - contentChanged', () => { + const disposerSpy = spyOn(plugin, 'setTableEditor').and.callThrough(); + plugin.onPluginEvent({ eventType: 'contentChanged', source: null }); + expect(disposerSpy).toHaveBeenCalledWith(null); + }); + + it('onPluginEvent - scroll', () => { + const disposerSpy = spyOn(plugin, 'setTableEditor').and.callThrough(); + plugin.onPluginEvent({ + eventType: 'scroll', + rawEvent: null, + scrollContainer: editor.getScrollContainer(), + }); + expect(disposerSpy).toHaveBeenCalledWith(null); + }); + + it('onPluginEvent - zoomChanged', () => { + const disposerSpy = spyOn(plugin, 'setTableEditor').and.callThrough(); + plugin.onPluginEvent({ eventType: 'zoomChanged', newZoomScale: 1 }); + expect(disposerSpy).toHaveBeenCalledWith(null); + }); + it('setTableEditor - Dismiss table editor on mouse out', () => { const ele = createElement( { diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditorTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditorTest.ts index 6ad1efb7242..7cbe0623633 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditorTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/tableEditorTest.ts @@ -1,132 +1,178 @@ -import { DOMEventHandlerFunction, IEditor } from 'roosterjs-content-model-types'; -import { getModelTable } from './tableData'; +import { afterTableTest, beforeTableTest, getCellRect, initialize } from './TableEditTestHelper'; +import { ContentModelTable, IEditor } from 'roosterjs-content-model-types'; +import { getMergedFirstColumnTable, getMergedTopRowTable, getModelTable } from './tableData'; import { TABLE_MOVER_ID } from '../../lib/tableEdit/editors/features/TableMover'; import { TABLE_RESIZER_ID } from '../../lib/tableEdit/editors/features/TableResizer'; import { TableEditFeatureName } from '../../lib/tableEdit/editors/features/TableEditFeatureName'; -import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin'; +import { TableEditor } from '../../lib/tableEdit/editors/TableEditor'; import { HORIZONTAL_INSERTER_ID, VERTICAL_INSERTER_ID, } from '../../lib/tableEdit/editors/features/TableInserter'; -import { - afterTableTest, - beforeTableTest, - getCellRect, - initialize, - mouseToPoint, -} from './TableEditTestHelper'; import { HORIZONTAL_RESIZER_ID, VERTICAL_RESIZER_ID, } from '../../lib/tableEdit/editors/features/CellResizer'; -describe('TableEdit', () => { +describe('TableEditor', () => { describe('disableFeatures', () => { const insideTheOffset = 5; let editor: IEditor; - let plugin: TableEditPlugin; - let handler: Record; + let table: HTMLTableElement; + let tEditor: TableEditor; const TEST_ID = 'test'; - function runDisableFeatureSetup(featuresToDisable: TableEditFeatureName[]) { - // Create editor, plugin, and table + function runDisableFeatureSetup( + cmTable: ContentModelTable, + featuresToDisable: TableEditFeatureName[], + anchorContainer?: HTMLElement + ) { const setup = beforeTableTest(TEST_ID, undefined, featuresToDisable); editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; - return initialize(editor, getModelTable()); + const rect = initialize(editor, cmTable); + table = editor.getDOMHelper().queryElements('table')[0]; + const contentDiv = editor.getDocument().getElementById(TEST_ID); + tEditor = new TableEditor( + editor, + table, + () => {}, + anchorContainer, + contentDiv, + undefined, + featuresToDisable + ); + return rect; } + /************************ Disable features tests ************************/ + it('Disable Horizontal Inserter', () => { - const tableRect = runDisableFeatureSetup(['HorizontalTableInserter']); + const tableRect = runDisableFeatureSetup(getModelTable(), ['HorizontalTableInserter']); // Move mouse to bottom left of table - mouseToPoint({ x: tableRect.left - insideTheOffset, y: tableRect.bottom }, handler); + tEditor.onMouseMove(tableRect.left - insideTheOffset, tableRect.bottom); const feature = editor.getDocument().getElementById(HORIZONTAL_INSERTER_ID); expect(!!feature).toBe(false); }); it('Disable Vertical Inserter', () => { - const tableRect = runDisableFeatureSetup(['VerticalTableInserter']); + const tableRect = runDisableFeatureSetup(getModelTable(), ['VerticalTableInserter']); // Move mouse to top right of table - mouseToPoint({ x: tableRect.right, y: tableRect.top - insideTheOffset }, handler); + tEditor.onMouseMove(tableRect.right, tableRect.top - insideTheOffset); const feature = editor.getDocument().getElementById(VERTICAL_INSERTER_ID); expect(!!feature).toBe(false); }); it('Disable Horizontal Resizer', () => { - const tableRect = runDisableFeatureSetup(['CellResizer']); + const tableRect = runDisableFeatureSetup(getModelTable(), ['CellResizer']); // Move mouse to bottom right of table - mouseToPoint({ x: tableRect.right - insideTheOffset, y: tableRect.bottom }, handler); + tEditor.onMouseMove(tableRect.right - insideTheOffset, tableRect.bottom); const feature = editor.getDocument().getElementById(HORIZONTAL_RESIZER_ID); expect(!!feature).toBe(false); }); it('Disable Vertical Resizer', () => { - const tableRect = runDisableFeatureSetup(['CellResizer']); + const tableRect = runDisableFeatureSetup(getModelTable(), ['CellResizer']); // Move mouse to bottom right of table - mouseToPoint({ x: tableRect.right, y: tableRect.bottom - insideTheOffset }, handler); + tEditor.onMouseMove(tableRect.right, tableRect.bottom - insideTheOffset); const feature = editor.getDocument().getElementById(VERTICAL_RESIZER_ID); expect(!!feature).toBe(false); }); it('Disable Table Resizer', () => { - const tableRect = runDisableFeatureSetup(['TableResizer']); + const tableRect = runDisableFeatureSetup(getModelTable(), ['TableResizer']); // Move mouse to center of table - mouseToPoint( - { - x: tableRect.left + tableRect.width / 2, - y: tableRect.top + tableRect.height / 2, - }, - handler + tEditor.onMouseMove( + tableRect.left + tableRect.width / 2, + tableRect.top + tableRect.height / 2 ); const feature = editor.getDocument().getElementById(TABLE_RESIZER_ID); expect(!!feature).toBe(false); }); it('Disable Table Mover', () => { - const tableRect = runDisableFeatureSetup(['TableMover', 'TableSelector']); + const tableRect = runDisableFeatureSetup(getModelTable(), [ + 'TableMover', + 'TableSelector', + ]); // Move mouse to center of table - mouseToPoint( - { - x: tableRect.left + tableRect.width / 2, - y: tableRect.top + tableRect.height / 2, - }, - handler + tEditor.onMouseMove( + tableRect.left + tableRect.width / 2, + tableRect.top + tableRect.height / 2 ); const feature = editor.getDocument().getElementById(TABLE_MOVER_ID); expect(!!feature).toBe(false); }); + /************************ Table Inserter tests ************************/ + + it('Not add table inserter if cursor on top left corner', () => { + const tableRect = runDisableFeatureSetup(getModelTable(), []); + // Move mouse to top left of table + tEditor.onMouseMove(tableRect.left - insideTheOffset, tableRect.top - insideTheOffset); + const featureH = editor.getDocument().getElementById(HORIZONTAL_INSERTER_ID); + const featureV = editor.getDocument().getElementById(VERTICAL_INSERTER_ID); + expect(!!featureH).toBe(false); + expect(!!featureV).toBe(false); + }); + + it('Not add table inserter if cursor on top middle of merged top row', () => { + const tableRect = runDisableFeatureSetup(getMergedTopRowTable(), []); + // Move mouse to top middle of table + tEditor.onMouseMove( + tableRect.left + tableRect.width / 2, + tableRect.top - insideTheOffset + ); + const featureH = editor.getDocument().getElementById(HORIZONTAL_INSERTER_ID); + const featureV = editor.getDocument().getElementById(VERTICAL_INSERTER_ID); + expect(!!featureH).toBe(false); + expect(!!featureV).toBe(false); + }); + + it('Add table inserter if cursor on left middle of merged first column', () => { + const tableRect = runDisableFeatureSetup(getMergedFirstColumnTable(), []); + // Move mouse to left middle of table + tEditor.onMouseMove( + tableRect.left - insideTheOffset, + tableRect.top + tableRect.height / 2 + ); + const featureH = editor.getDocument().getElementById(HORIZONTAL_INSERTER_ID); + const featureV = editor.getDocument().getElementById(VERTICAL_INSERTER_ID); + expect(!!featureH).toBe(true); + expect(!!featureV).toBe(false); + }); + afterEach(() => { - afterTableTest(editor, plugin, TEST_ID); + afterTableTest(editor, tEditor, TEST_ID); }); }); describe('anchorContainer', () => { let editor: IEditor; - let plugin: TableEditPlugin; + let tEditor: TableEditor; + let table: any; const TEST_ID = 'cellResizerTest'; const ANCHOR_CLASS = 'anchor_' + TEST_ID; - let handler: Record; afterEach(() => { - afterTableTest(editor, plugin, TEST_ID); + afterTableTest(editor, tEditor, TEST_ID); }); it('Table editor features, resizer and mover, inserted on anchor', () => { - // Create editor, plugin, and table const setup = beforeTableTest(TEST_ID, ANCHOR_CLASS); editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; initialize(editor, getModelTable()); + table = editor.getDOMHelper().queryElements('table')[0]; + const contentDiv = editor.getDocument().getElementById(TEST_ID); + const anchor = editor + .getDocument() + .getElementsByClassName(ANCHOR_CLASS)[0] as HTMLElement; + tEditor = new TableEditor(editor, table, () => {}, anchor, contentDiv, undefined); // Move mouse to the first cell const cellRect = getCellRect(editor, 0, 0); - mouseToPoint({ x: cellRect.left, y: cellRect.bottom }, handler); + tEditor.onMouseMove(cellRect.left, cellRect.bottom); // Look for table mover and resizer on the anchor - const anchor = editor.getDocument().getElementsByClassName(ANCHOR_CLASS)[0]; const mover = anchor?.querySelector('#' + TABLE_MOVER_ID); const resizer = anchor?.querySelector('#' + TABLE_RESIZER_ID); expect(!!mover).toBe(true); @@ -134,19 +180,21 @@ describe('TableEdit', () => { }); it('Table editor features, resizer and mover, not inserted on anchor', () => { - // Create editor, plugin, and table const setup = beforeTableTest(TEST_ID); editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; initialize(editor, getModelTable()); + table = editor.getDOMHelper().queryElements('table')[0]; + const contentDiv = editor.getDocument().getElementById(TEST_ID); + const anchor = editor + .getDocument() + .getElementsByClassName(ANCHOR_CLASS)[0] as HTMLElement; + tEditor = new TableEditor(editor, table, () => {}, anchor, contentDiv, undefined); // Move mouse to the first cell const cellRect = getCellRect(editor, 0, 0); - mouseToPoint({ x: cellRect.left, y: cellRect.bottom }, handler); + tEditor.onMouseMove(cellRect.left, cellRect.bottom); // Look for table mover and resizer on the anchor - const anchor = editor.getDocument().getElementsByClassName(ANCHOR_CLASS)[0]; const mover = anchor?.querySelector('#' + TABLE_MOVER_ID); const resizer = anchor?.querySelector('#' + TABLE_RESIZER_ID); expect(!!mover).toBe(false); diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/tableInserterTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/tableInserterTest.ts index 74386776bf7..0e416eb3a48 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/tableInserterTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/tableInserterTest.ts @@ -1,124 +1,108 @@ import * as getIntersectedRect from '../../lib/pluginUtils/Rect/getIntersectedRect'; -import { DOMEventHandlerFunction, IEditor } from 'roosterjs-content-model-types'; -import { getMergedFirstColumnTable, getMergedTopRowTable, getModelTable } from './tableData'; -import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin'; +import { Editor } from 'roosterjs-content-model-core'; +import { getModelTable } from './tableData'; import { HORIZONTAL_INSERTER_ID, + TableInsertHandler, VERTICAL_INSERTER_ID, createTableInserter, } from '../../lib/tableEdit/editors/features/TableInserter'; -import { - Position, - afterTableTest, - beforeTableTest, - getCurrentTable, - getTableColumns, - getTableRows, - initialize, -} from './TableEditTestHelper'; +import { ContentModelTable, EditorOptions, IEditor } from 'roosterjs-content-model-types'; + +import { getCurrentTable, getTableColumns, getTableRows } from './TableEditTestHelper'; describe('Table Inserter tests', () => { let editor: IEditor; - let plugin: TableEditPlugin; - const insideTheOffset = 5; - const TEST_ID = 'inserterTest'; - let handler: Record; - - beforeEach(() => { - const setup = beforeTableTest(TEST_ID); - editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; - }); + let id = 'tableInserterContainerId'; + let targetId = 'tableInserterTestId'; + let tInserter: TableInsertHandler; + let node: HTMLDivElement; + + function initialize(table: ContentModelTable) { + document.body.innerHTML = ''; + node = document.createElement('div'); + node.id = id; + document.body.insertBefore(node, document.body.childNodes[0]); + + let options: EditorOptions = { + plugins: [], + initialModel: { + blockGroupType: 'Document', + blocks: [{ ...table }], + format: {}, + }, + }; + + editor = new Editor(node, options); + } afterEach(() => { - afterTableTest(editor, plugin, TEST_ID); + !editor.isDisposed && editor.dispose(); + tInserter && tInserter.dispose(); + const div = document.getElementById(id); + div?.parentNode?.removeChild(div); + node.parentElement?.removeChild(node); }); - function isClickInsideInserter(click: Position, rect: DOMRect) { - return ( - click.x >= rect.left && - click.x <= rect.right && - click.y >= rect.top && - click.y <= rect.bottom - ); - } + function runInserterTest( + table: ContentModelTable, + inserterType: string, + colIndex: number, + rowIndex: number + ) { + //Arrange + initialize(table); + const nodeHeight = 1000; + const nodeWidth = 1000; + node.style.height = `${nodeHeight}px`; + node.style.width = `${nodeWidth}px`; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } - function runInserterTest(inserterType: string, mouseEnd: Position) { - handler.mousemove( - new MouseEvent('mousemove', { - clientX: mouseEnd.x, - clientY: mouseEnd.y, - }) + const targetTd = (target as HTMLTableElement).rows[rowIndex].cells[colIndex]; + const div = document.createElement('div'); + const onInsertSpy = jasmine.createSpy('onInsert'); + const beforeTable = getCurrentTable(editor); + const rows = getTableRows(beforeTable); + const cols = getTableColumns(beforeTable); + tInserter = new TableInsertHandler( + div, + targetTd, + target as HTMLTableElement, + inserterType == HORIZONTAL_INSERTER_ID, + editor, + onInsertSpy ); - const inserter = editor.getDocument().getElementById(inserterType); - if (!!inserter) { - const inserterRect = inserter.getBoundingClientRect(); - if (!isClickInsideInserter(mouseEnd, inserterRect)) { - // Inserter is visible, but pointer is not over it - return 'not clickable'; - } - let table = getCurrentTable(editor); - const rows = getTableRows(table); - const cols = getTableColumns(table); - inserter.dispatchEvent(new MouseEvent('click')); - - table = getCurrentTable(editor); - const newRows = getTableRows(table); - const newCols = getTableColumns(table); - expect(newRows).toBe(inserterType == VERTICAL_INSERTER_ID ? rows : rows + 1); - expect(newCols).toBe(inserterType == HORIZONTAL_INSERTER_ID ? cols : cols + 1); - } - return !!inserter ? 'found' : 'not found'; + //Act + div.click(); + + //Assert + expect(onInsertSpy).toHaveBeenCalled(); + const afterTable = getCurrentTable(editor); + const newRows = getTableRows(afterTable); + const newCols = getTableColumns(afterTable); + expect(newRows).toBe(inserterType == VERTICAL_INSERTER_ID ? rows : rows + 1); + expect(newCols).toBe(inserterType == HORIZONTAL_INSERTER_ID ? cols : cols + 1); } - it('adds a new column if the vertical inserter is detected and clicked', () => { - const rect = initialize(editor, getModelTable()); - const inserterFound = runInserterTest(VERTICAL_INSERTER_ID, { - x: rect.right, - y: rect.top - insideTheOffset, - }); - expect(inserterFound).toBe('found'); - }); - - it('adds a new row if the horizontal inserter is detected and clicked', () => { - const rect = initialize(editor, getModelTable()); - const inserterFound = runInserterTest(HORIZONTAL_INSERTER_ID, { - x: rect.left - insideTheOffset, - y: rect.bottom, - }); - expect(inserterFound).toBe('found'); - }); - - it('does not add inserter if top left corner hovered', () => { - const rect = initialize(editor, getModelTable()); - const inserterFound = runInserterTest(VERTICAL_INSERTER_ID, { - x: rect.left - insideTheOffset, - y: rect.top - insideTheOffset, - }); - expect(inserterFound).toBe('not found'); - }); - - it('does not add new column if top middle clicked on merged top row', () => { - const rect = initialize(editor, getMergedTopRowTable()); - const inserterFound = runInserterTest(VERTICAL_INSERTER_ID, { - x: (rect.right - rect.left) / 2 + 10, - y: rect.top - insideTheOffset, - }); - expect(inserterFound).toBe('not clickable'); + it('adds a new column', () => { + runInserterTest(getModelTable(targetId), VERTICAL_INSERTER_ID, 0, 0); }); - it('does not add new row if left middle clicked on merged first column', () => { - const rect = initialize(editor, getMergedFirstColumnTable()); - const inserterFound = runInserterTest(HORIZONTAL_INSERTER_ID, { - x: rect.left - insideTheOffset, - y: (rect.bottom - rect.top) / 2, - }); - expect(inserterFound).toBe('not clickable'); + it('adds a new row', () => { + runInserterTest(getModelTable(targetId), HORIZONTAL_INSERTER_ID, 0, 0); }); it('Customize table inserter', () => { + initialize(getModelTable(targetId)); spyOn(getIntersectedRect, 'getIntersectedRect').and.returnValue({ bottom: 10, left: 10, @@ -173,6 +157,7 @@ describe('Table Inserter tests', () => { }); it('Customize table inserter, do not customize editortype is not in the cb', () => { + initialize(getModelTable(targetId)); spyOn(getIntersectedRect, 'getIntersectedRect').and.returnValue({ bottom: 10, left: 10, diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/tableResizerTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/tableResizerTest.ts index 7acb5c00941..75ae8d9fc5d 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/tableResizerTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/tableResizerTest.ts @@ -1,204 +1,293 @@ import * as getIntersectedRect from '../../lib/pluginUtils/Rect/getIntersectedRect'; -import { createTableResizer } from '../../lib/tableEdit/editors/features/TableResizer'; +import { ContentModelTable, EditorOptions, IEditor } from 'roosterjs-content-model-types'; +import { Editor } from 'roosterjs-content-model-core'; +import { getCMTableFromTable } from '../../lib/tableEdit/editors/utils/getTableFromContentModel'; +import { getCurrentTable, getTableRectSet, Position, resizeDirection } from './TableEditTestHelper'; import { getModelTable } from './tableData'; import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin'; - -import { - ContentModelTable, - DOMEventHandlerFunction, - IEditor, - PluginEvent, -} from 'roosterjs-content-model-types'; import { - Position, - afterTableTest, - beforeTableTest, - getCellRect, - getCurrentTable, - getTableRectSet, - initialize, - moveAndResize, - resizeDirection, -} from './TableEditTestHelper'; - -const TABLE_RESIZER_ID = '_Table_Resizer'; - -xdescribe('Table Resizer tests', () => { + createTableResizer, + TableResizerInitValue, + onDragging, + onDragStart, + TableResizerContext, + onDragEnd, +} from '../../lib/tableEdit/editors/features/TableResizer'; + +describe('Table Resizer tests', () => { let editor: IEditor; - let plugin: TableEditPlugin; - const TEST_ID = 'resizerTest'; - let handler: Record; + let id = 'tableResizerContainerId'; + let targetId = 'tableResizerTestId'; + let tableEdit: TableEditPlugin; + let node: HTMLDivElement; + const cmTable: ContentModelTable = getModelTable(targetId); beforeEach(() => { - const setup = beforeTableTest(TEST_ID); - editor = setup.editor; - plugin = setup.plugin; - handler = setup.handler; + document.body.innerHTML = ''; + node = document.createElement('div'); + node.id = id; + document.body.insertBefore(node, document.body.childNodes[0]); + tableEdit = new TableEditPlugin(); + + let options: EditorOptions = { + plugins: [tableEdit], + initialModel: { + blockGroupType: 'Document', + blocks: [{ ...cmTable }], + format: {}, + }, + }; + + editor = new Editor(node, options); }); afterEach(() => { - afterTableTest(editor, plugin, TEST_ID); + editor.dispose(); + tableEdit.dispose(); + const div = document.getElementById(id); + div?.parentNode?.removeChild(div); + node.parentElement?.removeChild(node); }); - /************************** Resizer removing tests **************************/ + it('Resize - onDragStart', () => { + //Arrange + node.style.height = '500px'; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); - function removeResizerTest(pluginEvent: PluginEvent) { - let resizer: HTMLElement | null = null; - plugin.initialize(editor); - initialize(editor, getModelTable()); - const cellRect = getCellRect(editor, 0, 0); - handler.mousemove( - new MouseEvent('mousemove', { clientX: cellRect?.right, clientY: cellRect?.bottom }) - ); - resizer = editor.getDocument().getElementById(TABLE_RESIZER_ID); - expect(!!resizer).toBe(true); - plugin.onPluginEvent(pluginEvent); - resizer = editor.getDocument().getElementById(TABLE_RESIZER_ID); - expect(!!resizer).toBe(false); - } - - it('removes table resizer on input', () => { - const pluginEvent: PluginEvent = { - eventType: 'input', - rawEvent: null, - }; - removeResizerTest(pluginEvent); - }); + if (!target) { + fail('Table not found'); + return; + } - it('removes table resizer on content change', () => { - const pluginEvent: PluginEvent = { - eventType: 'contentChanged', - source: null, + const div = document.createElement('div'); + const onStartSpy = jasmine.createSpy('onStart'); + const context: TableResizerContext = { + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: onStartSpy, + onEnd: () => false, + div: div, + editor: editor, + contentDiv: node, }; - removeResizerTest(pluginEvent); - }); - it('removes table resizer on scrolling', () => { - const pluginEvent: PluginEvent = { - eventType: 'scroll', - scrollContainer: editor.getScrollContainer(), - rawEvent: null, - }; - removeResizerTest(pluginEvent); + const editorCMTable = getCMTableFromTable(editor, target as HTMLTableElement); + + const heights: number[] = []; + editorCMTable?.rows.forEach(row => { + heights.push(row.height); + }); + const tableRect = target.getBoundingClientRect(); + + //Act + const initvalue = onDragStart(context, {} as MouseEvent); + + //Assert + expect(onStartSpy).toHaveBeenCalled(); + expect(initvalue.cmTable).toEqual(editorCMTable); + expect(initvalue.originalRect).toEqual(tableRect); + expect(initvalue.originalHeights).toEqual(heights); + expect(initvalue.originalWidths).toEqual(editorCMTable.widths); }); - /************************ Resizing table related tests ************************/ - - function resizeWholeTableTest( - table: ContentModelTable, - growth: number, - direction: resizeDirection - ) { - const delta = 20 * growth; - const tableRect = initialize(editor, table); - const mouseStart = { x: tableRect.right + 3, y: tableRect.bottom + 3 }; - let mouseEnd: Position = { x: 0, y: 0 }; - switch (direction) { - case 'horizontal': - mouseEnd = { x: tableRect.right + 3 + delta, y: tableRect.bottom + 3 }; - break; - case 'vertical': - mouseEnd = { x: tableRect.right + 3, y: tableRect.bottom + 3 + delta }; - break; - case 'both': - mouseEnd = { x: tableRect.right + 3 + delta, y: tableRect.bottom + 3 + delta }; - break; + describe('Resize - onDragging', () => { + function resizeWholeTableTest(growth: number, direction: resizeDirection) { + //Arrange + const nodeHeight = 1000; + const nodeWidth = 1000; + node.style.height = `${nodeHeight}px`; + node.style.width = `${nodeWidth}px`; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } + + const div = document.createElement('div'); + + const context: TableResizerContext = { + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: () => {}, + onEnd: () => false, + div: div, + editor: editor, + contentDiv: node, + }; + + const heights: number[] = []; + cmTable?.rows.forEach(row => { + heights.push(row.height); + }); + const tableRect = target.getBoundingClientRect(); + + const initValue: TableResizerInitValue = { + originalRect: tableRect, + originalHeights: heights, + originalWidths: cmTable.widths, + cmTable: cmTable, + }; + const beforeSize = getTableRectSet(getCurrentTable(editor)); + const delta = 10 * growth; + let mouseEnd: Position = { x: 0, y: 0 }; + switch (direction) { + case 'horizontal': + mouseEnd = { x: 3 * delta, y: 0 }; + break; + case 'vertical': + mouseEnd = { x: 0, y: 3 * delta }; + break; + case 'both': + mouseEnd = { x: 3 * delta, y: 3 * delta }; + break; + } + + //Act + const result = onDragging(context, {} as MouseEvent, initValue, mouseEnd.x, mouseEnd.y); + + //Assert + expect(result).toBeTrue(); + const afterSize = getTableRectSet(getCurrentTable(editor)); + compareTableRects(beforeSize, afterSize, growth, direction); } - const beforeSize = getTableRectSet(getCurrentTable(editor)); - moveAndResize(mouseStart, mouseEnd, 'both', editor, handler, TEST_ID); - const afterSize = getTableRectSet(getCurrentTable(editor)); - compareTableRects(beforeSize, afterSize, growth, direction); - } - - function verifyTableRectChange( - rect1: DOMRect, - rect2: DOMRect, - growth: number, - direction: resizeDirection - ): boolean { - switch (direction) { - case 'horizontal': - return growth > 0 ? rect1.width < rect2.width : rect1.width > rect2.width; - case 'vertical': - return growth > 0 ? rect1.height < rect2.height : rect1.height > rect2.height; - case 'both': - return growth > 0 - ? rect1.width < rect2.width && rect1.height < rect2.height - : rect1.width > rect2.width && rect1.height > rect2.height; + + function verifyTableRectChange( + rect1: DOMRect, + rect2: DOMRect, + growth: number, + direction: resizeDirection + ): boolean { + switch (direction) { + case 'horizontal': + return growth > 0 ? rect1.width < rect2.width : rect1.width > rect2.width; + case 'vertical': + return growth > 0 ? rect1.height < rect2.height : rect1.height > rect2.height; + case 'both': + return growth > 0 + ? rect1.width < rect2.width && rect1.height < rect2.height + : rect1.width > rect2.width && rect1.height > rect2.height; + } } - } - - function verifyCellRectChange( - rect1: DOMRect, - rect2: DOMRect, - growth: number, - direction: resizeDirection - ): boolean { - switch (direction) { - case 'horizontal': - return rect1.top == rect2.top && rect1.bottom == rect2.bottom && growth > 0 - ? rect1.left <= rect2.left && rect1.right <= rect2.right - : rect1.left >= rect2.left && rect1.right >= rect2.right; - case 'vertical': - return rect1.left == rect2.left && rect1.right == rect2.right && growth > 0 - ? rect1.top <= rect2.top && rect1.bottom <= rect2.bottom - : rect1.top >= rect2.top && rect1.bottom >= rect2.bottom; - case 'both': - return growth > 0 - ? rect1.left <= rect2.left && - rect1.right <= rect2.right && - rect1.top <= rect2.top && - rect1.bottom <= rect2.bottom - : rect1.left >= rect2.left && - rect1.right >= rect2.right && - rect1.top >= rect2.top && - rect1.bottom >= rect2.bottom; + + function verifyCellRectChange( + rect1: DOMRect, + rect2: DOMRect, + growth: number, + direction: resizeDirection + ): boolean { + switch (direction) { + case 'horizontal': + return rect1.top == rect2.top && rect1.bottom == rect2.bottom && growth > 0 + ? rect1.left <= rect2.left && rect1.right <= rect2.right + : rect1.left >= rect2.left && rect1.right >= rect2.right; + case 'vertical': + return rect1.left == rect2.left && rect1.right == rect2.right && growth > 0 + ? rect1.top <= rect2.top && rect1.bottom <= rect2.bottom + : rect1.top >= rect2.top && rect1.bottom >= rect2.bottom; + case 'both': + return growth > 0 + ? rect1.left <= rect2.left && + rect1.right <= rect2.right && + rect1.top <= rect2.top && + rect1.bottom <= rect2.bottom + : rect1.left >= rect2.left && + rect1.right >= rect2.right && + rect1.top >= rect2.top && + rect1.bottom >= rect2.bottom; + } + } + + function compareTableRects( + beforeTableRectSet1: DOMRect[], + afterTableRectSet2: DOMRect[], + growth: number, + direction: resizeDirection + ) { + expect(beforeTableRectSet1.length).toBe(afterTableRectSet2.length); + beforeTableRectSet1.forEach((rect, i) => { + i == 0 + ? expect( + verifyTableRectChange(rect, afterTableRectSet2[i], growth, direction) + ).toBe(true) // Verify a change to whole table size + : expect( + verifyCellRectChange(rect, afterTableRectSet2[i], growth, direction) + ).toBe( + true // Verify a change to each cell size + ); + }); } - } - - function compareTableRects( - beforeTableRectSet1: DOMRect[], - afterTableRectSet2: DOMRect[], - growth: number, - direction: resizeDirection - ) { - expect(beforeTableRectSet1.length).toBe(afterTableRectSet2.length); - beforeTableRectSet1.forEach((rect, i) => { - i == 0 - ? expect( - verifyTableRectChange(rect, afterTableRectSet2[i], growth, direction) - ).toBe(true) // Verify a change to whole table size - : expect(verifyCellRectChange(rect, afterTableRectSet2[i], growth, direction)).toBe( - true // Verify a change to each cell size - ); + + it('increases the width of the table', () => { + resizeWholeTableTest(1, 'horizontal'); }); - } - it('increases the width of the table', () => { - resizeWholeTableTest(getModelTable(), 1, 'horizontal'); - }); + it('increases the height of the table', () => { + resizeWholeTableTest(1, 'vertical'); + }); - it('increases the height of the table', () => { - resizeWholeTableTest(getModelTable(), 1, 'vertical'); - }); + it('increases the width and height of the table', () => { + resizeWholeTableTest(1, 'both'); + }); - it('increases the width and height of the table', () => { - resizeWholeTableTest(getModelTable(), 1, 'both'); - }); + it('decreases the width of the table', () => { + resizeWholeTableTest(-1, 'horizontal'); + }); - it('decreases the width of the table', () => { - resizeWholeTableTest(getModelTable(), -1, 'horizontal'); - }); + it('decreases the height of the table', () => { + resizeWholeTableTest(-1, 'vertical'); + }); - it('decreases the height of the table', () => { - resizeWholeTableTest(getModelTable(), -1, 'vertical'); + it('decreases the width and height of the table', () => { + resizeWholeTableTest(-1, 'both'); + }); }); - it('decreases the width and height of the table', () => { - resizeWholeTableTest(getModelTable(), -1, 'both'); + it('Resize - onDragEnd', () => { + //Arrange + node.style.height = '500px'; + node.style.overflowX = 'auto'; + node.scrollTop = 0; + const target = document.getElementById(targetId); + editor.focus(); + + if (!target) { + fail('Table not found'); + return; + } + + const div = document.createElement('div'); + const styleSpy = spyOnProperty(div, 'style').and.callThrough(); + const onEndSpy = jasmine.createSpy('onEnd'); + const context: TableResizerContext = { + table: target as HTMLTableElement, + isRTL: false, + zoomScale: 1, + onStart: () => {}, + onEnd: onEndSpy, + div: div, + editor: editor, + contentDiv: node, + }; + + //Act + const result = onDragEnd(context, {} as MouseEvent, undefined); + + //Assert + expect(onEndSpy).toHaveBeenCalled(); + expect(result).toBeFalse(); + expect(styleSpy).toHaveBeenCalled(); }); - it('Customize table inserter', () => { + it('Customize table resizer', () => { spyOn(getIntersectedRect, 'getIntersectedRect').and.returnValue({ bottom: 10, left: 10, @@ -210,17 +299,18 @@ xdescribe('Table Resizer tests', () => { const changeCb = jasmine.createSpy('changeCb'); //Act const result = createTableResizer( - { + ({ getBoundingClientRect: () => { return { bottom: 10, height: 10, left: 10, right: 10, + top: 10, }; - ownerDocument: document; }, - }, + ownerDocument: document, + }), editor, false, () => {}, @@ -241,7 +331,7 @@ xdescribe('Table Resizer tests', () => { expect(changeCb).toHaveBeenCalled(); }); - it('Customize table inserter, do not customize wrong editor type', () => { + it('Customize table resizer, do not customize wrong editor type', () => { spyOn(getIntersectedRect, 'getIntersectedRect').and.returnValue({ bottom: 10, left: 10, @@ -253,17 +343,18 @@ xdescribe('Table Resizer tests', () => { const changeCb = jasmine.createSpy('changeCb'); //Act const result = createTableResizer( - { + ({ getBoundingClientRect: () => { return { bottom: 10, height: 10, left: 10, right: 10, + top: 10, }; - ownerDocument: document; }, - }, + ownerDocument: document, + }), editor, false, () => {}, From c13dfd31143061b5c691d2e25463a6feb5c8adc9 Mon Sep 17 00:00:00 2001 From: AndresCT-98 Date: Thu, 30 May 2024 14:20:06 -0600 Subject: [PATCH 3/3] capitalisation --- .../tableEdit/editors/features/CellResizer.ts | 24 +++++++++---------- .../test/tableEdit/cellResizerTest.ts | 14 +++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts index fe1040305da..add6bc3a658 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts @@ -45,18 +45,18 @@ export function createCellResizer( (anchorContainer || document.body).appendChild(div); - const context: cellResizerContext = { editor, td, table, isRTL, zoomScale, onStart }; + const context: CellResizerContext = { editor, td, table, isRTL, zoomScale, onStart }; const setPosition = isHorizontal ? setHorizontalPosition : setVerticalPosition; setPosition(context, div); - const handler: DragAndDropHandler = { + const handler: DragAndDropHandler = { onDragStart, // Horizontal modifies row height, vertical modifies column width onDragging: isHorizontal ? onDraggingHorizontal : onDraggingVertical, onDragEnd: onEnd, }; - const featureHandler = new DragAndDropHelper( + const featureHandler = new DragAndDropHelper( div, context, setPosition, @@ -72,7 +72,7 @@ export function createCellResizer( * @internal * Exported for testing */ -export interface cellResizerContext { +export interface CellResizerContext { editor: IEditor; td: HTMLTableCellElement; table: HTMLTableElement; @@ -85,7 +85,7 @@ export interface cellResizerContext { * @internal * Exported for testing */ -export interface cellResizerInitValue { +export interface CellResizerInitValue { cmTable: ContentModelTable | undefined; anchorColumn: number | undefined; anchorRow: number | undefined; @@ -97,7 +97,7 @@ export interface cellResizerInitValue { * @internal * Exported for testing */ -export function onDragStart(context: cellResizerContext, event: MouseEvent): cellResizerInitValue { +export function onDragStart(context: CellResizerContext, event: MouseEvent): CellResizerInitValue { const { td, onStart } = context; const rect = normalizeRect(td.getBoundingClientRect()); @@ -148,9 +148,9 @@ export function onDragStart(context: cellResizerContext, event: MouseEvent): cel * Exported for testing */ export function onDraggingHorizontal( - context: cellResizerContext, + context: CellResizerContext, event: MouseEvent, - initValue: cellResizerInitValue, + initValue: CellResizerInitValue, deltaX: number, deltaY: number ) { @@ -183,9 +183,9 @@ export function onDraggingHorizontal( * Exported for testing */ export function onDraggingVertical( - context: cellResizerContext, + context: CellResizerContext, event: MouseEvent, - initValue: cellResizerInitValue, + initValue: CellResizerInitValue, deltaX: number ) { const { table, isRTL } = context; @@ -231,7 +231,7 @@ export function onDraggingVertical( } } -function setHorizontalPosition(context: cellResizerContext, trigger: HTMLElement) { +function setHorizontalPosition(context: CellResizerContext, trigger: HTMLElement) { const { td } = context; const rect = normalizeRect(td.getBoundingClientRect()); if (rect) { @@ -243,7 +243,7 @@ function setHorizontalPosition(context: cellResizerContext, trigger: HTMLElement } } -function setVerticalPosition(context: cellResizerContext, trigger: HTMLElement) { +function setVerticalPosition(context: CellResizerContext, trigger: HTMLElement) { const { td, isRTL } = context; const rect = normalizeRect(td.getBoundingClientRect()); if (rect) { diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts index 86729a98162..8ee0b13b051 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts @@ -5,8 +5,8 @@ import { getCurrentTable } from './TableEditTestHelper'; import { getModelTable } from './tableData'; import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin'; import { - cellResizerContext, - cellResizerInitValue, + CellResizerContext, + CellResizerInitValue, onDragStart, onDraggingHorizontal, onDraggingVertical, @@ -62,7 +62,7 @@ describe('Cell Resizer tests', () => { const targetTd = (target as HTMLTableElement).rows[0].cells[0]; const onStartSpy = jasmine.createSpy('onStart'); - const context: cellResizerContext = { + const context: CellResizerContext = { editor: editor, td: targetTd as HTMLTableCellElement, table: target as HTMLTableElement, @@ -101,7 +101,7 @@ describe('Cell Resizer tests', () => { return; } - const initValue: cellResizerInitValue = { + const initValue: CellResizerInitValue = { cmTable: cmTable, anchorColumn: cellColumn, anchorRow: cellRow, @@ -112,7 +112,7 @@ describe('Cell Resizer tests', () => { const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn]; const onStartSpy = jasmine.createSpy('onStart'); - const context: cellResizerContext = { + const context: CellResizerContext = { editor: editor, td: targetTd as HTMLTableCellElement, table: target as HTMLTableElement, @@ -176,7 +176,7 @@ describe('Cell Resizer tests', () => { return; } - const initValue: cellResizerInitValue = { + const initValue: CellResizerInitValue = { cmTable: cmTable, anchorColumn: cellColumn, anchorRow: cellRow, @@ -187,7 +187,7 @@ describe('Cell Resizer tests', () => { const targetTd = (target as HTMLTableElement).rows[cellRow].cells[cellColumn]; const onStartSpy = jasmine.createSpy('onStart'); - const context: cellResizerContext = { + const context: CellResizerContext = { editor: editor, td: targetTd as HTMLTableCellElement, table: target as HTMLTableElement,