Skip to content

Commit 5fa10c2

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 a258400 commit 5fa10c2

File tree

4 files changed

+26
-7
lines changed

4 files changed

+26
-7
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,22 @@ describe('CdkDrag', () => {
17401740
}));
17411741
}));
17421742

1743+
it('should sort correctly if the <html> node has been offset', fakeAsync(() => {
1744+
const documentElement = document.documentElement!;
1745+
const fixture = createComponent(DraggableInDropZone);
1746+
fixture.detectChanges();
1747+
1748+
documentElement.style.position = 'absolute';
1749+
documentElement.style.top = '-100px';
1750+
1751+
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
1752+
return item.element.nativeElement;
1753+
}));
1754+
1755+
documentElement.style.position = '';
1756+
documentElement.style.top = '';
1757+
}));
1758+
17431759
it('should move the placeholder as an item is being sorted down on a scrolled page',
17441760
fakeAsync(() => {
17451761
const fixture = createComponent(DraggableInDropZone);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export class DragDrop {
2727
constructor(
2828
@Inject(DOCUMENT) private _document: any,
2929
private _ngZone: NgZone,
30-
private _viewportRuler: ViewportRuler,
30+
// @breaking-change 8.0.0 `_viewportRuler` parameter isn't being used. To be removed.
31+
_viewportRuler: ViewportRuler,
3132
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>) {}
3233

3334
/**
@@ -38,8 +39,7 @@ export class DragDrop {
3839
createDrag<T = any>(element: ElementRef<HTMLElement> | HTMLElement,
3940
config: DragRefConfig = DEFAULT_CONFIG): DragRef<T> {
4041

41-
return new DragRef<T>(element, config, this._document, this._ngZone, this._viewportRuler,
42-
this._dragDropRegistry);
42+
return new DragRef<T>(element, config, this._document, this._ngZone, this._dragDropRegistry);
4343
}
4444

4545
/**

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 {Direction} from '@angular/cdk/bidi';
1211
import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
1312
import {coerceBooleanProperty, coerceElement} from '@angular/cdk/coercion';
@@ -286,7 +285,6 @@ export class DragRef<T = any> {
286285
private _config: DragRefConfig,
287286
private _document: Document,
288287
private _ngZone: NgZone,
289-
private _viewportRuler: ViewportRuler,
290288
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>) {
291289

292290
this.withRootElement(element);
@@ -691,7 +689,12 @@ export class DragRef<T = any> {
691689
this._initialContainer = this._dropContainer!;
692690
this._pointerMoveSubscription = this._dragDropRegistry.pointerMove.subscribe(this._pointerMove);
693691
this._pointerUpSubscription = this._dragDropRegistry.pointerUp.subscribe(this._pointerUp);
694-
this._scrollPosition = this._viewportRuler.getViewportScrollPosition();
692+
693+
// Note that we use the scrollX and scrollY directly, instead of going through the
694+
// ViewportRuler, because the first value the ruler looks at is the top/left offset of the
695+
// `document.documentElement` which works for most cases, but breaks if the element
696+
// is offset by something like the `BlockScrollStrategy`.
697+
this._scrollPosition = {top: window.scrollY, left: window.scrollX};
695698

696699
if (this._boundaryElement) {
697700
this._boundaryRect = this._boundaryElement.getBoundingClientRect();

tools/public_api_guard/cdk/drag-drop.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export declare class DragRef<T = any> {
253253
started: Subject<{
254254
source: DragRef<any>;
255255
}>;
256-
constructor(element: ElementRef<HTMLElement> | HTMLElement, _config: DragRefConfig, _document: Document, _ngZone: NgZone, _viewportRuler: ViewportRuler, _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>);
256+
constructor(element: ElementRef<HTMLElement> | HTMLElement, _config: DragRefConfig, _document: Document, _ngZone: NgZone, _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>);
257257
_withDropContainer(container: DropListRef): void;
258258
disableHandle(handle: HTMLElement): void;
259259
dispose(): void;

0 commit comments

Comments
 (0)