Skip to content

Commit 76994f7

Browse files
crisbetoVivian Hu
authored andcommitted
fix(drag-drop): unable to move item into connected container by passing through another container (#14651)
Fixes not being able to move an item from one container into another by passing it through an intermediate container that isn't connected to the final one. The issue comes from the fact that the way things are set up at the moment, the container from which the item started the sequence knows which containers it can go into, however all that knowledge is reset once the item enters into a different container. These changes rework the logic to have the individual containers know whether the item can enter into them and have the source container "ask" each of its siblings whether the item can enter. Fixes #14645.
1 parent 4d4b074 commit 76994f7

File tree

3 files changed

+161
-103
lines changed

3 files changed

+161
-103
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,6 +2736,63 @@ describe('CdkDrag', () => {
27362736
'Expected new container not to have the receiving class after entering.');
27372737
}));
27382738

2739+
it('should be able to move the item over an intermediate container before ' +
2740+
'dropping it into the final one', fakeAsync(() => {
2741+
const fixture = createComponent(ConnectedDropZones);
2742+
fixture.detectChanges();
2743+
2744+
const dropInstances = fixture.componentInstance.dropInstances.toArray();
2745+
dropInstances[0].connectedTo = [dropInstances[1], dropInstances[2]];
2746+
dropInstances[1].connectedTo = [];
2747+
dropInstances[2].connectedTo = [];
2748+
fixture.detectChanges();
2749+
2750+
const groups = fixture.componentInstance.groupedDragItems;
2751+
const dropZones = dropInstances.map(d => d.element.nativeElement);
2752+
const item = groups[0][1];
2753+
const intermediateRect = dropZones[1].getBoundingClientRect();
2754+
const finalRect = dropZones[2].getBoundingClientRect();
2755+
2756+
startDraggingViaMouse(fixture, item.element.nativeElement);
2757+
2758+
const placeholder = dropZones[0].querySelector('.cdk-drag-placeholder')!;
2759+
2760+
expect(placeholder).toBeTruthy();
2761+
expect(dropZones[0].contains(placeholder))
2762+
.toBe(true, 'Expected placeholder to be inside the first container.');
2763+
2764+
dispatchMouseEvent(document, 'mousemove',
2765+
intermediateRect.left + 1, intermediateRect.top + 1);
2766+
fixture.detectChanges();
2767+
2768+
expect(dropZones[1].contains(placeholder))
2769+
.toBe(true, 'Expected placeholder to be inside second container.');
2770+
2771+
dispatchMouseEvent(document, 'mousemove', finalRect.left + 1, finalRect.top + 1);
2772+
fixture.detectChanges();
2773+
2774+
expect(dropZones[2].contains(placeholder))
2775+
.toBe(true, 'Expected placeholder to be inside third container.');
2776+
2777+
dispatchMouseEvent(document, 'mouseup');
2778+
fixture.detectChanges();
2779+
flush();
2780+
fixture.detectChanges();
2781+
2782+
const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
2783+
2784+
expect(event).toBeTruthy();
2785+
expect(event).toEqual(jasmine.objectContaining({
2786+
previousIndex: 1,
2787+
currentIndex: 0,
2788+
item: groups[0][1],
2789+
container: dropInstances[2],
2790+
previousContainer: dropInstances[0],
2791+
isPointerOverContainer: false
2792+
}));
2793+
2794+
}));
2795+
27392796
});
27402797

27412798
});
@@ -3025,6 +3082,14 @@ class DraggableInDropZoneWithCustomPlaceholder {
30253082
(cdkDropListDropped)="droppedSpy($event)">
30263083
<div [cdkDragData]="item" *ngFor="let item of done" cdkDrag>{{item}}</div>
30273084
</div>
3085+
3086+
<div
3087+
cdkDropList
3088+
#extraZone="cdkDropList"
3089+
[cdkDropListData]="extra"
3090+
(cdkDropListDropped)="droppedSpy($event)">
3091+
<div [cdkDragData]="item" *ngFor="let item of extra" cdkDrag>{{item}}</div>
3092+
</div>
30283093
`
30293094
})
30303095
class ConnectedDropZones implements AfterViewInit {
@@ -3034,6 +3099,7 @@ class ConnectedDropZones implements AfterViewInit {
30343099
groupedDragItems: CdkDrag[][] = [];
30353100
todo = ['Zero', 'One', 'Two', 'Three'];
30363101
done = ['Four', 'Five', 'Six'];
3102+
extra = [];
30373103
droppedSpy = jasmine.createSpy('dropped spy');
30383104

30393105
ngAfterViewInit() {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,8 @@ export class DragRef<T = any> {
704704
*/
705705
private _updateActiveDropContainer({x, y}: Point) {
706706
// Drop container that draggable has been moved into.
707-
let newContainer = this.dropContainer!._getSiblingContainerFromPosition(this, x, y);
707+
let newContainer = this.dropContainer!._getSiblingContainerFromPosition(this, x, y) ||
708+
this._initialContainer._getSiblingContainerFromPosition(this, x, y);
708709

709710
// If we couldn't find a new container to move the item into, and the item has left it's
710711
// initial container, check whether the it's over the initial container. This handles the
@@ -715,7 +716,7 @@ export class DragRef<T = any> {
715716
newContainer = this._initialContainer;
716717
}
717718

718-
if (newContainer) {
719+
if (newContainer && newContainer !== this.dropContainer) {
719720
this._ngZone.run(() => {
720721
// Notify the old container that the item has left.
721722
this.exited.next({item: this, container: this.dropContainer!});

0 commit comments

Comments
 (0)