Skip to content
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

Fix for Shadow DOM #470

Closed
wants to merge 4 commits into from
Closed
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
22 changes: 19 additions & 3 deletions dragula.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ function dragula (initialContainers, options) {
if (ignore) {
return; // we only care about honest-to-god left clicks and touch events
}
var item = e.target;
var item = e.target.shadowRoot && e.composedPath()[0] || e.target;
var context = canStart(item);
if (!context) {
return;
Expand Down Expand Up @@ -551,16 +551,32 @@ function getElementBehindPoint (point, x, y) {
var state = p.className;
var el;
p.className += ' gu-hide';
el = doc.elementFromPoint(x, y);
el = elementFromPoint(x, y);
p.className = state;
return el;

// Find the topmost element at point x, y.
// Traverses any shadow root to find the deepest element.
function elementFromPoint(x, y) {
var el = doc.elementFromPoint(x, y);
var maybeElem;
while (el && el.shadowRoot) {
maybeElem = el.shadowRoot.elementFromPoint(x, y);
if (maybeElem) {
el = maybeElem;
} else {
return el;
}
}
return el;
}
}

function never () { return false; }
function always () { return true; }
function getRectWidth (rect) { return rect.width || (rect.right - rect.left); }
function getRectHeight (rect) { return rect.height || (rect.bottom - rect.top); }
function getParent (el) { return el.parentNode === doc ? null : el.parentNode; }
function getParent (el) { return el.parentNode === doc ? null : (el.parentNode || el.host); }
function isInput (el) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || isEditable(el); }
function isEditable (el) {
if (!el) { return false; } // no parents were editable
Expand Down
178 changes: 99 additions & 79 deletions test/cancel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
var test = require('tape');
var dragula = require('..');

test('with normal DOM', function(t) {
domTests(t, document.body);
t.end();
});

test('with nested shadow DOM', function(t) {
var div = document.createElement('div');
var div2 = document.createElement('div');
div.createShadowRoot();
div2.createShadowRoot();
div.shadowRoot.appendChild(div2);
document.body.appendChild(div);

domTests(t, div2.shadowRoot);
t.end();
});

test('cancel does not throw when not dragging', function (t) {
t.test('a single time', function once (st) {
var drake = dragula();
Expand All @@ -24,85 +41,88 @@ test('cancel does not throw when not dragging', function (t) {
t.end();
});

test('when dragging and cancel gets called, nothing happens', function (t) {
var div = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div]);
div.appendChild(item);
document.body.appendChild(div);
drake.start(item);
drake.cancel();
t.equal(div.children.length, 1, 'nothing happens');
t.equal(drake.dragging, false, 'drake has stopped dragging');
t.end();
});
function domTests(t, root) {

test('when dragging and cancel gets called, cancel event is emitted', function (t) {
var div = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div]);
div.appendChild(item);
document.body.appendChild(div);
drake.start(item);
drake.on('cancel', cancel);
drake.on('dragend', dragend);
drake.cancel();
t.plan(3);
t.end();
function dragend () {
t.pass('dragend got called');
}
function cancel (target, container) {
t.equal(target, item, 'cancel was invoked with item');
t.equal(container, div, 'cancel was invoked with container');
}
});
t.test('when dragging and cancel gets called, nothing happens', function (t) {
var div = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div]);
div.appendChild(item);
root.appendChild(div);
drake.start(item);
drake.cancel();
t.equal(div.children.length, 1, 'nothing happens');
t.equal(drake.dragging, false, 'drake has stopped dragging');
t.end();
});

test('when dragging a copy and cancel gets called, default does not revert', function (t) {
var div = document.createElement('div');
var div2 = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div, div2]);
div.appendChild(item);
document.body.appendChild(div);
document.body.appendChild(div2);
drake.start(item);
div2.appendChild(item);
drake.on('drop', drop);
drake.on('dragend', dragend);
drake.cancel();
t.plan(4);
t.end();
function dragend () {
t.pass('dragend got called');
}
function drop (target, parent, source) {
t.equal(target, item, 'drop was invoked with item');
t.equal(parent, div2, 'drop was invoked with final container');
t.equal(source, div, 'drop was invoked with source container');
}
});
t.test('when dragging and cancel gets called, cancel event is emitted', function (t) {
var div = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div]);
div.appendChild(item);
root.appendChild(div);
drake.start(item);
drake.on('cancel', cancel);
drake.on('dragend', dragend);
drake.cancel();
t.plan(3);
t.end();
function dragend () {
t.pass('dragend got called');
}
function cancel (target, container) {
t.equal(target, item, 'cancel was invoked with item');
t.equal(container, div, 'cancel was invoked with container');
}
});

test('when dragging a copy and cancel gets called, revert is executed', function (t) {
var div = document.createElement('div');
var div2 = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div, div2]);
div.appendChild(item);
document.body.appendChild(div);
document.body.appendChild(div2);
drake.start(item);
div2.appendChild(item);
drake.on('cancel', cancel);
drake.on('dragend', dragend);
drake.cancel(true);
t.plan(3);
t.end();
function dragend () {
t.pass('dragend got called');
}
function cancel (target, container) {
t.equal(target, item, 'cancel was invoked with item');
t.equal(container, div, 'cancel was invoked with container');
}
});
t.test('when dragging a copy and cancel gets called, default does not revert', function (t) {
var div = document.createElement('div');
var div2 = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div, div2]);
div.appendChild(item);
root.appendChild(div);
root.appendChild(div2);
drake.start(item);
div2.appendChild(item);
drake.on('drop', drop);
drake.on('dragend', dragend);
drake.cancel();
t.plan(4);
t.end();
function dragend () {
t.pass('dragend got called');
}
function drop (target, parent, source) {
t.equal(target, item, 'drop was invoked with item');
t.equal(parent, div2, 'drop was invoked with final container');
t.equal(source, div, 'drop was invoked with source container');
}
});

t.test('when dragging a copy and cancel gets called, revert is executed', function (t) {
var div = document.createElement('div');
var div2 = document.createElement('div');
var item = document.createElement('div');
var drake = dragula([div, div2]);
div.appendChild(item);
root.appendChild(div);
root.appendChild(div2);
drake.start(item);
div2.appendChild(item);
drake.on('cancel', cancel);
drake.on('dragend', dragend);
drake.cancel(true);
t.plan(3);
t.end();
function dragend () {
t.pass('dragend got called');
}
function cancel (target, container) {
t.equal(target, item, 'cancel was invoked with item');
t.equal(container, div, 'cancel was invoked with container');
}
});
}
Loading