Skip to content

Commit

Permalink
added preventDefault prop
Browse files Browse the repository at this point in the history
  • Loading branch information
totalolage committed Jul 5, 2021
1 parent 22b0615 commit 208c773
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 25 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog

### 4.4.4 (July 4, 2021)

- Add `preventDefault` prop to allow touch scroll

### 4.4.3 (June 8, 2020)

- Add `nodeRef` to TypeScript definitions
Expand Down
10 changes: 8 additions & 2 deletions README.md
Expand Up @@ -264,7 +264,12 @@ positionOffset: {x: number | string, y: number | string},
// Specifies the scale of the canvas your are dragging this element on. This allows
// you to, for example, get the correct drag deltas while you are zoomed in or out via
// a transform or matrix in the parent of this element.
scale: number
scale: number,

// If set to false, the input event will not be default-prevented.
// You should call `.preventDefault() `within the `onStart`, `onDrag`, and `onEnd` event handlers.
// This allows for touch scrolling to work when the event originates on a draggable element.
preventDefault: boolean
}
```

Expand Down Expand Up @@ -321,7 +326,8 @@ on itself and thus must have callbacks attached to be useful.
onDrag: DraggableEventHandler,
onStop: DraggableEventHandler,
onMouseDown: (e: MouseEvent) => void,
scale: number
scale: number,
preventDefault: boolean
}
```

Expand Down
5 changes: 5 additions & 0 deletions example/example.js
Expand Up @@ -150,6 +150,11 @@ class App extends React.Component {
Both parent padding and child margin work properly.
</div>
</Draggable>
<Draggable bounds="parent" {...dragHandlers} preventDefault={false}>
<div className="box">
I don't prevent touches from scrolling the container.
</div>
</Draggable>
</div>
</div>
<Draggable bounds="body" {...dragHandlers}>
Expand Down
7 changes: 6 additions & 1 deletion lib/DraggableCore.js
Expand Up @@ -55,6 +55,7 @@ export type DraggableCoreDefaultProps = {
onDrag: DraggableEventHandler,
onStop: DraggableEventHandler,
onMouseDown: (e: MouseEvent) => void,
preventDefault: true,
scale: number,
};

Expand All @@ -65,6 +66,7 @@ export type DraggableCoreProps = {
offsetParent: HTMLElement,
grid: [number, number],
handle: string,
preventDefault: boolean,
nodeRef?: ?React.ElementRef<any>,
};

Expand Down Expand Up @@ -208,6 +210,8 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
*/
scale: PropTypes.number,

preventDefault: PropTypes.bool,

/**
* These properties should be defined on the child, not here.
*/
Expand All @@ -224,6 +228,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
onDrag: function(){},
onStop: function(){},
onMouseDown: function(){},
preventDefault: true,
scale: 1,
};

Expand Down Expand Up @@ -292,7 +297,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D

// Prevent scrolling on mobile devices, like ipad/iphone.
// Important that this is after handle/cancel.
if (e.type === 'touchstart') e.preventDefault();
if (this.props.preventDefault && e.type === 'touchstart') e.preventDefault();

// Set touch identifier in component state if this is a touch event. This allows us to
// distinguish between individual touches on multitouch screens by identifying which
Expand Down
122 changes: 100 additions & 22 deletions specs/draggable.spec.jsx
Expand Up @@ -97,7 +97,7 @@ describe('react-draggable', function () {

// Not easy to actually test equality here. The functions are bound as static props so we can't test those easily.
const toOmit = ['onStart', 'onStop', 'onDrag', 'onMouseDown', 'children'];
assert.deepEqual(
assert.deepStrictEqual(
_.omit(output.props, toOmit),
_.omit(expected.props, toOmit)
);
Expand Down Expand Up @@ -148,7 +148,7 @@ describe('react-draggable', function () {
</Draggable>
);

simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should throw when setting className', function () {
Expand Down Expand Up @@ -249,7 +249,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand All @@ -265,7 +265,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand All @@ -281,7 +281,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand All @@ -297,7 +297,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand All @@ -313,7 +313,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand Down Expand Up @@ -348,7 +348,7 @@ describe('react-draggable', function () {
);

const node = ReactDOM.findDOMNode(drag);
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

const transform = node.getAttribute('transform');
assert(transform.indexOf('translate(100,100)') >= 0);
Expand Down Expand Up @@ -461,7 +461,7 @@ describe('react-draggable', function () {
const body = iframeDoc.body;
const node = body.querySelector('.react-draggable');
if (!node) return setTimeout(checkIframe, 50);
simulateMovementFromTo(node, 0, 0, 100, 100);
simulateMouseFromTo(node, 0, 0, 100, 100);

const style = node.getAttribute('style');
assert(dragged === true);
Expand Down Expand Up @@ -711,6 +711,48 @@ describe('react-draggable', function () {
done();
}, 50);
});

// it('should allow touch scrolling parent', function (done) {
// let dragCalled = false;
// function onDrag(event, data) {
// dragCalled = true;
// // assert(data.x === 100);
// // assert(data.y === 100);
// // assert(data.deltaX === 100);
// // assert(data.deltaY === 100);
// }

// const scrollParent = fragmentFromString(`
// <div style="overflow: auto; width: 500px; height: 500px; outline: 1px solid black">
// <div style="width: 10000px; height: 10000px;">
// </div>
// </div>
// `);

// drag = TestUtils.renderIntoDocument(
// <Draggable onDrag={onDrag} preventDefault={false} defaultPosition={{x: 200, y: 200}} offsetParent={scrollParent}>
// <div style={{position: 'relative', width: '100px', height: '100px', outline: '1px solid red'}} />
// </Draggable>
// );
// const node = ReactDOM.findDOMNode(drag);

// transplantNodeInto(node, scrollParent, (f) => f.children[0]);

// const scrollParentNode = ReactDOM.findDOMNode(scrollParent);

// simulateTouchFromTo(node, 200, 200, 100, 100);

// setTimeout(() => {
// console.log(node);
// assert(dragCalled, 'onDrag was not called');
// assert(scrollParentNode.scrollTop !== 0 && scrollParentNode.scrollLeft !== 0, 'parent didn\'t scroll on touch');
// assert(scrollParentNode.scrollTop === 100, 'parent vertical scroll is off');
// assert(scrollParentNode.scrollLeft === 100, 'parent horizontal scroll is off');
// // cleanup
// document.body.removeChild(scrollParent);
// done();
// }, 50);
// });
});

describe('draggable callbacks', function () {
Expand All @@ -729,7 +771,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should call back with correct dom node with nodeRef', function () {
Expand All @@ -748,7 +790,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should call back on drag, with values within the defined bounds', function(){
Expand All @@ -765,7 +807,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);

});

Expand All @@ -782,7 +824,7 @@ describe('react-draggable', function () {
</Draggable>
);

simulateMovementFromTo(drag, 200, 200, 300, 300);
simulateMouseFromTo(drag, 200, 200, 300, 300);
});

it('should call back with correct position when parent element is 2x scaled', function() {
Expand All @@ -801,7 +843,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should call back with correct position when parent element is 0.5x scaled', function() {
Expand All @@ -820,7 +862,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should not throw an error if unmounted during a callback', function () {
Expand All @@ -847,7 +889,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(dragRef.current, 0, 0, 100, 100);
simulateMouseFromTo(dragRef.current, 0, 0, 100, 100);

// ok, was a setstate warning thrown?
// Assert unmounted
Expand All @@ -872,7 +914,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should call back with correct position when parent element is 2x scaled', function() {
Expand All @@ -891,7 +933,7 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});

it('should call back with correct position when parent element is 0.5x scaled', function() {
Expand All @@ -910,11 +952,10 @@ describe('react-draggable', function () {
);

// (element, fromX, fromY, toX, toY)
simulateMovementFromTo(drag, 0, 0, 100, 100);
simulateMouseFromTo(drag, 0, 0, 100, 100);
});
});


describe('validation', function () {
it('should result with invariant when there isn\'t a child', function () {
const renderer = new ShallowRenderer();
Expand Down Expand Up @@ -951,15 +992,52 @@ function mouseMove(x, y, node) {
return evt;
}

function createClientXY(x, y) {
return { clientX: x, clientY: y };
}

function touchMove(x, y, node) {
const touchObj = new Touch({
identifier: Date.now(),
target: node,
clientX: x,
clientY: y,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 0.5,
});

const touchEvent = new TouchEvent('touchmove', {
cancelable: true,
bubbles: true,
touches: [touchObj],
targetTouches: [],
changedTouches: [touchObj],
shiftKey: true,
});

node.dispatchEvent(touchEvent);
}


function simulateMovementFromTo(drag, fromX, fromY, toX, toY) {
function simulateMouseFromTo(drag, fromX, fromY, toX, toY) {
const node = ReactDOM.findDOMNode(drag);

TestUtils.Simulate.mouseDown(node, {clientX: fromX, clientY: fromY});
TestUtils.Simulate.mouseDown(node, createClientXY(fromX, fromY));
mouseMove(toX, toY, node);
TestUtils.Simulate.mouseUp(node);
}

// // Does not work, cannot figure out how to correctly simulate touches
// function simulateTouchFromTo(drag, fromX, fromY, toX, toY) {
// const node = ReactDOM.findDOMNode(drag);

// TestUtils.Simulate.touchStart(node, { touches: [createClientXY(fromX, fromY)] });
// touchMove(toX, toY, node);
// TestUtils.Simulate.touchEnd(node, { touches: [createClientXY(toX, toY)], changedTouches: [createClientXY(toX, toY)]});
// }

function fragmentFromString(strHTML) {
var temp = document.createElement('div');
temp.innerHTML = strHTML;
Expand Down

0 comments on commit 208c773

Please sign in to comment.