diff --git a/packages/ckeditor5-utils/src/dom/scroll.ts b/packages/ckeditor5-utils/src/dom/scroll.ts index 73b427cdbb7..1745ad95629 100644 --- a/packages/ckeditor5-utils/src/dom/scroll.ts +++ b/packages/ckeditor5-utils/src/dom/scroll.ts @@ -151,14 +151,17 @@ export function scrollViewportToShowTarget new Rect( target ), - ancestorOffset + ancestorOffset, + limiterElement } ); } @@ -291,6 +294,7 @@ function scrollWindowToShowRect>( * anyway. * @param options.forceScroll When set `true`, the `rect` will be aligned to the top of scrollable ancestors * whether it is already visible or not. This option will only work when `alignToTop` is `true` + * @param options.limiterElement The outermost ancestor that should be scrolled. Defaults to the `` element. */ function scrollAncestorsToShowRect>( { @@ -298,20 +302,24 @@ function scrollAncestorsToShowRect>( getRect, alignToTop, forceScroll, - ancestorOffset = 0 + ancestorOffset = 0, + limiterElement }: { readonly parent: HTMLElement; readonly getRect: () => Rect; readonly alignToTop?: T; readonly forceScroll?: U; readonly ancestorOffset?: number; + readonly limiterElement?: HTMLElement; } ): void { const parentWindow = getWindow( parent ); const forceScrollToTop = alignToTop && forceScroll; let parentRect: Rect, targetRect: Rect, targetFitsInTarget: boolean; - while ( parent != parentWindow.document.body ) { + const limiter = limiterElement || parentWindow.document.body; + + while ( parent != limiter ) { targetRect = getRect(); parentRect = new Rect( parent ).excludeScrollbarsAndBorders(); targetFitsInTarget = parentRect.contains( targetRect ); diff --git a/packages/ckeditor5-utils/tests/dom/scroll.js b/packages/ckeditor5-utils/tests/dom/scroll.js index dd283a1745e..788a9f72b49 100644 --- a/packages/ckeditor5-utils/tests/dom/scroll.js +++ b/packages/ckeditor5-utils/tests/dom/scroll.js @@ -115,6 +115,15 @@ describe( 'scrollAncestorsToShowTarget()', () => { assertScrollPosition( document.body, { scrollLeft: 1000, scrollTop: 1000 } ); } ); + it( 'should not change the scroll of the ancestors of the given limiter', () => { + stubGeometry( testUtils, target, { top: 25, right: 75, bottom: 75, left: 25, width: 50, height: 50 } ); + + scrollAncestorsToShowTarget( target, 0, firstAncestor ); + + assertScrollPosition( firstAncestor, { scrollTop: 100, scrollLeft: 100 } ); + assertScrollPosition( secondAncestor, { scrollTop: 100, scrollLeft: 100 } ); + } ); + it( 'should set #scrollTop and #scrollLeft of the ancestor to show the target (above)', () => { stubGeometry( testUtils, target, { top: -100, right: 75, bottom: 0, left: 25, width: 50, height: 100 } ); @@ -171,6 +180,15 @@ describe( 'scrollAncestorsToShowTarget()', () => { assertScrollPosition( document.body, { scrollLeft: 1000, scrollTop: 1000 } ); } ); + it( 'should not change the scroll of the ancestors of the given limiter', () => { + stubGeometry( testUtils, target, { top: 25, right: 75, bottom: 75, left: 25, width: 50, height: 50 } ); + + scrollAncestorsToShowTarget( target, 20, firstAncestor ); + + assertScrollPosition( firstAncestor, { scrollTop: 100, scrollLeft: 100 } ); + assertScrollPosition( secondAncestor, { scrollTop: 100, scrollLeft: 100 } ); + } ); + it( 'should set #scrollTop and #scrollLeft of the ancestor to show the target (above)', () => { stubGeometry( testUtils, target, { top: -100, right: 75, bottom: 0, left: 25, width: 50, height: 100 } );