Skip to content

Commit dd59044

Browse files
crisbetojelbourn
authored andcommitted
fix(drag-drop): unable to drop into connected sibling that was scrolled into view (#16681)
In the scenario where two drop lists are connected and one of them is scrolled out of view, even if the user scrolls it into view, the list won't accept the dragged item until the user starts a new drag sequence. The issue comes from the fact that we only update the cached dimensions of the list in which we're currently dragging, but not the ones that it's connected to. These changes fix the issue by also updating the dimensions of all connected lists.
1 parent 3059e65 commit dd59044

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3927,6 +3927,50 @@ describe('CdkDrag', () => {
39273927
expect(itemEnterEvent).toEqual(expectedEvent);
39283928
}));
39293929

3930+
it('should be able to drop into a new container after scrolling into view', fakeAsync(() => {
3931+
const fixture = createComponent(ConnectedDropZones);
3932+
fixture.detectChanges();
3933+
3934+
// Make the page scrollable and scroll the items out of view.
3935+
const cleanup = makePageScrollable();
3936+
scrollTo(0, 4000);
3937+
dispatchFakeEvent(document, 'scroll');
3938+
fixture.detectChanges();
3939+
flush();
3940+
fixture.detectChanges();
3941+
3942+
const groups = fixture.componentInstance.groupedDragItems;
3943+
const item = groups[0][1];
3944+
3945+
// Start dragging and then scroll the elements back into view.
3946+
startDraggingViaMouse(fixture, item.element.nativeElement);
3947+
scrollTo(0, 0);
3948+
dispatchFakeEvent(document, 'scroll');
3949+
3950+
const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect();
3951+
dispatchMouseEvent(document, 'mousemove', targetRect.left + 1, targetRect.top + 1);
3952+
dispatchMouseEvent(document, 'mouseup', targetRect.left + 1, targetRect.top + 1);
3953+
fixture.detectChanges();
3954+
flush();
3955+
fixture.detectChanges();
3956+
3957+
expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1);
3958+
3959+
const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
3960+
3961+
expect(event).toEqual({
3962+
previousIndex: 1,
3963+
currentIndex: 3,
3964+
item,
3965+
container: fixture.componentInstance.dropInstances.toArray()[1],
3966+
previousContainer: fixture.componentInstance.dropInstances.first,
3967+
isPointerOverContainer: true,
3968+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
3969+
});
3970+
3971+
cleanup();
3972+
}));
3973+
39303974
});
39313975

39323976
describe('with nested drags', () => {

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,7 @@ export class DropListRef<T = any> {
254254

255255
// @breaking-change 9.0.0 Remove check for _viewportRuler once it's marked as a required param.
256256
if (this._viewportRuler) {
257-
this._viewportScrollPosition = this._viewportRuler.getViewportScrollPosition();
258-
this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe(() => {
259-
if (this.isDragging()) {
260-
const newPosition = this._viewportRuler!.getViewportScrollPosition();
261-
this._updateAfterScroll(this._viewportScrollPosition, newPosition.top, newPosition.left,
262-
this._clientRect);
263-
}
264-
});
257+
this._listenToScrollEvents();
265258
}
266259
}
267260

@@ -854,6 +847,7 @@ export class DropListRef<T = any> {
854847
if (!activeSiblings.has(sibling)) {
855848
activeSiblings.add(sibling);
856849
this._cacheOwnPosition();
850+
this._listenToScrollEvents();
857851
}
858852
}
859853

@@ -863,6 +857,24 @@ export class DropListRef<T = any> {
863857
*/
864858
_stopReceiving(sibling: DropListRef) {
865859
this._activeSiblings.delete(sibling);
860+
this._viewportScrollSubscription.unsubscribe();
861+
}
862+
863+
/**
864+
* Starts listening to scroll events on the viewport.
865+
* Used for updating the internal state of the list.
866+
*/
867+
private _listenToScrollEvents() {
868+
this._viewportScrollPosition = this._viewportRuler!.getViewportScrollPosition();
869+
this._viewportScrollSubscription = this._dragDropRegistry.scroll.subscribe(() => {
870+
if (this.isDragging()) {
871+
const newPosition = this._viewportRuler!.getViewportScrollPosition();
872+
this._updateAfterScroll(this._viewportScrollPosition, newPosition.top, newPosition.left,
873+
this._clientRect);
874+
} else if (this.isReceiving()) {
875+
this._cacheOwnPosition();
876+
}
877+
});
866878
}
867879
}
868880

0 commit comments

Comments
 (0)