diff --git a/lib/utils/positionFns.js b/lib/utils/positionFns.js index 74b57e5a..248d4d0b 100644 --- a/lib/utils/positionFns.js +++ b/lib/utils/positionFns.js @@ -22,8 +22,14 @@ export function getBoundPosition(draggable: Draggable, x: number, y: number): [n if (bounds === 'parent') { boundNode = node.parentNode; } else { - boundNode = ownerDocument.querySelector(bounds); + // Flow assigns the wrong return type (Node) for getRootNode(), + // so we cast it to one of the correct types (Element). + // The others are Document and ShadowRoot. + // All three implement querySelector() so it's safe to call. + const rootNode = (((node.getRootNode()): any): Element); + boundNode = rootNode.querySelector(bounds); } + if (!(boundNode instanceof ownerWindow.HTMLElement)) { throw new Error('Bounds selector "' + bounds + '" could not find an element.'); } diff --git a/specs/draggable.spec.jsx b/specs/draggable.spec.jsx index 143eae96..cd014f78 100644 --- a/specs/draggable.spec.jsx +++ b/specs/draggable.spec.jsx @@ -807,6 +807,129 @@ describe('react-draggable', function () { }); + it('should clip dragging to parent, with bounds set to "parent"', function(done){ + function onDrag(event, data) { + assert.equal(data.x, 100); + assert.equal(data.y, 100); + assert.equal(data.deltaX, 50); + assert.equal(data.deltaY, 50); + done(); + } + drag = TestUtils.renderIntoDocument( + +
+ + ); + const node = ReactDOM.findDOMNode(drag); + + // Create a parent container. + const fragment = fragmentFromString(` +
+
+ `); + transplantNodeInto(node, fragment, (f) => f); + + + // (element, fromX, fromY, toX, toY) + simulateMovementFromTo(drag, 50, 50, 350, 350); + + }); + + it('should clip dragging to parent, with bounds set to "parent", in a shadow tree', function(done){ + function onDrag(event, data) { + assert.equal(data.x, 100); + assert.equal(data.y, 100); + assert.equal(data.deltaX, 50); + assert.equal(data.deltaY, 50); + done(); + } + drag = TestUtils.renderIntoDocument( + +
+ + ); + const node = ReactDOM.findDOMNode(drag); + + // Create a parent container. + const fragment = fragmentFromString(` +
+
+ `); + + // Add the parent fragment to a shadow root + const div = document.createElement('div'); + const shadowRoot = div.attachShadow({mode: 'open'}); + shadowRoot.appendChild(fragment); + + transplantNodeInto(node, shadowRoot, (f) => f.children[0]); + + + // (element, fromX, fromY, toX, toY) + simulateMovementFromTo(drag, 50, 50, 350, 350); + + }); + + it('should clip dragging to parent, with bounds set to selector', function(done){ + function onDrag(event, data) { + assert.equal(data.x, 100); + assert.equal(data.y, 100); + assert.equal(data.deltaX, 50); + assert.equal(data.deltaY, 50); + done(); + } + drag = TestUtils.renderIntoDocument( + +
+ + ); + const node = ReactDOM.findDOMNode(drag); + + // Create a parent container. + const fragment = fragmentFromString(` +
+
+ `); + transplantNodeInto(node, fragment, (f) => f); + + + // (element, fromX, fromY, toX, toY) + simulateMovementFromTo(drag, 50, 50, 350, 350); + + }); + + it('should clip dragging to parent, with bounds set to selector, in a shadow tree', function(done){ + function onDrag(event, data) { + assert.equal(data.x, 100); + assert.equal(data.y, 100); + assert.equal(data.deltaX, 50); + assert.equal(data.deltaY, 50); + done(); + } + drag = TestUtils.renderIntoDocument( + +
+ + ); + const node = ReactDOM.findDOMNode(drag); + + // Create a parent container. + const fragment = fragmentFromString(` +
+
+ `); + + // Add the parent fragment to a shadow root + const div = document.createElement('div'); + const shadowRoot = div.attachShadow({mode: 'open'}); + shadowRoot.appendChild(fragment); + + transplantNodeInto(node, shadowRoot, (f) => f.children[0]); + + // (element, fromX, fromY, toX, toY) + simulateMovementFromTo(drag, 50, 50, 350, 350); + + }); + it('should call back with offset left/top, not client', function(done) { function onDrag(event, data) { assert.equal(data.x, 100);