Skip to content

Commit ce1bc90

Browse files
committed
fix(drag-drop): incorrectly sorting element inside dialog with blocked scrolling
We use the `ViewportRuler` to determine how much a page has been scrolled in order to compensate for it in some of the `ClientRect`-related calculations. This breaks down when the drop list is inside a dialog with blocked scrolling, because scroll blocking works by offsetting the `html` node which in turn throws off the `ViewportRuler`. These changes switches to reading the values off the `window` which won't be thrown off. Fixes #14280.
1 parent a9e8776 commit ce1bc90

File tree

3 files changed

+26
-5
lines changed

3 files changed

+26
-5
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,22 @@ describe('CdkDrag', () => {
14481448
}));
14491449
}));
14501450

1451+
it('should sort correctly if the <html> node has been offset', fakeAsync(() => {
1452+
const documentElement = document.documentElement!;
1453+
const fixture = createComponent(DraggableInDropZone);
1454+
fixture.detectChanges();
1455+
1456+
documentElement.style.position = 'absolute';
1457+
documentElement.style.top = '-100px';
1458+
1459+
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
1460+
return item.element.nativeElement;
1461+
}));
1462+
1463+
documentElement.style.position = 'absolute';
1464+
documentElement.style.top = '';
1465+
}));
1466+
14511467
it('should move the placeholder as an item is being sorted down on a scrolled page',
14521468
fakeAsync(() => {
14531469
const fixture = createComponent(DraggableInDropZone);

src/cdk/drag-drop/directives/drag.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,15 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
168168
@Inject(DOCUMENT) private _document: any,
169169
private _ngZone: NgZone,
170170
private _viewContainerRef: ViewContainerRef,
171-
private _viewportRuler: ViewportRuler,
171+
172+
// @breaking-change 8.0.0 `_viewportRuler` argument to be removed as it isn't being used.
173+
_viewportRuler: ViewportRuler,
172174
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>,
173175
@Inject(CDK_DRAG_CONFIG) private _config: DragRefConfig,
174176
@Optional() private _dir: Directionality) {
175177

176178
const ref = this._dragRef = new DragRef(element, this._document, this._ngZone,
177-
this._viewContainerRef, this._viewportRuler, this._dragDropRegistry,
179+
this._viewContainerRef, this._dragDropRegistry,
178180
this._config, this.dropContainer ? this.dropContainer._dropListRef : undefined,
179181
this._dir);
180182
ref.data = this;

src/cdk/drag-drop/drag-ref.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {EmbeddedViewRef, ElementRef, NgZone, ViewContainerRef, TemplateRef} from '@angular/core';
10-
import {ViewportRuler} from '@angular/cdk/scrolling';
1110
import {Directionality} from '@angular/cdk/bidi';
1211
import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
1312
import {coerceBooleanProperty} from '@angular/cdk/coercion';
@@ -267,7 +266,6 @@ export class DragRef<T = any> {
267266
private _document: Document,
268267
private _ngZone: NgZone,
269268
private _viewContainerRef: ViewContainerRef,
270-
private _viewportRuler: ViewportRuler,
271269
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>,
272270
private _config: DragRefConfig,
273271
/** Droppable container that the draggable is a part of. */
@@ -630,7 +628,12 @@ export class DragRef<T = any> {
630628
this._initialContainer = this.dropContainer!;
631629
this._pointerMoveSubscription = this._dragDropRegistry.pointerMove.subscribe(this._pointerMove);
632630
this._pointerUpSubscription = this._dragDropRegistry.pointerUp.subscribe(this._pointerUp);
633-
this._scrollPosition = this._viewportRuler.getViewportScrollPosition();
631+
632+
// Note that we use the scrollX and scrollY directly, instead of going through the
633+
// ViewportRuler, because the first value the ruler looks at is the top/left offset of the
634+
// `document.documentElement` which works for most cases, but breaks if the element
635+
// is offset by something like the `BlockScrollStrategy`.
636+
this._scrollPosition = {top: window.scrollY, left: window.scrollX};
634637

635638
if (this._boundaryElement) {
636639
this._boundaryRect = this._boundaryElement.getBoundingClientRect();

0 commit comments

Comments
 (0)