|
1 | 1 | import { fireEvent } from "@testing-library/react" |
2 | | -import { motion } from "framer-motion" |
3 | 2 | import * as React from "react" |
| 3 | +import { motion } from "../../" |
4 | 4 | import { render } from "../../jest.setup" |
5 | 5 |
|
6 | 6 | describe("motion component rendering and styles", () => { |
@@ -310,38 +310,78 @@ describe("motion component rendering and styles", () => { |
310 | 310 | }) |
311 | 311 |
|
312 | 312 | it("layout animations interrupt jump", async () => { |
313 | | - const promise = new Promise((r)=>{ |
314 | | - const Component = ()=>{ |
315 | | - const [open,setOpen] = React.useState(false) |
| 313 | + const promise = new Promise((r) => { |
| 314 | + const Component = () => { |
| 315 | + const [open, setOpen] = React.useState(false) |
316 | 316 | const divRef = React.useRef<HTMLDivElement>(null) |
317 | | - async function handleLayoutJump(){ |
| 317 | + async function handleLayoutJump() { |
318 | 318 | setOpen(true) |
319 | | - await new Promise(resolve => setTimeout(resolve, 1500)) |
320 | | - const firstSize = divRef.current?.getBoundingClientRect().width||0 |
| 319 | + await new Promise((resolve) => setTimeout(resolve, 1500)) |
| 320 | + const firstSize = |
| 321 | + divRef.current?.getBoundingClientRect().width || 0 |
321 | 322 | setOpen(false) |
322 | | - const secondSize = divRef.current?.getBoundingClientRect().width||0 |
| 323 | + const secondSize = |
| 324 | + divRef.current?.getBoundingClientRect().width || 0 |
323 | 325 | r(Math.abs(firstSize - secondSize)) |
324 | 326 | } |
325 | | - React.useEffect(()=>{ |
| 327 | + React.useEffect(() => { |
326 | 328 | handleLayoutJump() |
327 | | - },[]) |
328 | | - return <motion.div layout="size"> |
329 | | - <motion.div |
330 | | - layout="size" |
331 | | - ref={divRef} |
332 | | - style={{ width:open? "200px" : "50px" }} |
333 | | - transition={{ |
334 | | - layout: { |
335 | | - duration: 2, |
336 | | - ease: "linear", |
337 | | - }, |
338 | | - }} |
339 | | - /> |
340 | | - </motion.div> |
| 329 | + }, []) |
| 330 | + return ( |
| 331 | + <motion.div layout="size"> |
| 332 | + <motion.div |
| 333 | + layout="size" |
| 334 | + ref={divRef} |
| 335 | + style={{ width: open ? "200px" : "50px" }} |
| 336 | + transition={{ |
| 337 | + layout: { |
| 338 | + duration: 2, |
| 339 | + ease: "linear", |
| 340 | + }, |
| 341 | + }} |
| 342 | + /> |
| 343 | + </motion.div> |
| 344 | + ) |
341 | 345 | } |
342 | 346 | render(<Component />) |
343 | 347 | }) |
344 | 348 |
|
345 | 349 | expect(promise).resolves.toBeLessThan(50) |
346 | 350 | }) |
| 351 | + |
| 352 | + it("handles ref swapping correctly", () => { |
| 353 | + let ref1Element: HTMLDivElement | null = null |
| 354 | + let ref2Element: HTMLDivElement | null = null |
| 355 | + |
| 356 | + const Component = ({ useRef2 }: { useRef2: boolean }) => { |
| 357 | + const ref1 = React.useRef<HTMLDivElement>(null) |
| 358 | + const ref2 = React.useRef<HTMLDivElement>(null) |
| 359 | + |
| 360 | + const currentRef = useRef2 ? ref2 : ref1 |
| 361 | + |
| 362 | + React.useEffect(() => { |
| 363 | + // Capture ref values after each render |
| 364 | + ref1Element = ref1.current |
| 365 | + ref2Element = ref2.current |
| 366 | + }) |
| 367 | + |
| 368 | + return <motion.div ref={currentRef} data-testid="ref-element" /> |
| 369 | + } |
| 370 | + |
| 371 | + // Initial render with ref1 |
| 372 | + const { rerender, getByTestId } = render(<Component useRef2={false} />) |
| 373 | + |
| 374 | + // Verify ref1 is populated, ref2 is null |
| 375 | + expect(ref1Element).toBeTruthy() |
| 376 | + expect(ref2Element).toBeNull() |
| 377 | + expect(ref1Element).toBe(getByTestId("ref-element")) |
| 378 | + |
| 379 | + // Re-render with ref2 (swap refs) |
| 380 | + rerender(<Component useRef2={true} />) |
| 381 | + |
| 382 | + // Verify ref2 is now populated, ref1 is null |
| 383 | + expect(ref1Element).toBeNull() |
| 384 | + expect(ref2Element).toBeTruthy() |
| 385 | + expect(ref2Element).toBe(getByTestId("ref-element")) |
| 386 | + }) |
347 | 387 | }) |
0 commit comments