Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Motion adheres to [Semantic Versioning](http://semver.org/).

Undocumented APIs should be considered internal and may change without warning.

## [12.30.1] 2026-02-03

### Fixed

- Allow drag to be initiated by child `a` and `button` elements.

## [12.30.0] 2026-02-02

### Added
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ test-e2e: test-nextjs test-html test-react test-react-19
yarn test-playwright

test-single: build test-mkdir
yarn start-server-and-test "yarn dev-server" http://localhost:9991 "cd packages/framer-motion && cypress run --config-file=cypress.react-19.json --headed --spec cypress/integration/unit-conversion.ts"
yarn start-server-and-test "yarn dev-server" http://localhost:9991 "cd packages/framer-motion && cypress run --config-file=cypress.react.json --headed --spec cypress/integration/drag-nested.ts"

lint: bootstrap
yarn lint
Expand Down
8 changes: 4 additions & 4 deletions dev/html/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "html-env",
"private": true,
"version": "12.30.0",
"version": "12.30.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -10,9 +10,9 @@
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^12.30.0",
"motion": "^12.30.0",
"motion-dom": "^12.30.0",
"framer-motion": "^12.30.1",
"motion": "^12.30.1",
"motion-dom": "^12.30.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
4 changes: 2 additions & 2 deletions dev/next/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "next-env",
"private": true,
"version": "12.30.0",
"version": "12.30.1",
"type": "module",
"scripts": {
"dev": "next dev",
Expand All @@ -10,7 +10,7 @@
"build": "next build"
},
"dependencies": {
"motion": "^12.30.0",
"motion": "^12.30.1",
"next": "15.4.10",
"react": "19.0.0",
"react-dom": "19.0.0"
Expand Down
4 changes: 2 additions & 2 deletions dev/react-19/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-19-env",
"private": true,
"version": "12.30.0",
"version": "12.30.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"motion": "^12.30.0",
"motion": "^12.30.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
Expand Down
4 changes: 2 additions & 2 deletions dev/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-env",
"private": true,
"version": "12.30.0",
"version": "12.30.1",
"type": "module",
"scripts": {
"dev": "yarn vite",
Expand All @@ -11,7 +11,7 @@
"preview": "yarn vite preview"
},
"dependencies": {
"framer-motion": "^12.30.0",
"framer-motion": "^12.30.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "12.30.0",
"version": "12.30.1",
"packages": [
"packages/*",
"dev/*"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/**
* Tests for issue #1674: Interactive elements inside draggable elements should not trigger drag
* https://github.com/motiondivision/motion/issues/1674
* Tests for drag behavior with interactive elements inside draggable elements.
*
* Form controls where text selection or direct interaction is expected
* (input, textarea, select, contenteditable) should NOT trigger drag.
*
* Buttons and links SHOULD allow drag since they don't have click-and-move
* actions of their own.
*/
describe("Drag Input Propagation", () => {
it("Should not drag when clicking and dragging on an input inside draggable", () => {
Expand Down Expand Up @@ -60,7 +65,7 @@ describe("Drag Input Propagation", () => {
})
})

it("Should not drag when clicking and dragging on a button inside draggable", () => {
it("Should drag when clicking and dragging on a button inside draggable", () => {
cy.visit("?test=drag-input-propagation")
.wait(200)
.get("[data-testid='draggable']")
Expand All @@ -70,7 +75,7 @@ describe("Drag Input Propagation", () => {
expect(top).to.equal(100)
})

// Attempt to drag by clicking on the button
// Drag by clicking on the button - buttons don't have click-and-move actions
cy.get("[data-testid='button']")
.trigger("pointerdown", 5, 5)
.trigger("pointermove", 10, 10)
Expand All @@ -79,15 +84,16 @@ describe("Drag Input Propagation", () => {
.wait(50)
.trigger("pointerup", { force: true })

// Verify the draggable element did NOT move
// Verify the draggable element DID move
cy.get("[data-testid='draggable']").should(($draggable) => {
const { left, top } = $draggable[0].getBoundingClientRect()
expect(left).to.equal(100)
expect(top).to.equal(100)
// Element should have moved
expect(left).to.be.greaterThan(200)
expect(top).to.be.greaterThan(200)
})
})

it("Should not drag when clicking and dragging on a link inside draggable", () => {
it("Should drag when clicking and dragging on a link inside draggable", () => {
cy.visit("?test=drag-input-propagation")
.wait(200)
.get("[data-testid='draggable']")
Expand All @@ -97,7 +103,7 @@ describe("Drag Input Propagation", () => {
expect(top).to.equal(100)
})

// Attempt to drag by clicking on the link
// Drag by clicking on the link - links don't have click-and-move actions
cy.get("[data-testid='link']")
.trigger("pointerdown", 5, 5)
.trigger("pointermove", 10, 10)
Expand All @@ -106,11 +112,12 @@ describe("Drag Input Propagation", () => {
.wait(50)
.trigger("pointerup", { force: true })

// Verify the draggable element did NOT move
// Verify the draggable element DID move
cy.get("[data-testid='draggable']").should(($draggable) => {
const { left, top } = $draggable[0].getBoundingClientRect()
expect(left).to.equal(100)
expect(top).to.equal(100)
// Element should have moved
expect(left).to.be.greaterThan(200)
expect(top).to.be.greaterThan(200)
})
})

Expand Down
4 changes: 2 additions & 2 deletions packages/framer-motion/cypress/integration/drag-nested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,9 @@ function testAlternateAxes(parentLayout: boolean, childLayout: boolean) {
.wait(200)
.get("#child")
.trigger("pointerdown", 5, 5, { force: true })
.wait(50)
.wait(80)
.trigger("pointermove", 10, 10, { force: true })
.wait(50)
.wait(80)
.trigger("pointermove", 100, 100, { force: true })
.wait(80)
.should(([$child]: any) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/framer-motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion",
"version": "12.30.0",
"version": "12.30.1",
"description": "A simple and powerful JavaScript animation library",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -88,7 +88,7 @@
"measure": "rollup -c ./rollup.size.config.mjs"
},
"dependencies": {
"motion-dom": "^12.30.0",
"motion-dom": "^12.30.1",
"motion-utils": "^12.29.2",
"tslib": "^2.4.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
createBox,
eachAxis,
frame,
isElementKeyboardAccessible,
isElementTextInput,
measurePageBox,
mixNumber,
PanInfo,
Expand Down Expand Up @@ -655,15 +655,17 @@ export class VisualElementDragControls {
const target = event.target as Element

/**
* Only block drag if clicking on a keyboard-accessible child element.
* If the draggable element itself is keyboard-accessible (e.g., motion.button),
* dragging should still work when clicking directly on it.
* Only block drag if clicking on a text input child element
* (input, textarea, select, contenteditable) where users might
* want to select text or interact with the control.
*
* Buttons and links don't block drag since they don't have
* click-and-move actions of their own.
*/
const isClickingKeyboardAccessibleChild =
target !== element &&
isElementKeyboardAccessible(target)
const isClickingTextInputChild =
target !== element && isElementTextInput(target)

if (drag && dragListener && !isClickingKeyboardAccessibleChild) {
if (drag && dragListener && !isClickingTextInputChild) {
this.start(event)
}
}
Expand Down
Loading
Loading