summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html')
-rw-r--r--testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html417
1 files changed, 417 insertions, 0 deletions
diff --git a/testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html b/testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html
new file mode 100644
index 0000000000..7996e48cea
--- /dev/null
+++ b/testing/web-platform/tests/scroll-animations/css/scroll-timeline-attachment.html
@@ -0,0 +1,417 @@
+<!DOCTYPE html>
+<title>Scroll Timeline Attachment</title>
+<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/7759">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+
+<main id=main></main>
+<script>
+ function inflate(t, template) {
+ t.add_cleanup(() => main.replaceChildren());
+ main.append(template.content.cloneNode(true));
+ main.offsetTop;
+ }
+
+ async function scrollTop(e, value) {
+ e.scrollTop = value;
+ await waitForNextFrame();
+ }
+</script>
+<style>
+ @keyframes anim {
+ from { width: 0px; --applied:true; }
+ to { width: 200px; --applied:true; }
+ }
+
+ .scroller {
+ overflow-y: hidden;
+ width: 200px;
+ height: 200px;
+ }
+ .scroller > .content {
+ margin: 400px 0px;
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+ .target {
+ background-color: coral;
+ width: 0px;
+ animation: anim auto linear;
+ animation-timeline: t1;
+ }
+ .timeline {
+ scroll-timeline-name: t1;
+ }
+ .local {
+ scroll-timeline-attachment: local;
+ }
+ .defer {
+ scroll-timeline-attachment: defer;
+ }
+ .ancestor {
+ scroll-timeline-attachment: ancestor;
+ }
+
+</style>
+
+
+<!-- Basic Behavior -->
+
+<template id=scroll_timeline_defer>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+ }, 'Descendant can attach to deferred timeline');
+</script>
+
+<template id=scroll_timeline_defer_no_attach>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer_no_attach);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ }, 'Deferred timeline with no attachments');
+</script>
+
+<template id=scroll_timeline_defer_no_attach_to_prev_sibling>
+ <div class="timeline defer">
+ <div class="scroller timeline">
+ <div class=content></div>
+ </div>
+ <div class=target>Test</div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer_no_attach_to_prev_sibling);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ }, 'Deferred timeline with no attachments to previous sibling');
+</script>
+
+<template id=scroll_timeline_local_ancestor>
+ <div class="scroller timeline local">
+ <div class=content>
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_local_ancestor);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+ }, 'Timeline with ancestor attachment does not attach to local');
+</script>
+
+<template id=scroll_timeline_defer_two_attachments>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ <!-- Extra attachment -->
+ <div class="timeline ancestor"></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer_two_attachments);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ }, 'Deferred timeline with two attachments');
+</script>
+
+<!-- Effective Axis of ScrollTimeline -->
+
+<template id=scroll_timeline_defer_axis>
+ <div class="timeline defer" style="scroll-timeline-axis:inline">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor" style="scroll-timeline-axis:vertical">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer_axis);
+ let target = main.querySelector('.target');
+ assert_equals(target.getAnimations().length, 1);
+ let anim = target.getAnimations()[0];
+ assert_not_equals(anim.timeline, null);
+ assert_equals(anim.timeline.axis, 'vertical');
+ }, 'Axis of deferred timeline is taken from attached timeline');
+</script>
+
+
+<template id=scroll_timeline_defer_axis_multiple>
+ <div class="timeline defer" style="scroll-timeline-axis:inline">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor" style="scroll-timeline-axis:vertical">
+ <div class=content></div>
+ </div>
+ <!-- Extra attachment -->
+ <div class="timeline ancestor"></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_defer_axis_multiple);
+ let target = main.querySelector('.target');
+ assert_equals(target.getAnimations().length, 1);
+ let anim = target.getAnimations()[0];
+ assert_not_equals(anim.timeline, null);
+ assert_equals(anim.timeline.axis, 'block');
+ }, 'Axis of deferred timeline with multiple attachments');
+</script>
+
+
+<!-- Dynamic Reattachment -->
+
+
+<template id=scroll_timeline_reattach>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ <div class="scroller timeline">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_reattach);
+ let scrollers = main.querySelectorAll('.scroller');
+ assert_equals(scrollers.length, 2);
+ let target = main.querySelector('.target');
+ await scrollTop(scrollers[0], 350); // 50%
+ await scrollTop(scrollers[1], 175); // 25%
+
+ // Attached to scrollers[0].
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ // Reattach to scrollers[1].
+ scrollers[0].classList.remove('ancestor');
+ scrollers[1].classList.add('ancestor');
+
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '50px'); // 0px => 200px, 25%
+ }, 'Dynamically re-attaching');
+</script>
+
+
+<template id=scroll_timeline_dynamic_attach_second>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline">
+ <div class=content></div>
+ </div>
+ <div class="scroller timeline">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_dynamic_attach_second);
+ let scrollers = main.querySelectorAll('.scroller');
+ assert_equals(scrollers.length, 2);
+ let target = main.querySelector('.target');
+ await scrollTop(scrollers[0], 350); // 50%
+ await scrollTop(scrollers[1], 175); // 25%
+
+ // Attached to no timelines initially:
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+
+ // Attach to scrollers[0].
+ scrollers[0].classList.add('ancestor');
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ // Also attach scrollers[1].
+ scrollers[1].classList.add('ancestor');
+
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ }, 'Dynamically attaching');
+</script>
+
+
+<template id=scroll_timeline_dynamic_detach_second>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_dynamic_detach_second);
+ let scrollers = main.querySelectorAll('.scroller');
+ assert_equals(scrollers.length, 2);
+ let target = main.querySelector('.target');
+ await scrollTop(scrollers[0], 350); // 50%
+ await scrollTop(scrollers[1], 175); // 25%
+
+ // Attached to two timelines initially:
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+
+ // Detach scrollers[1].
+ scrollers[1].classList.remove('ancestor');
+
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ // Also detach scrollers[0].
+ scrollers[0].classList.remove('ancestor');
+
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ }, 'Dynamically detaching');
+</script>
+
+<template id=scroll_timeline_ancestor_attached_removed>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_ancestor_attached_removed);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ let scroller_parent = scroller.parentElement;
+ scroller.remove();
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+
+ scroller_parent.append(scroller);
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+ }, 'Removing/inserting ancestor attached element');
+</script>
+
+<template id=scroll_timeline_ancestor_attached_display_none>
+ <div class="timeline defer">
+ <div class=target>Test</div>
+ <div class="scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_ancestor_attached_display_none);
+ let scroller = main.querySelector('.scroller');
+ let target = main.querySelector('.target');
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ scroller.style.display = 'none';
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '0px');
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+
+ scroller.style.display = 'block';
+ await scrollTop(scroller, 350); // 50%
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+ }, 'Ancestor attached element becoming display:none/block');
+</script>
+
+<template id=scroll_timeline_dynamic_defer>
+ <style>
+ .inner-scroller {
+ overflow-y: hidden;
+ width: 50px;
+ height: 50px;
+ }
+ .inner-scroller > .content {
+ margin: 100px 0px;
+ width: 20px;
+ height: 20px;
+ background-color: red;
+ }
+ </style>
+ <div class="scroller timeline">
+ <div class="target content">
+ <div class="inner-scroller timeline ancestor">
+ <div class=content></div>
+ </div>
+ </div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, scroll_timeline_dynamic_defer);
+ let target = main.querySelector('.target');
+ let outer_scroller = main.querySelector('.scroller');
+ let inner_scroller = main.querySelector('.inner-scroller');
+
+ await scrollTop(outer_scroller, 350); // 50%
+ await scrollTop(inner_scroller, 17); // 10%
+
+ // Attached to outer_scroller (local).
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+
+ // Effectively attached to inner_scroller.
+ outer_scroller.classList.add('defer');
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '20px'); // 0px => 200px, 10%
+
+ // Attached to outer_scroller again.
+ outer_scroller.classList.remove('defer');
+ await waitForNextFrame();
+ assert_equals(getComputedStyle(target).width, '100px'); // 0px => 200px, 50%
+ }, 'Dynamically becoming a deferred timeline');
+</script>