Skip to content

Commit c1c1a4b

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 f9ebb26 commit c1c1a4b

File tree

4 files changed

+25
-7
lines changed

4 files changed

+25
-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
@@ -2028,6 +2028,22 @@ describe('CdkDrag', () => {
20282028
}));
20292029
}));
20302030

2031+
it('should sort correctly if the <html> node has been offset', fakeAsync(() => {
2032+
const documentElement = document.documentElement!;
2033+
const fixture = createComponent(DraggableInDropZone);
2034+
fixture.detectChanges();
2035+
2036+
documentElement.style.position = 'absolute';
2037+
documentElement.style.top = '-100px';
2038+
2039+
assertDownwardSorting(fixture, fixture.componentInstance.dragItems.map(item => {
2040+
return item.element.nativeElement;
2041+
}));
2042+
2043+
documentElement.style.position = '';
2044+
documentElement.style.top = '';
2045+
}));
2046+
20312047
it('should move the placeholder as an item is being sorted down on a scrolled page',
20322048
fakeAsync(() => {
20332049
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: 5 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';
@@ -278,7 +277,6 @@ export class DragRef<T = any> {
278277
private _config: DragRefConfig,
279278
private _document: Document,
280279
private _ngZone: NgZone,
281-
private _viewportRuler: ViewportRuler,
282280
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>) {
283281

284282
this.withRootElement(element);
@@ -726,7 +724,11 @@ export class DragRef<T = any> {
726724
this._pointerMoveSubscription = this._dragDropRegistry.pointerMove.subscribe(this._pointerMove);
727725
this._pointerUpSubscription = this._dragDropRegistry.pointerUp.subscribe(this._pointerUp);
728726
this._scrollSubscription = this._dragDropRegistry.scroll.pipe(startWith(null)).subscribe(() => {
729-
this._scrollPosition = this._viewportRuler.getViewportScrollPosition();
727+
// Note that we use the scrollX and scrollY directly, instead of going through the
728+
// ViewportRuler, because the first value the ruler looks at is the top/left offset of the
729+
// `document.documentElement` which works for most cases, but breaks if the element
730+
// is offset by something like the `BlockScrollStrategy`.
731+
this._scrollPosition = {top: window.scrollY, left: window.scrollX};
730732
});
731733

732734
if (this._boundaryElement) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ export declare class DragRef<T = any> {
273273
started: Subject<{
274274
source: DragRef<any>;
275275
}>;
276-
constructor(element: ElementRef<HTMLElement> | HTMLElement, _config: DragRefConfig, _document: Document, _ngZone: NgZone, _viewportRuler: ViewportRuler, _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>);
276+
constructor(element: ElementRef<HTMLElement> | HTMLElement, _config: DragRefConfig, _document: Document, _ngZone: NgZone, _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>);
277277
_sortFromLastPointerPosition(): void;
278278
_withDropContainer(container: DropListRef): void;
279279
disableHandle(handle: HTMLElement): void;

0 commit comments

Comments
 (0)