diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index e20ad712d793..e42ad447f117 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -3927,6 +3927,50 @@ describe('CdkDrag', () => { expect(itemEnterEvent).toEqual(expectedEvent); })); + it('should be able to drop into a new container after scrolling into view', fakeAsync(() => { + const fixture = createComponent(ConnectedDropZones); + fixture.detectChanges(); + + // Make the page scrollable and scroll the items out of view. + const cleanup = makePageScrollable(); + scrollTo(0, 4000); + dispatchFakeEvent(document, 'scroll'); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + + const groups = fixture.componentInstance.groupedDragItems; + const item = groups[0][1]; + + // Start dragging and then scroll the elements back into view. + startDraggingViaMouse(fixture, item.element.nativeElement); + scrollTo(0, 0); + dispatchFakeEvent(document, 'scroll'); + + const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect(); + dispatchMouseEvent(document, 'mousemove', targetRect.left + 1, targetRect.top + 1); + dispatchMouseEvent(document, 'mouseup', targetRect.left + 1, targetRect.top + 1); + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + + expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1); + + const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0]; + + expect(event).toEqual({ + previousIndex: 1, + currentIndex: 3, + item, + container: fixture.componentInstance.dropInstances.toArray()[1], + previousContainer: fixture.componentInstance.dropInstances.first, + isPointerOverContainer: true, + distance: {x: jasmine.any(Number), y: jasmine.any(Number)} + }); + + cleanup(); + })); + }); describe('with nested drags', () => { diff --git a/src/cdk/drag-drop/drop-list-ref.ts b/src/cdk/drag-drop/drop-list-ref.ts index 21b9cf2760ff..b5b5d49f72a5 100644 --- a/src/cdk/drag-drop/drop-list-ref.ts +++ b/src/cdk/drag-drop/drop-list-ref.ts @@ -254,14 +254,7 @@ export class DropListRef { // @breaking-change 9.0.0 Remove check for _viewportRuler once it's marked as a required param. if (this._viewportRuler) { - this._viewportScrollPosition = this._viewportRuler.getViewportScrollPosition(); - this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe(() => { - if (this.isDragging()) { - const newPosition = this._viewportRuler!.getViewportScrollPosition(); - this._updateAfterScroll(this._viewportScrollPosition, newPosition.top, newPosition.left, - this._clientRect); - } - }); + this._listenToScrollEvents(); } } @@ -854,6 +847,7 @@ export class DropListRef { if (!activeSiblings.has(sibling)) { activeSiblings.add(sibling); this._cacheOwnPosition(); + this._listenToScrollEvents(); } } @@ -863,6 +857,24 @@ export class DropListRef { */ _stopReceiving(sibling: DropListRef) { this._activeSiblings.delete(sibling); + this._viewportScrollSubscription.unsubscribe(); + } + + /** + * Starts listening to scroll events on the viewport. + * Used for updating the internal state of the list. + */ + private _listenToScrollEvents() { + this._viewportScrollPosition = this._viewportRuler!.getViewportScrollPosition(); + this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe(() => { + if (this.isDragging()) { + const newPosition = this._viewportRuler!.getViewportScrollPosition(); + this._updateAfterScroll(this._viewportScrollPosition, newPosition.top, newPosition.left, + this._clientRect); + } else if (this.isReceiving()) { + this._cacheOwnPosition(); + } + }); } }