Skip to content
Closed
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
70 changes: 70 additions & 0 deletions dev/html/public/animate-layout/basic-position-change.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
}

#box {
width: 100px;
height: 100px;
background-color: #00cc88;
}

#box.moved {
position: absolute;
top: 100px;
left: 200px;
}

[data-layout-correct="false"] {
background: #dd1144 !important;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="box" data-layout></div>

<script type="module" src="/src/imports/animate-layout.js"></script>
<script type="module" src="/src/imports/script-assert.js"></script>
<script type="module">
const { animateLayout, frame } = window.AnimateLayout
const { matchViewportBox, showError } = window.Assert
const box = document.getElementById("box")

const boxOrigin = box.getBoundingClientRect()

async function runTest() {
// Target position from .moved class
const targetTop = 100
const targetLeft = 200

const controls = await animateLayout(
() => {
box.classList.add("moved")
},
{ duration: 10, ease: "linear" }
)

// Pause at halfway
controls.pause()
controls.time = controls.duration * 0.5

// Wait one frame for the animation to apply
await new Promise((resolve) => frame.postRender(resolve))

// At halfway, the box should be at the midpoint between origin and target
const expectedHalfway = {
top: 50,
left: 100,
}

matchViewportBox(box, expectedHalfway)
}

runTest().catch(console.error)
</script>
</body>
</html>
94 changes: 94 additions & 0 deletions dev/html/public/animate-layout/enter-animation-scoped.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
}

#container {
width: 300px;
height: 300px;
background-color: #f0f0f0;
padding: 20px;
}

.card {
width: 100px;
height: 100px;
background-color: #00cc88;
margin: 10px;
}

#outside-element {
width: 50px;
height: 50px;
background-color: purple;
position: fixed;
top: 10px;
right: 10px;
}

[data-layout-correct="false"] {
background: #dd1144 !important;
opacity: 0.5;
}
</style>
</head>
<body>
<!-- Element outside the scoped container - should NOT animate -->
<div data-layout id="outside-element"></div>
<div id="container"></div>

<script type="module" src="/src/imports/animate-layout.js"></script>
<script type="module" src="/src/imports/script-assert.js"></script>
<script type="module">
const { animateLayout, frame } = window.AnimateLayout
const container = document.getElementById("container")
const outsideElement = document.getElementById("outside-element")

async function runTest() {
// Add elements with enter animation, scoped to container
// The outside-element should NOT be affected
const controls = await animateLayout(
container,
() => {
container.innerHTML = `
<div class="card" data-layout id="card1"></div>
`
},
{ duration: 10, ease: () => 0.5 }
).enter({ opacity: 1 }, { duration: 10, ease: () => 0.5 })

// Wait one frame for the animation to apply
await new Promise((resolve) => frame.postRender(resolve))

const card1 = document.getElementById("card1")

// card1 should be fading in (opacity < 1)
const card1Opacity = parseFloat(
window.getComputedStyle(card1).opacity
)
if (card1Opacity >= 1) {
window.showError(
card1,
`Expected card1 opacity < 1 during animation, got ${card1Opacity}`
)
}

// outside-element should NOT be affected - opacity should be 1
const outsideOpacity = parseFloat(
window.getComputedStyle(outsideElement).opacity
)
if (outsideOpacity !== 1) {
window.showError(
outsideElement,
`Expected outside element opacity = 1 (not in scope), got ${outsideOpacity}`
)
}
}

runTest().catch(console.error)
</script>
</body>
</html>
88 changes: 88 additions & 0 deletions dev/html/public/animate-layout/enter-animation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
}

#container {
width: 300px;
height: 300px;
background-color: #f0f0f0;
padding: 20px;
}

.card {
width: 100px;
height: 100px;
background-color: #00cc88;
margin: 10px;
}

[data-layout-correct="false"] {
background: #dd1144 !important;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="container"></div>

<script type="module" src="/src/imports/animate-layout.js"></script>
<script type="module" src="/src/imports/script-assert.js"></script>
<script type="module">
const { animateLayout, frame } = window.AnimateLayout
const { matchOpacity } = window.Assert
const container = document.getElementById("container")

async function runTest() {
// Add elements with enter animation
// card2 has a nested data-layout child which should NOT animate opacity
const controls = await animateLayout(
container,
() => {
container.innerHTML = `
<div class="card" data-layout id="card1"></div>
<div class="card" data-layout id="card2">
<div data-layout id="nested-child" style="width: 50px; height: 50px; background: blue;"></div>
</div>
`
},
{ duration: 10, ease: () => 0.5 }
).enter({ opacity: 1 }, { duration: 10, ease: () => 0.5 })

// Wait one frame for the animation to apply
await new Promise((resolve) => frame.postRender(resolve))

const card1 = document.getElementById("card1")
const card2 = document.getElementById("card2")
const nestedChild = document.getElementById("nested-child")

// Root entering elements (card1, card2) should be fading in (opacity < 1)
const card1Opacity = parseFloat(
window.getComputedStyle(card1).opacity
)
if (card1Opacity >= 1) {
window.showError(
card1,
`Expected card1 opacity < 1 during animation, got ${card1Opacity}`
)
}

// Nested child should NOT animate opacity - it should be 1
const nestedOpacity = parseFloat(
window.getComputedStyle(nestedChild).opacity
)
if (nestedOpacity !== 1) {
window.showError(
nestedChild,
`Expected nested child opacity = 1 (no animation), got ${nestedOpacity}`
)
}
}

runTest().catch(console.error)
</script>
</body>
</html>
117 changes: 117 additions & 0 deletions dev/html/public/animate-layout/exit-animation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
}

#container {
width: 300px;
height: 300px;
background-color: #f0f0f0;
padding: 20px;
}

.card {
width: 100px;
height: 100px;
background-color: #00cc88;
margin: 10px;
}

[data-layout-correct="false"] {
background: #dd1144 !important;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="container">
<div
class="card"
data-layout
data-layout-correct="true"
id="card1"
></div>
</div>

<script type="module" src="/src/imports/animate-layout.js"></script>
<script type="module" src="/src/imports/script-assert.js"></script>
<script type="module">
const { animateLayout, frame } = window.AnimateLayout
const container = document.getElementById("container")
const card1 = document.getElementById("card1")

const cardOrigin = card1.getBoundingClientRect()

async function runTest() {
// Remove element with exit animation (don't await - we need controls before it finishes)
const controls = await animateLayout(
container,
() => {
container.innerHTML = ""
},
{ duration: 10, ease: "linear" }
).exit({ opacity: 0 }, { duration: 10, ease: "linear" })

// Pause and seek to 75% (opacity should be 0.25)
controls.pause()
controls.time = controls.duration * 0.75

// Card should still be in DOM (reattached for exit animation)
if (!card1.isConnected) {
window.showError(
container,
"Card should still be connected during exit animation"
)
return
}

// // Card should be in absolute position at original location
const currentBounds = card1.getBoundingClientRect()
const threshold = 1 // pixel tolerance

if (
Math.abs(cardOrigin.top - currentBounds.top) > threshold ||
Math.abs(cardOrigin.left - currentBounds.left) > threshold
) {
window.showError(
card1,
`Card position shifted during exit: expected (${cardOrigin.left}, ${cardOrigin.top}), got (${currentBounds.left}, ${currentBounds.top})`
)
}

// Check opacity is around 0.25 (75% of the way from 1 to 0)
// This verifies the animation is going in the exit direction (toward 0)
const opacity = parseFloat(
window.getComputedStyle(card1).opacity
)
const expectedOpacity = 0.25
const opacityThreshold = 0.1

if (Math.abs(opacity - expectedOpacity) > opacityThreshold) {
window.showError(
card1,
`Expected opacity ~${expectedOpacity} during exit, got ${opacity}`
)
}

// Manually complete the animation
controls.complete()

requestAnimationFrame(() => {
if (card1.isConnected) {
window.showError(
container,
"Card should not be connected after exit animation"
)
return
}
})
}

runTest().catch(console.error)
</script>
</body>
</html>
Loading