Skip to content

Commit

Permalink
Prevent ScrollTop to be lost when the focus is done to the editor (#2564
Browse files Browse the repository at this point in the history
)

* init

* init

---------

Co-authored-by: Julia Roldi <87443959+juliaroldi@users.noreply.github.com>
  • Loading branch information
BryanValverdeU and juliaroldi authored Apr 5, 2024
1 parent 31037a1 commit 53f1f4d
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
private disposer: (() => void) | null = null;
private isSafari = false;
private isMac = false;
private scrollTopCache: number = 0;

constructor(options: EditorOptions) {
this.state = {
Expand Down Expand Up @@ -113,6 +114,12 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
case 'contentChanged':
this.state.tableSelection = null;
break;

case 'scroll':
if (!this.editor.hasFocus()) {
this.scrollTopCache = event.scrollContainer.scrollTop;
}
break;
}
}

Expand Down Expand Up @@ -521,11 +528,21 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
// Editor is focused, now we can get live selection. So no need to keep a selection if the selection type is range.
this.state.selection = null;
}

if (this.scrollTopCache && this.editor) {
const sc = this.editor.getScrollContainer();
sc.scrollTop = this.scrollTopCache;
this.scrollTopCache = 0;
}
};

private onBlur = () => {
if (!this.state.selection && this.editor) {
this.state.selection = this.editor.getDOMSelection();
if (this.editor) {
if (!this.state.selection) {
this.state.selection = this.editor.getDOMSelection();
}
const sc = this.editor.getScrollContainer();
this.scrollTopCache = sc.scrollTop;
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => {
let setDOMSelectionSpy: jasmine.Spy;
let removeEventListenerSpy: jasmine.Spy;
let addEventListenerSpy: jasmine.Spy;
let getScrollContainerSpy: jasmine.Spy;

let editor: IEditor;

Expand All @@ -100,6 +101,7 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => {
addEventListener: addEventListenerSpy,
});
setDOMSelectionSpy = jasmine.createSpy('setDOMSelection');
getScrollContainerSpy = jasmine.createSpy('getScrollContainer');

plugin = createSelectionPlugin({});

Expand All @@ -113,6 +115,7 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => {
},
getElementAtCursor: getElementAtCursorSpy,
setDOMSelection: setDOMSelectionSpy,
getScrollContainer: getScrollContainerSpy,
});
plugin.initialize(editor);
});
Expand Down Expand Up @@ -152,6 +155,109 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => {
tableSelection: null,
});
});

it('Trigger onFocusEvent, use cached scrollTop', () => {
const scMock: any = {};
const scrollTop = 5;
getScrollContainerSpy.and.returnValue(scMock);
(plugin as any).scrollTopCache = scrollTop;

eventMap.focus.beforeDispatch();

expect(scMock.scrollTop).toEqual(scrollTop);
expect((plugin as any).scrollTopCache).toEqual(0);
});

it('onBlur cache scrollTop', () => {
const scrollTop = 5;
const scMock: any = { scrollTop };
getScrollContainerSpy.and.returnValue(scMock);
plugin.getState().selection = <any>true;

eventMap.blur.beforeDispatch();

expect((plugin as any).scrollTopCache).toEqual(scrollTop);
});
});

describe('SelectionPlugin scroll event ', () => {
let plugin: PluginWithState<SelectionPluginState>;
let triggerEvent: jasmine.Spy;
let getElementAtCursorSpy: jasmine.Spy;
let getDocumentSpy: jasmine.Spy;
let setDOMSelectionSpy: jasmine.Spy;
let removeEventListenerSpy: jasmine.Spy;
let addEventListenerSpy: jasmine.Spy;
let getScrollContainerSpy: jasmine.Spy;
let hasFocusSpy: jasmine.Spy;

let editor: IEditor;

beforeEach(() => {
triggerEvent = jasmine.createSpy('triggerEvent');
getElementAtCursorSpy = jasmine.createSpy('getElementAtCursor');
removeEventListenerSpy = jasmine.createSpy('removeEventListener');
addEventListenerSpy = jasmine.createSpy('addEventListener');
getDocumentSpy = jasmine.createSpy('getDocument').and.returnValue({
removeEventListener: removeEventListenerSpy,
addEventListener: addEventListenerSpy,
});
setDOMSelectionSpy = jasmine.createSpy('setDOMSelection');
getScrollContainerSpy = jasmine.createSpy('getScrollContainer');
hasFocusSpy = jasmine.createSpy('hasFocus');

plugin = createSelectionPlugin({});

editor = <IEditor>(<any>{
getDocument: getDocumentSpy,
triggerEvent,
getEnvironment: () => ({}),
attachDomEvent: () => {
return jasmine.createSpy('disposer');
},
getElementAtCursor: getElementAtCursorSpy,
setDOMSelection: setDOMSelectionSpy,
getScrollContainer: getScrollContainerSpy,
hasFocus: hasFocusSpy,
});
plugin.initialize(editor);
});

afterEach(() => {
plugin.dispose();
});

it('Cache scrollTop', () => {
hasFocusSpy.and.returnValue(false);
const scrollTop = 5;
const scMock: any = { scrollTop };
getScrollContainerSpy.and.returnValue(scMock);
(plugin as any).scrollTopCache = undefined;

plugin.onPluginEvent?.({
eventType: 'scroll',
rawEvent: <any>{},
scrollContainer: scMock,
});

expect((plugin as any).scrollTopCache).toEqual(scrollTop);
});

it('Do not cache scrollTop', () => {
hasFocusSpy.and.returnValue(true);
const scrollTop = 5;
const scMock: any = { scrollTop };
getScrollContainerSpy.and.returnValue(scMock);
(plugin as any).scrollTopCache = undefined;

plugin.onPluginEvent?.({
eventType: 'scroll',
rawEvent: <any>{},
scrollContainer: scMock,
});

expect((plugin as any).scrollTopCache).toEqual(undefined);
});
});

describe('SelectionPlugin handle image selection', () => {
Expand Down

0 comments on commit 53f1f4d

Please sign in to comment.