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
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export class VisualElementDragControls {

if (
dragSnapToOrigin === true ||
dragSnapToOrigin === axis
(dragSnapToOrigin as unknown) === axis
Copy link

Choose a reason for hiding this comment

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

Over-broad TypeScript cast

Using as unknown fully erases type information and bypasses all TypeScript safety checks. The root cause is that getProps() returns MotionProps (from framer-motion's own types), but the type for dragSnapToOrigin in that interface appears to be narrower than the full boolean | "x" | "y" union defined in motion-dom/src/node/types.ts.

A more precise cast — or better yet, aligning MotionProps with the upstream type — would be safer:

Suggested change
(dragSnapToOrigin as unknown) === axis
(dragSnapToOrigin as boolean | "x" | "y") === axis

If the mismatch is in MotionProps, the proper long-term fix is to ensure MotionProps.dragSnapToOrigin is typed as boolean | "x" | "y", which removes the need for a cast entirely.

)
transition = { min: 0, max: 0 }

Expand Down
24 changes: 24 additions & 0 deletions packages/framer-motion/src/motion/__tests__/component-svg.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,30 @@ describe("SVG", () => {
)
})

test("MotionValue can be used for transform attribute on g element", async () => {
const Component = () => {
const transformValue = useMotionValue("translate(50, 50)")

return (
<svg>
<motion.g transform={transformValue as any}>
<motion.rect width={50} height={50} />
</motion.g>
</svg>
)
}

const { container } = render(<Component />)

await nextFrame()

const gElement = container.querySelector("g")!
// The transform should NOT be rendered as "[object Object]"
expect(gElement.getAttribute("transform")).not.toBe("[object Object]")
Copy link

Choose a reason for hiding this comment

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

Weak negative assertion

The assertion not.toBe("[object Object]") passes trivially if getAttribute("transform") returns null (which it will when the attribute is absent — the expected, correct behavior after filtering). Consider replacing it with a positive assertion that explicitly documents what the value should be:

Suggested change
expect(gElement.getAttribute("transform")).not.toBe("[object Object]")
expect(gElement.getAttribute("transform")).toBeNull()

This would make the test self-documenting: after the fix, filterProps drops the MotionValue, so the transform DOM attribute should not be set at all (the value is applied via CSS transform instead).

// It should be applied as a CSS style transform
expect(gElement).toHaveStyle("transform: translate(50, 50)")
})

test("animates viewBox", async () => {
const Component = () => {
return (
Expand Down
3 changes: 3 additions & 0 deletions packages/framer-motion/src/render/dom/utils/filter-props.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isMotionValue } from "motion-dom"
import type { MotionProps } from "../../../motion/types"
import { isValidMotionProp } from "../../../motion/utils/valid-prop"

Expand Down Expand Up @@ -58,6 +59,8 @@ export function filterProps(
*/
if (key === "values" && typeof props.values === "object") continue

if (isMotionValue(props[key as keyof typeof props])) continue

if (
shouldForward(key) ||
(forwardMotionProps === true && isValidMotionProp(key)) ||
Expand Down