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

added preventDefault prop #581

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "react-draggable",
"version": "4.4.3",
"version": "4.4.4",
"description": "React draggable component",
"main": "build/cjs/cjs.js",
"unpkg": "build/web/react-draggable.min.js",
Expand Down Expand Up @@ -96,4 +96,4 @@
"react": ">= 16.3.0",
"react-dom": ">= 16.3.0"
}
}
}
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