Skip to content

fix(DragDropSort): enable mobile drag-and-drop support#12418

Open
logonoff wants to merge 1 commit intopatternfly:mainfrom
logonoff:12415-touch-sensor
Open

fix(DragDropSort): enable mobile drag-and-drop support#12418
logonoff wants to merge 1 commit intopatternfly:mainfrom
logonoff:12415-touch-sensor

Conversation

@logonoff
Copy link
Copy Markdown
Member

@logonoff logonoff commented May 7, 2026

What: Closes #12415

Three changes to enable mobile drag-and-drop:

  1. Draggable: Set touch-action: none on the drag activator element — on DragButton when useDragButton is true (so only the handle blocks native scrolling), on the wrapper div otherwise. Without this, mobile browsers fire pointercancel before the PointerSensor can activate.

  2. DragDropContainer: Add activationConstraint: { distance: 8 } to PointerSensor so the drag only activates after 8px of deliberate pointer movement, preventing accidental drags on both desktop and mobile.

  3. DragDropContainer: Add restrictToWindowEdges modifier to DndContext so dragged items cannot be moved outside the browser viewport. Placed before {...props} so consumers can override via the modifiers prop.

Additional issues: N/A

Summary by CodeRabbit

  • New Features
    • Drag now requires a small deliberate pointer movement to start, reducing accidental drags on touch devices.
    • Drag movement is constrained to the visible window, preventing items from being dragged off-screen.
  • Bug Fixes / Improvements
    • Touch gesture handling improved (touchAction applied) to prevent unintended scrolling during drag interactions, including when using the drag handle/button.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Walkthrough

Registers PointerSensor with activationConstraint { distance: 8 }; adds restrictToWindowEdges to DndContext; applies touchAction: 'none' to draggable outer element when not using a drag button and to DragButton when used.

Changes

Drag behavior and input handling

Layer / File(s) Summary
Sensor configuration
packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx
PointerSensor registration in useSensors now includes activationConstraint: { distance: 8 }.
DndContext modifier
packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx
DndContext now includes modifiers={[restrictToWindowEdges]} to clamp drag movement to the window edges.
Draggable outer element style
packages/react-drag-drop/src/components/DragDrop/Draggable.tsx
Outer element style keeps transform/transition and conditionally adds touchAction: 'none' when useDragButton is false.
DragButton style prop
packages/react-drag-drop/src/components/DragDrop/Draggable.tsx
When useDragButton is true, DragButton is rendered with style={{ touchAction: 'none' }}.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • thatblindgeye
  • nicolethoen
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: enabling mobile drag-and-drop support through PointerSensor activation constraints and touch-action CSS modifications.
Linked Issues check ✅ Passed The code changes directly address issue #12415 by implementing touch-action: none styling and PointerSensor activation distance constraints to enable mobile drag-and-drop functionality.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked issue: DragDropContainer adds PointerSensor constraints and restrictToWindowEdges modifier, while Draggable adds touch-action CSS. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx (1)

106-112: ⚠️ Potential issue | 🔴 Critical

Remove redundant TouchSensor or switch to MouseSensor + TouchSensor; PointerSensor + TouchSensor combination is unsupported and causes duplicate event handling.

The code registers both PointerSensor and TouchSensor. According to dnd-kit documentation, this combination should not be used as both sensors overlap on touch input.

The correct approach is one of the following:

  1. Modern approach (recommended): Use PointerSensor alone with activation constraints:

    const sensors = useSensors(
      useSensor(PointerSensor, {
        activationConstraint: {
          delay: 250,
          tolerance: 5
        }
      }),
      useSensor(KeyboardSensor, {
        coordinateGetter: sortableKeyboardCoordinates
      })
    );
  2. Legacy approach: If PointerSensor does not work for your use case, replace it with MouseSensor and keep TouchSensor with activation constraints:

    const sensors = useSensors(
      useSensor(MouseSensor),
      useSensor(TouchSensor, {
        activationConstraint: {
          delay: 250,
          tolerance: 5
        }
      }),
      useSensor(KeyboardSensor, {
        coordinateGetter: sortableKeyboardCoordinates
      })
    );

PointerSensor is the unified replacement for Mouse and Touch APIs and handles all pointer input types. TouchSensor is legacy from dnd-kit v1 and is redundant when PointerSensor is present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx`
around lines 106 - 112, The sensors array currently registers both PointerSensor
and TouchSensor which overlap on touch input; remove the TouchSensor and either
(recommended) keep PointerSensor with an activationConstraint or (legacy)
replace PointerSensor with MouseSensor and keep TouchSensor with
activationConstraint; update the useSensors call (symbols: useSensors,
useSensor, PointerSensor, TouchSensor, MouseSensor, KeyboardSensor,
sortableKeyboardCoordinates) to use one of the two approaches and add
activationConstraint (delay/tolerance) to the pointer/mouse or touch sensor as
appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx`:
- Around line 106-112: The sensors array currently registers both PointerSensor
and TouchSensor which overlap on touch input; remove the TouchSensor and either
(recommended) keep PointerSensor with an activationConstraint or (legacy)
replace PointerSensor with MouseSensor and keep TouchSensor with
activationConstraint; update the useSensors call (symbols: useSensors,
useSensor, PointerSensor, TouchSensor, MouseSensor, KeyboardSensor,
sortableKeyboardCoordinates) to use one of the two approaches and add
activationConstraint (delay/tolerance) to the pointer/mouse or touch sensor as
appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 835faa1f-7759-44dd-a3a2-10046563ea2f

📥 Commits

Reviewing files that changed from the base of the PR and between 4656548 and 8552fc4.

📒 Files selected for processing (1)
  • packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx

@patternfly-build
Copy link
Copy Markdown
Collaborator

patternfly-build commented May 7, 2026

@logonoff logonoff force-pushed the 12415-touch-sensor branch from 8552fc4 to fce3142 Compare May 7, 2026 19:00
@logonoff logonoff changed the title fix(DragDropSort): add TouchSensor for mobile drag-and-drop support fix(DragDropSort): enable mobile drag-and-drop support May 7, 2026
@logonoff logonoff force-pushed the 12415-touch-sensor branch from fce3142 to bb0b653 Compare May 7, 2026 19:19
Set touch-action: none on the drag activator element (DragButton when
useDragButton is true, wrapper div otherwise) to prevent the browser
from intercepting touch events for native scrolling. Without this,
mobile browsers fire pointercancel before the PointerSensor can
activate the drag.

Also adds activationConstraint: { distance: 8 } to PointerSensor so
the drag only activates after deliberate pointer movement, preventing
accidental drags on both desktop and mobile.

Adds restrictToWindowEdges modifier to DndContext so dragged items
cannot be moved outside the browser viewport. Consumers can override
this via the modifiers prop.

Fixes patternfly#12415
@logonoff logonoff force-pushed the 12415-touch-sensor branch from bb0b653 to d6db4ff Compare May 7, 2026 19:34
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-drag-drop/src/components/DragDrop/Draggable.tsx`:
- Around line 29-33: The current style object lets caller-provided props.style
overwrite drag-critical CSS (transform, transition, touchAction) because props
is spread after computed style; fix by ensuring computed drag styles take
precedence: when building the style object (the local variable named style in
Draggable.tsx), spread props.style first and then set transform:
CSS.Transform.toString(transform), transition, and ...( !useDragButton && {
touchAction: 'none' }), so the computed values override any caller values; apply
the same change to both places where style is constructed so
transform/transition/touchAction cannot be overridden by props.style.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 591c7d81-a876-42cf-ac64-b7c6d8266c38

📥 Commits

Reviewing files that changed from the base of the PR and between bb0b653 and d6db4ff.

📒 Files selected for processing (2)
  • packages/react-drag-drop/src/components/DragDrop/DragDropContainer.tsx
  • packages/react-drag-drop/src/components/DragDrop/Draggable.tsx

Comment on lines 29 to 33
const style = {
transform: CSS.Transform.toString(transform),
transition
transition,
...(!useDragButton && { touchAction: 'none' as const })
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether Draggable is currently used with an explicit style prop in this repository.
# If matches exist, those call sites are directly exposed to this override bug.
rg -nP --type=tsx '<Draggable\b[^>]*\bstyle\s*='
rg -nP --type=tsx 'items\s*=\s*{[\s\S]*?\bprops\s*:\s*{[\s\S]*?\bstyle\s*:' -U

Repository: patternfly/patternfly-react

Length of output: 1970


🏁 Script executed:

# Locate the Draggable.tsx file
find . -name "Draggable.tsx" -type f 2>/dev/null | head -20

Repository: patternfly/patternfly-react

Length of output: 207


🏁 Script executed:

# Once located, read the full component to see lines 29-53 and verify the structure
# This will help verify the spreading order concern
cat -n packages/react-drag-drop/src/components/DragDrop/Draggable.tsx 2>/dev/null || echo "File not found at expected path"

Repository: patternfly/patternfly-react

Length of output: 2155


🏁 Script executed:

# Search for DraggableProps definition
rg "interface DraggableProps|type DraggableProps" -A 10 2>/dev/null || echo "Definition not found with rg"

Repository: patternfly/patternfly-react

Length of output: 2294


🏁 Script executed:

# Search for Draggable component usages with explicit style prop
rg '<Draggable\b[^>]*\bstyle\s*=' --type=tsx --type=ts

Repository: patternfly/patternfly-react

Length of output: 99


🏁 Script executed:

# Also search in JSX/TSX for Draggable without explicit opening, to find all usages
rg 'Draggable' --type=tsx --type=ts -B 2 -A 5 | grep -A 7 '<Draggable'

Repository: patternfly/patternfly-react

Length of output: 99


🏁 Script executed:

# Search for Draggable component usages with style prop using globbing
rg '<Draggable\b[^>]*\bstyle\s*=' -g '*.tsx' -g '*.ts'

Repository: patternfly/patternfly-react

Length of output: 365


🏁 Script executed:

# Also search more broadly for any Draggable usage
rg '<Draggable' -g '*.tsx' -g '*.ts' -B 1 -A 3 | head -100

Repository: patternfly/patternfly-react

Length of output: 10297


🏁 Script executed:

# Check where Draggable is exported/imported from
rg "from.*Draggable|import.*Draggable" -g '*.tsx' -g '*.ts' | head -30

Repository: patternfly/patternfly-react

Length of output: 3263


🏁 Script executed:

# Check React.CSSProperties type usage in codebase for reference
rg "React\.CSSProperties" -g '*.tsx' -g '*.ts' | head -10

Repository: patternfly/patternfly-react

Length of output: 1146


🏁 Script executed:

# Check how other components handle style prop merging in this repo
rg "propStyle|styleProps|\.style" -g '*.tsx' -g '*.ts' -B 2 -A 2 | grep -A 4 "propStyle\|const.*style.*=" | head -40

Repository: patternfly/patternfly-react

Length of output: 3612


props.style can override drag-critical styles (including touchAction)

Because ...props is applied after style on Line 40 and Line 52, any caller-provided style replaces transform, transition, and touchAction. This breaks dragging and reintroduces the mobile failure this PR is fixing.

Suggested fix
 export const Draggable: React.FunctionComponent<DraggableProps> = ({
   children,
   id,
   className,
   useDragButton = false,
+  style: propStyle,
   ...props
 }: DraggableProps) => {
@@
-  const style = {
-    transform: CSS.Transform.toString(transform),
-    transition,
-    ...(!useDragButton && { touchAction: 'none' as const })
-  };
+  const style = {
+    ...(propStyle as React.CSSProperties),
+    transform: CSS.Transform.toString(transform),
+    transition,
+    ...(!useDragButton ? { touchAction: 'none' as const } : {})
+  };

Also applies to: 49-52

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-drag-drop/src/components/DragDrop/Draggable.tsx` around lines
29 - 33, The current style object lets caller-provided props.style overwrite
drag-critical CSS (transform, transition, touchAction) because props is spread
after computed style; fix by ensuring computed drag styles take precedence: when
building the style object (the local variable named style in Draggable.tsx),
spread props.style first and then set transform:
CSS.Transform.toString(transform), transition, and ...( !useDragButton && {
touchAction: 'none' }), so the computed values override any caller values; apply
the same change to both places where style is constructed so
transform/transition/touchAction cannot be overridden by props.style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug - DragDropSort - Doesn't work on mobile

2 participants