Skip to content

Commit 208c773

Browse files
committed
added preventDefault prop
1 parent 22b0615 commit 208c773

File tree

5 files changed

+123
-25
lines changed

5 files changed

+123
-25
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
### 4.4.4 (July 4, 2021)
4+
5+
- Add `preventDefault` prop to allow touch scroll
6+
37
### 4.4.3 (June 8, 2020)
48

59
- Add `nodeRef` to TypeScript definitions

README.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,12 @@ positionOffset: {x: number | string, y: number | string},
264264
// Specifies the scale of the canvas your are dragging this element on. This allows
265265
// you to, for example, get the correct drag deltas while you are zoomed in or out via
266266
// a transform or matrix in the parent of this element.
267-
scale: number
267+
scale: number,
268+
269+
// If set to false, the input event will not be default-prevented.
270+
// You should call `.preventDefault() `within the `onStart`, `onDrag`, and `onEnd` event handlers.
271+
// This allows for touch scrolling to work when the event originates on a draggable element.
272+
preventDefault: boolean
268273
}
269274
```
270275

@@ -321,7 +326,8 @@ on itself and thus must have callbacks attached to be useful.
321326
onDrag: DraggableEventHandler,
322327
onStop: DraggableEventHandler,
323328
onMouseDown: (e: MouseEvent) => void,
324-
scale: number
329+
scale: number,
330+
preventDefault: boolean
325331
}
326332
```
327333

example/example.js

+5
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ class App extends React.Component {
150150
Both parent padding and child margin work properly.
151151
</div>
152152
</Draggable>
153+
<Draggable bounds="parent" {...dragHandlers} preventDefault={false}>
154+
<div className="box">
155+
I don't prevent touches from scrolling the container.
156+
</div>
157+
</Draggable>
153158
</div>
154159
</div>
155160
<Draggable bounds="body" {...dragHandlers}>

lib/DraggableCore.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type DraggableCoreDefaultProps = {
5555
onDrag: DraggableEventHandler,
5656
onStop: DraggableEventHandler,
5757
onMouseDown: (e: MouseEvent) => void,
58+
preventDefault: true,
5859
scale: number,
5960
};
6061

@@ -65,6 +66,7 @@ export type DraggableCoreProps = {
6566
offsetParent: HTMLElement,
6667
grid: [number, number],
6768
handle: string,
69+
preventDefault: boolean,
6870
nodeRef?: ?React.ElementRef<any>,
6971
};
7072

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

213+
preventDefault: PropTypes.bool,
214+
211215
/**
212216
* These properties should be defined on the child, not here.
213217
*/
@@ -224,6 +228,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps, D
224228
onDrag: function(){},
225229
onStop: function(){},
226230
onMouseDown: function(){},
231+
preventDefault: true,
227232
scale: 1,
228233
};
229234

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

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

297302
// Set touch identifier in component state if this is a touch event. This allows us to
298303
// distinguish between individual touches on multitouch screens by identifying which

specs/draggable.spec.jsx

+100-22
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ describe('react-draggable', function () {
9797

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

151-
simulateMovementFromTo(drag, 0, 0, 100, 100);
151+
simulateMouseFromTo(drag, 0, 0, 100, 100);
152152
});
153153

154154
it('should throw when setting className', function () {
@@ -249,7 +249,7 @@ describe('react-draggable', function () {
249249
);
250250

251251
const node = ReactDOM.findDOMNode(drag);
252-
simulateMovementFromTo(drag, 0, 0, 100, 100);
252+
simulateMouseFromTo(drag, 0, 0, 100, 100);
253253

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

267267
const node = ReactDOM.findDOMNode(drag);
268-
simulateMovementFromTo(drag, 0, 0, 100, 100);
268+
simulateMouseFromTo(drag, 0, 0, 100, 100);
269269

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

283283
const node = ReactDOM.findDOMNode(drag);
284-
simulateMovementFromTo(drag, 0, 0, 100, 100);
284+
simulateMouseFromTo(drag, 0, 0, 100, 100);
285285

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

299299
const node = ReactDOM.findDOMNode(drag);
300-
simulateMovementFromTo(drag, 0, 0, 100, 100);
300+
simulateMouseFromTo(drag, 0, 0, 100, 100);
301301

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

315315
const node = ReactDOM.findDOMNode(drag);
316-
simulateMovementFromTo(drag, 0, 0, 100, 100);
316+
simulateMouseFromTo(drag, 0, 0, 100, 100);
317317

318318
const style = node.getAttribute('style');
319319
assert(dragged === true);
@@ -348,7 +348,7 @@ describe('react-draggable', function () {
348348
);
349349

350350
const node = ReactDOM.findDOMNode(drag);
351-
simulateMovementFromTo(drag, 0, 0, 100, 100);
351+
simulateMouseFromTo(drag, 0, 0, 100, 100);
352352

353353
const transform = node.getAttribute('transform');
354354
assert(transform.indexOf('translate(100,100)') >= 0);
@@ -461,7 +461,7 @@ describe('react-draggable', function () {
461461
const body = iframeDoc.body;
462462
const node = body.querySelector('.react-draggable');
463463
if (!node) return setTimeout(checkIframe, 50);
464-
simulateMovementFromTo(node, 0, 0, 100, 100);
464+
simulateMouseFromTo(node, 0, 0, 100, 100);
465465

466466
const style = node.getAttribute('style');
467467
assert(dragged === true);
@@ -711,6 +711,48 @@ describe('react-draggable', function () {
711711
done();
712712
}, 50);
713713
});
714+
715+
// it('should allow touch scrolling parent', function (done) {
716+
// let dragCalled = false;
717+
// function onDrag(event, data) {
718+
// dragCalled = true;
719+
// // assert(data.x === 100);
720+
// // assert(data.y === 100);
721+
// // assert(data.deltaX === 100);
722+
// // assert(data.deltaY === 100);
723+
// }
724+
725+
// const scrollParent = fragmentFromString(`
726+
// <div style="overflow: auto; width: 500px; height: 500px; outline: 1px solid black">
727+
// <div style="width: 10000px; height: 10000px;">
728+
// </div>
729+
// </div>
730+
// `);
731+
732+
// drag = TestUtils.renderIntoDocument(
733+
// <Draggable onDrag={onDrag} preventDefault={false} defaultPosition={{x: 200, y: 200}} offsetParent={scrollParent}>
734+
// <div style={{position: 'relative', width: '100px', height: '100px', outline: '1px solid red'}} />
735+
// </Draggable>
736+
// );
737+
// const node = ReactDOM.findDOMNode(drag);
738+
739+
// transplantNodeInto(node, scrollParent, (f) => f.children[0]);
740+
741+
// const scrollParentNode = ReactDOM.findDOMNode(scrollParent);
742+
743+
// simulateTouchFromTo(node, 200, 200, 100, 100);
744+
745+
// setTimeout(() => {
746+
// console.log(node);
747+
// assert(dragCalled, 'onDrag was not called');
748+
// assert(scrollParentNode.scrollTop !== 0 && scrollParentNode.scrollLeft !== 0, 'parent didn\'t scroll on touch');
749+
// assert(scrollParentNode.scrollTop === 100, 'parent vertical scroll is off');
750+
// assert(scrollParentNode.scrollLeft === 100, 'parent horizontal scroll is off');
751+
// // cleanup
752+
// document.body.removeChild(scrollParent);
753+
// done();
754+
// }, 50);
755+
// });
714756
});
715757

716758
describe('draggable callbacks', function () {
@@ -729,7 +771,7 @@ describe('react-draggable', function () {
729771
);
730772

731773
// (element, fromX, fromY, toX, toY)
732-
simulateMovementFromTo(drag, 0, 0, 100, 100);
774+
simulateMouseFromTo(drag, 0, 0, 100, 100);
733775
});
734776

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

750792
// (element, fromX, fromY, toX, toY)
751-
simulateMovementFromTo(drag, 0, 0, 100, 100);
793+
simulateMouseFromTo(drag, 0, 0, 100, 100);
752794
});
753795

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

767809
// (element, fromX, fromY, toX, toY)
768-
simulateMovementFromTo(drag, 0, 0, 100, 100);
810+
simulateMouseFromTo(drag, 0, 0, 100, 100);
769811

770812
});
771813

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

785-
simulateMovementFromTo(drag, 200, 200, 300, 300);
827+
simulateMouseFromTo(drag, 200, 200, 300, 300);
786828
});
787829

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

803845
// (element, fromX, fromY, toX, toY)
804-
simulateMovementFromTo(drag, 0, 0, 100, 100);
846+
simulateMouseFromTo(drag, 0, 0, 100, 100);
805847
});
806848

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

822864
// (element, fromX, fromY, toX, toY)
823-
simulateMovementFromTo(drag, 0, 0, 100, 100);
865+
simulateMouseFromTo(drag, 0, 0, 100, 100);
824866
});
825867

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

849891
// (element, fromX, fromY, toX, toY)
850-
simulateMovementFromTo(dragRef.current, 0, 0, 100, 100);
892+
simulateMouseFromTo(dragRef.current, 0, 0, 100, 100);
851893

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

874916
// (element, fromX, fromY, toX, toY)
875-
simulateMovementFromTo(drag, 0, 0, 100, 100);
917+
simulateMouseFromTo(drag, 0, 0, 100, 100);
876918
});
877919

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

893935
// (element, fromX, fromY, toX, toY)
894-
simulateMovementFromTo(drag, 0, 0, 100, 100);
936+
simulateMouseFromTo(drag, 0, 0, 100, 100);
895937
});
896938

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

912954
// (element, fromX, fromY, toX, toY)
913-
simulateMovementFromTo(drag, 0, 0, 100, 100);
955+
simulateMouseFromTo(drag, 0, 0, 100, 100);
914956
});
915957
});
916958

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

995+
function createClientXY(x, y) {
996+
return { clientX: x, clientY: y };
997+
}
998+
999+
function touchMove(x, y, node) {
1000+
const touchObj = new Touch({
1001+
identifier: Date.now(),
1002+
target: node,
1003+
clientX: x,
1004+
clientY: y,
1005+
radiusX: 2.5,
1006+
radiusY: 2.5,
1007+
rotationAngle: 10,
1008+
force: 0.5,
1009+
});
1010+
1011+
const touchEvent = new TouchEvent('touchmove', {
1012+
cancelable: true,
1013+
bubbles: true,
1014+
touches: [touchObj],
1015+
targetTouches: [],
1016+
changedTouches: [touchObj],
1017+
shiftKey: true,
1018+
});
1019+
1020+
node.dispatchEvent(touchEvent);
1021+
}
1022+
9541023

955-
function simulateMovementFromTo(drag, fromX, fromY, toX, toY) {
1024+
function simulateMouseFromTo(drag, fromX, fromY, toX, toY) {
9561025
const node = ReactDOM.findDOMNode(drag);
9571026

958-
TestUtils.Simulate.mouseDown(node, {clientX: fromX, clientY: fromY});
1027+
TestUtils.Simulate.mouseDown(node, createClientXY(fromX, fromY));
9591028
mouseMove(toX, toY, node);
9601029
TestUtils.Simulate.mouseUp(node);
9611030
}
9621031

1032+
// // Does not work, cannot figure out how to correctly simulate touches
1033+
// function simulateTouchFromTo(drag, fromX, fromY, toX, toY) {
1034+
// const node = ReactDOM.findDOMNode(drag);
1035+
1036+
// TestUtils.Simulate.touchStart(node, { touches: [createClientXY(fromX, fromY)] });
1037+
// touchMove(toX, toY, node);
1038+
// TestUtils.Simulate.touchEnd(node, { touches: [createClientXY(toX, toY)], changedTouches: [createClientXY(toX, toY)]});
1039+
// }
1040+
9631041
function fragmentFromString(strHTML) {
9641042
var temp = document.createElement('div');
9651043
temp.innerHTML = strHTML;

0 commit comments

Comments
 (0)