Skip to content

fix(drag-drop): not picking up handle that isn't a direct descendant #13360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/cdk/drag-drop/drag-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, ElementRef} from '@angular/core';
import {Directive, ElementRef, Inject, Optional} from '@angular/core';
import {CDK_DRAG_PARENT} from './drag-parent';
import {toggleNativeDragInteractions} from './drag-styling';

/** Handle that can be used to drag and CdkDrag instance. */
Expand All @@ -17,7 +18,14 @@ import {toggleNativeDragInteractions} from './drag-styling';
}
})
export class CdkDragHandle {
constructor(public element: ElementRef<HTMLElement>) {
/** Closest parent draggable instance. */
_parentDrag: {} | undefined;

constructor(
public element: ElementRef<HTMLElement>,
@Inject(CDK_DRAG_PARENT) @Optional() parentDrag?: any) {

this._parentDrag = parentDrag;
toggleNativeDragInteractions(element.nativeElement, false);
}
}
17 changes: 17 additions & 0 deletions src/cdk/drag-drop/drag-parent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {InjectionToken} from '@angular/core';

/**
* Injection token that can be used for a `CdkDrag` to provide itself as a parent to the
* drag-specific child directive (`CdkDragHandle`, `CdkDragPreview` etc.). Used primarily
* to avoid circular imports.
* @docs-private
*/
export const CDK_DRAG_PARENT = new InjectionToken<{}>('CDK_DRAG_PARENT');
49 changes: 48 additions & 1 deletion src/cdk/drag-drop/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('CdkDrag', () => {
ComponentFixture<T> {
TestBed.configureTestingModule({
imports: [DragDropModule],
declarations: [componentType],
declarations: [componentType, PassthroughComponent],
providers: [
{
provide: CDK_DRAG_CONFIG,
Expand Down Expand Up @@ -442,6 +442,23 @@ describe('CdkDrag', () => {
expect(dragElement.style.transform).toBe('translate3d(150px, 300px, 0px)');
}));

it('should be able to drag with a handle that is not a direct descendant', fakeAsync(() => {
const fixture = createComponent(StandaloneDraggableWithIndirectHandle);
fixture.detectChanges();
const dragElement = fixture.componentInstance.dragElement.nativeElement;
const handle = fixture.componentInstance.handleElement.nativeElement;

expect(dragElement.style.transform).toBeFalsy();
dragElementViaMouse(fixture, dragElement, 50, 100);

expect(dragElement.style.transform)
.toBeFalsy('Expected not to be able to drag the element by itself.');

dragElementViaMouse(fixture, handle, 50, 100);
expect(dragElement.style.transform)
.toBe('translate3d(50px, 100px, 0px)', 'Expected to drag the element by its handle.');
}));

});

describe('in a drop container', () => {
Expand Down Expand Up @@ -1700,6 +1717,26 @@ class StandaloneDraggableWithDelayedHandle {
showHandle = false;
}

@Component({
template: `
<div #dragElement cdkDrag
style="width: 100px; height: 100px; background: red; position: relative">

<passthrough-component>
<div
#handleElement
cdkDragHandle
style="width: 10px; height: 10px; background: green;"></div>
</passthrough-component>
</div>
`
})
class StandaloneDraggableWithIndirectHandle {
@ViewChild('dragElement') dragElement: ElementRef<HTMLElement>;
@ViewChild('handleElement') handleElement: ElementRef<HTMLElement>;
}


@Component({
encapsulation: ViewEncapsulation.None,
styles: [`
Expand Down Expand Up @@ -1940,6 +1977,16 @@ class ConnectedDropZonesWithSingleItems {
droppedSpy = jasmine.createSpy('dropped spy');
}

/**
* Component that passes through whatever content is projected into it.
* Used to test having drag elements being projected into a component.
*/
@Component({
selector: 'passthrough-component',
template: '<ng-content></ng-content>'
})
class PassthroughComponent {}

/**
* Drags an element to a position on the page using the mouse.
* @param fixture Fixture on which to run change detection.
Expand Down
16 changes: 12 additions & 4 deletions src/cdk/drag-drop/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {CdkDragPreview} from './drag-preview';
import {CDK_DROP_CONTAINER, CdkDropContainer} from './drop-container';
import {getTransformTransitionDurationInMs} from './transition-duration';
import {extendStyles, toggleNativeDragInteractions} from './drag-styling';
import {CDK_DRAG_PARENT} from './drag-parent';


// TODO(crisbeto): add auto-scrolling functionality.
Expand Down Expand Up @@ -84,7 +85,11 @@ export function CDK_DRAG_CONFIG_FACTORY(): CdkDragConfig {
host: {
'class': 'cdk-drag',
'[class.cdk-drag-dragging]': '_hasStartedDragging && _isDragging()',
}
},
providers: [{
provide: CDK_DRAG_PARENT,
useExisting: CdkDrag
}]
})
export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
private _document: Document;
Expand Down Expand Up @@ -164,7 +169,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
private _pointerUpSubscription = Subscription.EMPTY;

/** Elements that can be used to drag the draggable item. */
@ContentChildren(CdkDragHandle) _handles: QueryList<CdkDragHandle>;
@ContentChildren(CdkDragHandle, {descendants: true}) _handles: QueryList<CdkDragHandle>;

/** Element that will be used as a template to create the draggable item's preview. */
@ContentChild(CdkDragPreview) _previewTemplate: CdkDragPreview;
Expand Down Expand Up @@ -287,9 +292,12 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {

/** Handler for the `mousedown`/`touchstart` events. */
_pointerDown = (event: MouseEvent | TouchEvent) => {
// Skip handles inside descendant `CdkDrag` instances.
const handles = this._handles.filter(handle => handle._parentDrag === this);

// Delegate the event based on whether it started from a handle or the element itself.
if (this._handles.length) {
const targetHandle = this._handles.find(handle => {
if (handles.length) {
const targetHandle = handles.find(handle => {
const element = handle.element.nativeElement;
const target = event.target;
return !!target && (target === element || element.contains(target as HTMLElement));
Expand Down