Skip to content

Commit 067fbdb

Browse files
tnrichSTRML
andauthored
fix: add allowMobileScroll prop to allow for clicks to optionally pass through on mobile (#760)
* fix: add doNotPreventMobileScroll prop to allow for clicks to optionally pass through on mobile * fix: switch doNotPreventMobileScroll to allowMobileScroll * fix: update prop in draggable core to allowMobileScroll * chore(allowMobileScroll): update readme and tests --------- Co-authored-by: Samuel Reed <[email protected]>
1 parent 2c3aa84 commit 067fbdb

File tree

5 files changed

+41
-3
lines changed

5 files changed

+41
-3
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ type DraggableData = {
164164
// If set to `true`, will allow dragging on non left-button clicks.
165165
allowAnyClick: boolean,
166166

167+
// Default `false` and default behavior before 4.5.0.
168+
// If set to `true`, the 'touchstart' event will not be prevented,
169+
// which will allow scrolling inside containers. We recommend
170+
// using the 'handle' / 'cancel' props when possible instead of enabling this.
171+
//
172+
// See https://github.com/react-grid-layout/react-draggable/issues/728
173+
allowMobileScroll: boolean,
174+
167175
// Determines which axis the draggable can move. This only affects
168176
// flushing to the DOM. Callbacks will still include all values.
169177
// Accepted values:
@@ -202,6 +210,9 @@ defaultPosition: {x: number, y: number},
202210
// If true, will not call any drag handlers.
203211
disabled: boolean,
204212

213+
// Default `true`. Adds "user-select: none" while dragging to avoid selecting text.
214+
enableUserSelectHack: boolean,
215+
205216
// Specifies the x and y that dragging should snap to.
206217
grid: [number, number],
207218

@@ -324,6 +335,7 @@ on itself and thus must have callbacks attached to be useful.
324335
```js
325336
{
326337
allowAnyClick: boolean,
338+
allowMobileScroll: boolean,
327339
cancel: string,
328340
disabled: boolean,
329341
enableUserSelectHack: boolean,

lib/DraggableCore.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type PositionOffsetControlPosition = {x: number|string, y: number|string}
4242

4343
export type DraggableCoreDefaultProps = {
4444
allowAnyClick: boolean,
45+
allowMobileScroll: boolean,
4546
disabled: boolean,
4647
enableUserSelectHack: boolean,
4748
onStart: DraggableEventHandler,
@@ -81,6 +82,15 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {
8182
*/
8283
allowAnyClick: PropTypes.bool,
8384

85+
/**
86+
* `allowMobileScroll` turns off cancellation of the 'touchstart' event
87+
* on mobile devices. Only enable this if you are having trouble with click
88+
* events. Prefer using 'handle' / 'cancel' instead.
89+
*
90+
* Defaults to `false`.
91+
*/
92+
allowMobileScroll: PropTypes.bool,
93+
8494
children: PropTypes.node.isRequired,
8595

8696
/**
@@ -213,6 +223,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {
213223

214224
static defaultProps: DraggableCoreDefaultProps = {
215225
allowAnyClick: false, // by default only accept left click
226+
allowMobileScroll: false,
216227
disabled: false,
217228
enableUserSelectHack: true,
218229
onStart: function(){},
@@ -288,7 +299,7 @@ export default class DraggableCore extends React.Component<DraggableCoreProps> {
288299

289300
// Prevent scrolling on mobile devices, like ipad/iphone.
290301
// Important that this is after handle/cancel.
291-
if (e.type === 'touchstart') e.preventDefault();
302+
if (e.type === 'touchstart' && !this.props.allowMobileScroll) e.preventDefault();
292303

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

specs/draggable.spec.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,18 @@ describe('react-draggable', function () {
622622
assert.equal(drag.state.dragging, true);
623623
});
624624

625+
it('should *not* call preventDefault on touchStart event if "allowMobileScroll"', function () {
626+
drag = TestUtils.renderIntoDocument(<Draggable allowMobileScroll={true}><div/></Draggable>);
627+
628+
const e = new Event('touchstart');
629+
// Oddly `e.defaultPrevented` is not changing here. Maybe because we're not mounted to a real doc?
630+
let pdCalled = false;
631+
e.preventDefault = function() { pdCalled = true; };
632+
ReactDOM.findDOMNode(drag).dispatchEvent(e);
633+
assert(!pdCalled);
634+
assert.equal(drag.state.dragging, true);
635+
});
636+
625637
it('should not call preventDefault on touchStart event if not on handle', function () {
626638
drag = TestUtils.renderIntoDocument(
627639
<Draggable handle=".handle">

typings/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ declare module 'react-draggable' {
4242

4343
export interface DraggableCoreProps {
4444
allowAnyClick: boolean,
45+
allowMobileScroll: boolean,
4546
cancel: string,
4647
children?: React.ReactNode,
4748
disabled: boolean,

typings/test.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ ReactDOM.render(
2020
onDrag={handleDrag}
2121
onStop={handleStop}
2222
offsetParent={document.body}
23-
allowAnyClick={true}
2423
onMouseDown={handleMouseDown}
24+
allowAnyClick={true}
25+
allowMobileScroll={false}
2526
disabled={true}
2627
enableUserSelectHack={false}
2728
bounds={false}
@@ -54,7 +55,8 @@ ReactDOM.render(
5455
onDrag={handleDrag}
5556
onStop={handleStop}
5657
offsetParent={document.body}
57-
enableUserSelectHack={false}>
58+
enableUserSelectHack={false}
59+
allowMobileScroll={false}>
5860
<div className="foo bar" ref={nodeRefCore}>
5961
<div className="handle"/>
6062
<div className="cancel"/>

0 commit comments

Comments
 (0)