Skip to content

Commit dc5d1c7

Browse files
Merge branch 'dropeffects' into dev
2 parents 27e8fa5 + 3ff34af commit dc5d1c7

File tree

4 files changed

+141
-37
lines changed

4 files changed

+141
-37
lines changed

angular-drag-and-drop-lists.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,6 @@
381381
var ignoreDataTransfer = mimeType == MSIE_MIME_TYPE && dndState.isDragging;
382382
var dropEffect = getDropEffect(event, ignoreDataTransfer);
383383
if (dropEffect == 'none') return stopDragover();
384-
if (!ignoreDataTransfer) {
385-
event.dataTransfer.dropEffect = dropEffect;
386-
}
387384

388385
// Invoke the callback, which can transform the transferredObject and even abort the drop.
389386
var index = getPlaceholderIndex();
@@ -393,6 +390,12 @@
393390
}
394391
dndState.dropEffect = dropEffect;
395392

393+
// The drop is definitely going to happen now, store the dropEffect.
394+
dndState.dropEffect = dropEffect;
395+
if (!ignoreDataTransfer) {
396+
event.dataTransfer.dropEffect = dropEffect;
397+
}
398+
396399
// Insert the object into the array, unless dnd-drop took care of that (returned true).
397400
if (data !== true) {
398401
scope.$apply(function() {

test/dndDraggableSpec.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ describe('dndDraggable', function() {
7070
expect(Dragstart.on(element).effectAllowed).toBe('copyMove');
7171
});
7272

73+
it('sets effectAllowed to single effect in IE', function() {
74+
element = compileAndLink('<div dnd-draggable dnd-effect-allowed="copyLink"></div>');
75+
expect(Dragstart.on(element, {allowedMimeTypes: ['Text']}).effectAllowed).toBe('copy');
76+
});
77+
7378
it('adds CSS classes to element', inject(function($timeout) {
7479
Dragstart.on(element);
7580
expect(element.hasClass('dndDragging')).toBe(true);
@@ -124,24 +129,18 @@ describe('dndDraggable', function() {
124129
expect(element.hasClass('dndDraggingSource')).toBe(false);
125130
}));
126131

127-
var dropEffects = {move: 'moved', copy: 'copied', none: 'canceled'};
132+
var dropEffects = {move: 'moved', copy: 'copied', link: 'linked', none: 'canceled'};
128133
angular.forEach(dropEffects, function(callback, dropEffect) {
129134
it('calls callbacks for dropEffect ' + dropEffect, function() {
130-
var html = '<div dnd-draggable="{}" dnd-dragend="de = dropEffect" '
131-
+ 'dnd-' + callback + '="ev = event"></div>';
135+
var html = '<div dnd-draggable="{}" dnd-effect-allowed="' + dropEffect + '" '
136+
+ 'dnd-dragend="returnedDropEffect = dropEffect" '
137+
+ 'dnd-' + callback + '="returnedEvent = event"></div>';
132138
var element = compileAndLink(html);
139+
var target = compileAndLink('<div dnd-list="[]"></div>');
140+
Dragstart.on(element).dragover(target).drop(target).dragend(element);
133141

134-
var dragstart = Dragstart.on(element);
135-
if (dropEffect != 'none') {
136-
var target = compileAndLink('<div dnd-list="[]"></div>');
137-
var options = {dropEffect: dropEffect};
138-
dragstart.dragover(target, options).drop(target).dragend(element);
139-
} else {
140-
dragstart.dragend(element);
141-
}
142-
143-
expect(element.scope().ev).toEqual(jasmine.any(DragEventMock));
144-
expect(element.scope().de).toBe(dropEffect);
142+
expect(element.scope().returnedEvent).toEqual(jasmine.any(DragEventMock));
143+
expect(element.scope().returnedDropEffect).toBe(dropEffect);
145144
});
146145
});
147146
});

test/dndListSpec.js

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -341,24 +341,6 @@ describe('dndList', function() {
341341
var dragenter = Dragenter.externalOn(element, {'application/x-dnd': 'Lorem ipsum'});
342342
verifyDropCancelled(dragenter.dragover(element).drop(element), element, true, 3);
343343
});
344-
345-
describe('dropEffect calculation', function() {
346-
testDropEffect('move', 'move');
347-
testDropEffect('blub', 'blub');
348-
testDropEffect('copy', 'none', 'copy');
349-
testDropEffect('move', 'none', 'move');
350-
testDropEffect('move', 'none', 'link');
351-
testDropEffect('copy', 'none', 'link', true);
352-
353-
function testDropEffect(expected, dropEffect, effectAllowed, ctrlKey) {
354-
it('stores ' + expected + ' for ' + [dropEffect, effectAllowed, ctrlKey], function() {
355-
var src = compileAndLink('<div dnd-draggable="{}" dnd-dragend="eff = dropEffect"></div>');
356-
var options = { dropEffect: dropEffect, effectAllowed: effectAllowed, ctrlKey: ctrlKey };
357-
Dragstart.on(src).dragover(element, options).drop(element).dragend(src);
358-
expect(src.scope().eff).toBe(expected);
359-
});
360-
}
361-
});
362344
});
363345

364346
describe('dragleave handler', function() {
@@ -399,6 +381,121 @@ describe('dndList', function() {
399381
}));
400382
});
401383

384+
describe('dropEffect', function() {
385+
// This matrix shows the expected drop effect, given two effectAllowed values.
386+
var ALL = [ 'all', 'move', 'copy', 'link', 'copyLink', 'copyMove', 'linkMove'];
387+
var EXPECTED_MATRIX = {
388+
move: ['move', 'move', 'none', 'none', 'none', 'move', 'move'],
389+
copy: ['copy', 'none', 'copy', 'none', 'copy', 'copy', 'none'],
390+
link: ['link', 'none', 'none', 'link', 'link', 'none', 'link'],
391+
copyLink: ['copy', 'none', 'copy', 'link', 'copy', 'copy', 'link'],
392+
copyMove: ['move', 'move', 'copy', 'none', 'copy', 'move', 'move'],
393+
linkMove: ['move', 'move', 'none', 'link', 'link', 'move', 'move'],
394+
all: ['move', 'move', 'copy', 'link', 'copy', 'move', 'move'],
395+
'': ['move', 'move', 'copy', 'link', 'copy', 'move', 'move'],
396+
};
397+
angular.forEach(ALL, function(sourceEffectAllowed, index) {
398+
angular.forEach(EXPECTED_MATRIX, function(expected, targetEffectAllowed) {
399+
expected = expected[index];
400+
it('is ' + expected + ' for effect-allowed ' + sourceEffectAllowed
401+
+ ' and ' + targetEffectAllowed, function() {
402+
var src = compileAndLink('<div dnd-draggable="{}" dnd-dragend="result = dropEffect" '
403+
+ 'dnd-effect-allowed="' + sourceEffectAllowed + '"></div>');
404+
var target = createListWithItemsAndCallbacks(false, targetEffectAllowed);
405+
expect(Dragstart.on(src).effectAllowed).toBe(sourceEffectAllowed);
406+
if (expected != 'none') {
407+
// Verify dragover.
408+
expect(Dragstart.on(src).dragover(target).dropEffect).toBe(expected);
409+
expect(target.scope().dragover.dropEffect).toBe(expected);
410+
// Verify drop.
411+
expect(Dragstart.on(src).dragover(target).drop(target).dropEffect).toBe(expected);
412+
expect(target.scope().drop.dropEffect).toBe(expected);
413+
// Verify dragend.
414+
Dragstart.on(src).dragover(target).drop(target).dragend(src);
415+
expect(src.scope().result).toBe(expected);
416+
} else {
417+
verifyDropCancelled(Dragstart.on(src).dragover(target), target, false, 3);
418+
verifyDropCancelled(Dragstart.on(src).dragover(target).drop(target), target, true, 3);
419+
Dragstart.on(src).dragend(src);
420+
expect(src.scope().result).toBe('none');
421+
}
422+
});
423+
});
424+
});
425+
426+
// In Safari dataTransfer.effectAllowed is always 'all', ignoring the value set in dragstart.
427+
it('is determined from internal state in Safari', function() {
428+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="link"></div>');
429+
var target = createListWithItemsAndCallbacks(false, 'copyLink');
430+
var options = {effectAllowed: 'all'};
431+
Dragstart.on(src).dragover(target, options).drop(target, options);
432+
expect(target.scope().dragover.dropEffect).toBe('link');
433+
expect(target.scope().drop.dropEffect).toBe('link');
434+
});
435+
436+
// On MacOS, modifiers automatically limit the effectAllowed passed to dragover and drop.
437+
it('is limited by modifier keys on MacOS', function() {
438+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
439+
var target = createListWithItemsAndCallbacks();
440+
Dragstart.on(src).dragover(target, {effectAllowed: 'copyLink'}).drop(target);
441+
expect(target.scope().dragover.dropEffect).toBe('copy');
442+
expect(target.scope().drop.dropEffect).toBe('copy');
443+
});
444+
445+
it('is copy if Ctrl key is pressed', function() {
446+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
447+
var target = createListWithItemsAndCallbacks();
448+
Dragstart.on(src).dragover(target, {ctrlKey: true}).drop(target);
449+
expect(target.scope().dragover.dropEffect).toBe('copy');
450+
expect(target.scope().drop.dropEffect).toBe('copy');
451+
});
452+
453+
it('is link if Alt key is pressed', function() {
454+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="all"></div>');
455+
var target = createListWithItemsAndCallbacks();
456+
Dragstart.on(src).dragover(target, {altKey: true}).drop(target);
457+
expect(target.scope().dragover.dropEffect).toBe('link');
458+
expect(target.scope().drop.dropEffect).toBe('link');
459+
});
460+
461+
it('ignores Ctrl key if copy is not possible', function() {
462+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="linkMove"></div>');
463+
var target = createListWithItemsAndCallbacks();
464+
Dragstart.on(src).dragover(target, {ctrlKey: true}).drop(target);
465+
expect(target.scope().dragover.dropEffect).toBe('move');
466+
expect(target.scope().drop.dropEffect).toBe('move');
467+
});
468+
469+
it('respects effectAllowed from external drops', function() {
470+
var target = createListWithItemsAndCallbacks();
471+
Dragenter.validExternalOn(target, {effectAllowed: 'copyLink'}).dragover(target).drop(target);
472+
expect(target.scope().dragover.dropEffect).toBe('copy');
473+
expect(target.scope().drop.dropEffect).toBe('copy');
474+
});
475+
476+
it('respects effectAllowed from external drops even in IE', function() {
477+
var target = createListWithItemsAndCallbacks();
478+
Dragenter.externalOn(target, {'Text': '{}'}, {effectAllowed: 'copyLink'})
479+
.dragover(target).drop(target);
480+
expect(target.scope().dragover.dropEffect).toBe('copy');
481+
expect(target.scope().drop.dropEffect).toBe('copy');
482+
});
483+
484+
it('ignores effectAllowed from internal drops in IE', function() {
485+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="copyLink"></div>');
486+
var target = createListWithItemsAndCallbacks();
487+
Dragstart.on(src, {allowedMimeTypes: ['Text']}).dragover(target, {altKey: true});
488+
expect(target.scope().dragover.dropEffect).toBe('link');
489+
});
490+
491+
it('does not set dropEffect in IE', function() {
492+
var src = compileAndLink('<div dnd-draggable="{}" dnd-effect-allowed="copyLink"></div>');
493+
var target = createListWithItemsAndCallbacks();
494+
var dragover = Dragstart.on(src, {allowedMimeTypes: ['Text']}).dragover(target);
495+
expect(dragover.dropEffect).toBeUndefined();
496+
});
497+
});
498+
402499
function verifyDropAccepted(result) {
403500
expect(result.defaultPrevented).toBe(true);
404501
if (result.type == 'dragenter') {
@@ -414,6 +511,7 @@ describe('dndList', function() {
414511
expect(result.returnValue).toBe(true);
415512
expect(result.propagationStopped).toBe(false);
416513
expect(result.defaultPrevented).toBe(opt_defaultPrevented || false);
514+
expect(result.dropEffect).toBeUndefined();
417515
expect(element.hasClass("dndDragover")).toBe(false);
418516
expect(element.children().length).toBe(opt_children || 0);
419517
}
@@ -428,10 +526,12 @@ describe('dndList', function() {
428526
verify(drop, element);
429527
}
430528

431-
function createListWithItemsAndCallbacks(horizontal) {
432-
var params = '{event: event, index: index, item: item, external: external, type: type}';
529+
function createListWithItemsAndCallbacks(horizontal, effectAllowed) {
530+
var params = '{event: event, dropEffect: dropEffect, index: index, '
531+
+ 'item: item, external: external, type: type}';
433532
var element = compileAndLink('<ul dnd-list="list" dnd-external-sources="true" ' +
434533
'dnd-horizontal-list="' + (horizontal || 'false') + '" ' +
534+
(effectAllowed ? 'dnd-effect-allowed="' + effectAllowed + '" ' : '') +
435535
'dnd-dragover="dragover = ' + params + '" ' +
436536
'dnd-drop="dropHandler(' + params + ')" ' +
437537
'dnd-inserted="inserted = ' + params + '">' +

test/mocks.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class DragEventMock {
7373
get clientX() { return this.$options.clientX || 0; }
7474
get clientY() { return this.$options.clientY || 0; }
7575
get ctrlKey() { return this.$options.ctrlKey || false; }
76+
get altKey() { return this.$options.altKey || false; }
7677
get dataTransfer() { return this.$dataTransfer; }
7778
get originalEvent() { return this; }
7879
get target() { return this.$options.target || undefined; }
@@ -96,6 +97,7 @@ class DragEventResult {
9697
get propagationStopped() { return !!this.$results.invokedStopPropagation; }
9798
get defaultPrevented() { return !!this.$results.invokedPreventDefault; }
9899
get returnValue() { return this.$results.returnValue; }
100+
get dropEffect() { return this.$results.dataTransfer.dropEffect; }
99101
get type() { return this.$type; }
100102
}
101103

0 commit comments

Comments
 (0)