From 95d089acc3000137991507223bb7dd05ccc977b0 Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Mon, 30 Oct 2023 10:05:15 -0600 Subject: [PATCH 1/4] init --- .../lib/plugins/TableResize/TableResize.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts index 063bcc8c98c..6681b99e419 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts @@ -1,5 +1,5 @@ import TableEditor from './editors/TableEditor'; -import { normalizeRect, safeInstanceOf } from 'roosterjs-editor-dom'; +import { contains, normalizeRect, safeInstanceOf } from 'roosterjs-editor-dom'; import { PluginEventType } from 'roosterjs-editor-types'; import type { CreateElementData, @@ -52,17 +52,18 @@ export default class TableResize implements EditorPlugin { this.editor = editor; this.onMouseMoveDisposer = this.editor.addDomEventHandler({ mousemove: this.onMouseMove, - mouseout: e => this.onMouseOut(e), }); + const scrollContainer = this.editor.getScrollContainer(); + scrollContainer.addEventListener('mouseout', this.onMouseOut); } - private onMouseOut = (ev: Event) => { + private onMouseOut = (ev: MouseEvent) => { + const scrollContainer = this.editor?.getScrollContainer(); if ( - isMouseEvent(ev) && safeInstanceOf(ev.relatedTarget, 'HTMLElement') && this.tableEditor && !this.tableEditor.isOwnedElement(ev.relatedTarget) && - !this.editor?.contains(ev.relatedTarget) + !contains(scrollContainer, ev.relatedTarget) ) { this.setTableEditor(null); } @@ -72,6 +73,8 @@ export default class TableResize implements EditorPlugin { * Dispose this plugin */ dispose() { + const scrollContainer = this.editor?.getScrollContainer(); + scrollContainer?.removeEventListener('mouseout', this.onMouseOut); this.onMouseMoveDisposer?.(); this.invalidateTableRects(); this.disposeTableEditor(); @@ -176,7 +179,3 @@ export default class TableResize implements EditorPlugin { } } } - -function isMouseEvent(e: Event): e is MouseEvent { - return !!(e as MouseEvent).pageX; -} From 37bf02e50f49e62f635ada58c98d7e561c431e7c Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Mon, 30 Oct 2023 11:21:15 -0600 Subject: [PATCH 2/4] init --- .../lib/plugins/TableResize/TableResize.ts | 12 +- .../test/TableResize/tableResizeTest.ts | 265 ++++++++++++++++++ 2 files changed, 271 insertions(+), 6 deletions(-) diff --git a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts index 6681b99e419..30a561f3761 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts @@ -57,13 +57,13 @@ export default class TableResize implements EditorPlugin { scrollContainer.addEventListener('mouseout', this.onMouseOut); } - private onMouseOut = (ev: MouseEvent) => { - const scrollContainer = this.editor?.getScrollContainer(); + private onMouseOut = ({ relatedTarget, currentTarget }: MouseEvent) => { if ( - safeInstanceOf(ev.relatedTarget, 'HTMLElement') && + safeInstanceOf(relatedTarget, 'HTMLElement') && + safeInstanceOf(currentTarget, 'HTMLElement') && this.tableEditor && - !this.tableEditor.isOwnedElement(ev.relatedTarget) && - !contains(scrollContainer, ev.relatedTarget) + !this.tableEditor.isOwnedElement(relatedTarget) && + !contains(currentTarget, relatedTarget) ) { this.setTableEditor(null); } @@ -132,7 +132,7 @@ export default class TableResize implements EditorPlugin { this.tableEditor?.onMouseMove(x, y); }; - private setTableEditor(table: HTMLTableElement | null, e?: MouseEvent) { + public setTableEditor(table: HTMLTableElement | null, e?: MouseEvent) { if (this.tableEditor && !this.tableEditor.isEditing() && table != this.tableEditor.table) { this.disposeTableEditor(); } diff --git a/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts b/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts index 3073b91896d..68bddf84e36 100644 --- a/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts +++ b/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts @@ -1,4 +1,6 @@ +import * as Contains from 'roosterjs-editor-dom/lib/utils/contains'; import * as TestHelper from 'roosterjs-editor-api/test/TestHelper'; +import { createElement } from 'roosterjs-editor-dom'; import { DEFAULT_TABLE, DEFAULT_TABLE_MERGED, EXCEL_TABLE, WORD_TABLE } from './tableData'; import { TableResize } from '../../lib/TableResize'; import { @@ -6,6 +8,7 @@ import { PluginEvent, PluginEventType, DOMEventHandlerFunction, + PluginKeyboardEvent, } from 'roosterjs-editor-types'; const VERTICAL_INSERTER = 'verticalInserter'; @@ -715,3 +718,265 @@ xdescribe('Table Resizer/Inserter tests', () => { expect(pluginName).toBe(expectedName); }); }); + +describe('TableResize', () => { + let editor: IEditor; + let plugin: TableResize; + const TEST_ID = 'inserterTest'; + + let mouseOutListener: undefined | ((this: HTMLElement, ev: MouseEvent) => any); + + beforeEach(() => { + editor = TestHelper.initEditor(TEST_ID); + plugin = new TableResize(); + + spyOn(editor, 'getScrollContainer').and.returnValue({ + addEventListener: ( + type: K, + listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, + options?: boolean | AddEventListenerOptions + ) => { + if (type == 'mouseout') { + mouseOutListener = listener as (this: HTMLElement, ev: MouseEvent) => any; + } + }, + removeEventListener: ( + type: K, + listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, + options?: boolean | EventListenerOptions + ) => { + if (type == 'mouseout') { + mouseOutListener = undefined; + } + }, + }); + plugin.initialize(editor); + }); + + afterEach(() => { + plugin.dispose(); + editor.dispose(); + TestHelper.removeElement(TEST_ID); + document.body = document.createElement('body'); + }); + + it('Dismiss table editor on mouse out', () => { + const ele = createElement( + { + tag: 'div', + children: [ + { + tag: 'div', + children: ['asd'], + }, + ], + }, + editor.getDocument() + ); + const table = createElement( + { + tag: 'table', + children: [ + { + tag: 'tr', + children: [ + { + tag: 'td', + children: ['Test'], + }, + ], + }, + ], + }, + editor.getDocument() + ) as HTMLTableElement; + editor.insertNode(table); + + spyOn(plugin, 'setTableEditor').and.callThrough(); + + plugin.setTableEditor(table); + + if (mouseOutListener) { + const boundedListener = mouseOutListener.bind(ele); + spyOn(Contains, 'default').and.returnValue(false); + boundedListener(({ + currentTarget: ele, + relatedTarget: ele, + })); + + expect(plugin.setTableEditor).toHaveBeenCalledWith(null); + } + }); + + it('Do not dismiss table editor on mouse out, related target is contained in scroll container', () => { + const ele = createElement( + { + tag: 'div', + children: [ + { + tag: 'div', + children: ['asd'], + }, + ], + }, + editor.getDocument() + ); + const table = createElement( + { + tag: 'table', + children: [ + { + tag: 'tr', + children: [ + { + tag: 'td', + children: ['Test'], + }, + ], + }, + ], + }, + editor.getDocument() + ) as HTMLTableElement; + editor.insertNode(table); + + spyOn(plugin, 'setTableEditor').and.callThrough(); + + plugin.setTableEditor(table); + + if (mouseOutListener) { + const boundedListener = mouseOutListener.bind(ele); + spyOn(Contains, 'default').and.returnValue(true); + boundedListener(({ + currentTarget: ele, + relatedTarget: ele, + })); + + expect(plugin.setTableEditor).not.toHaveBeenCalledWith(null); + } + }); + + it('Do not dismiss table editor on mouse out, table editor not', () => { + const ele = createElement( + { + tag: 'div', + children: [ + { + tag: 'div', + children: ['asd'], + }, + ], + }, + editor.getDocument() + ); + + spyOn(plugin, 'setTableEditor').and.callThrough(); + + if (mouseOutListener) { + const boundedListener = mouseOutListener.bind(ele); + spyOn(Contains, 'default').and.returnValue(false); + boundedListener(({ + currentTarget: ele, + relatedTarget: ele, + })); + + expect(plugin.setTableEditor).not.toHaveBeenCalledWith(null); + } + }); + + it('Do not dismiss table editor on mouse out, related target null', () => { + const ele = createElement( + { + tag: 'div', + children: [ + { + tag: 'div', + children: ['asd'], + }, + ], + }, + editor.getDocument() + ); + const table = createElement( + { + tag: 'table', + children: [ + { + tag: 'tr', + children: [ + { + tag: 'td', + children: ['Test'], + }, + ], + }, + ], + }, + editor.getDocument() + ) as HTMLTableElement; + editor.insertNode(table); + + spyOn(plugin, 'setTableEditor').and.callThrough(); + + plugin.setTableEditor(table); + + if (mouseOutListener) { + const boundedListener = mouseOutListener.bind(ele); + spyOn(Contains, 'default').and.returnValue(false); + boundedListener(({ + currentTarget: ele, + relatedTarget: null, + })); + + expect(plugin.setTableEditor).not.toHaveBeenCalledWith(null); + } + }); + + it('Do not dismiss table editor on mouse out, currentTarget null', () => { + const ele = createElement( + { + tag: 'div', + children: [ + { + tag: 'div', + children: ['asd'], + }, + ], + }, + editor.getDocument() + ); + const table = createElement( + { + tag: 'table', + children: [ + { + tag: 'tr', + children: [ + { + tag: 'td', + children: ['Test'], + }, + ], + }, + ], + }, + editor.getDocument() + ) as HTMLTableElement; + editor.insertNode(table); + + spyOn(plugin, 'setTableEditor').and.callThrough(); + + plugin.setTableEditor(table); + + if (mouseOutListener) { + const boundedListener = mouseOutListener.bind(ele); + spyOn(Contains, 'default').and.returnValue(false); + boundedListener(({ + currentTarget: null, + relatedTarget: ele, + })); + + expect(plugin.setTableEditor).not.toHaveBeenCalledWith(null); + } + }); +}); From 4dcc1736c518de511c95d1f1264c95a4be22f7f6 Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Mon, 30 Oct 2023 11:47:13 -0600 Subject: [PATCH 3/4] fix build --- .../test/TableResize/tableResizeTest.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts b/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts index 68bddf84e36..7a68f29e871 100644 --- a/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts +++ b/packages/roosterjs-editor-plugins/test/TableResize/tableResizeTest.ts @@ -8,7 +8,6 @@ import { PluginEvent, PluginEventType, DOMEventHandlerFunction, - PluginKeyboardEvent, } from 'roosterjs-editor-types'; const VERTICAL_INSERTER = 'verticalInserter'; @@ -730,7 +729,7 @@ describe('TableResize', () => { editor = TestHelper.initEditor(TEST_ID); plugin = new TableResize(); - spyOn(editor, 'getScrollContainer').and.returnValue({ + spyOn(editor, 'getScrollContainer').and.returnValue(({ addEventListener: ( type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, @@ -749,7 +748,7 @@ describe('TableResize', () => { mouseOutListener = undefined; } }, - }); + })); plugin.initialize(editor); }); From 780230b5da4ce8fb0aa4f9fcf5ef8c75f45dfb0e Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Mon, 30 Oct 2023 13:36:37 -0600 Subject: [PATCH 4/4] Add comment --- .../lib/plugins/TableResize/TableResize.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts index 30a561f3761..dc9cea12d9b 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts @@ -132,7 +132,12 @@ export default class TableResize implements EditorPlugin { this.tableEditor?.onMouseMove(x, y); }; - public setTableEditor(table: HTMLTableElement | null, e?: MouseEvent) { + /** + * @internal Public only for unit test + * @param table Table to use when setting the Editors + * @param event (Optional) Mouse event + */ + public setTableEditor(table: HTMLTableElement | null, event?: MouseEvent) { if (this.tableEditor && !this.tableEditor.isEditing() && table != this.tableEditor.table) { this.disposeTableEditor(); } @@ -148,7 +153,7 @@ export default class TableResize implements EditorPlugin { this.invalidateTableRects, this.onShowHelperElement, safeInstanceOf(container, 'HTMLElement') ? container : undefined, - e?.currentTarget + event?.currentTarget ); } }