Measure carousel items against the padded focal size in browser tests
Plan step 32. Since containment.md C-12 the carousel defaults to M3's 16dp of content padding, and Compose's createShiftedKeylineListForContentPadding shrinks every keyline near the ends by a share of it, so a large item at rest at the start or the end is masked by 1.33px a side in the showcase. The tests asked for a zero inset there and failed; `open(i)` asks whether an item is at the largest size any item has at that position. The helper's list of showcase carousels gains the multi-aspect one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
9942e98fdd
commit
3434426e23
@@ -34,11 +34,16 @@ class CarouselMorphProbe extends Component
|
||||
|
||||
/**
|
||||
* A script run against one carousel of the showcase (0 multi-browse, 1 hero, 2 uncontained,
|
||||
* 3 full-screen), or of another page under `scope`, scrolled into view. `root`, `scroller` and
|
||||
* `items` are in scope, with `surface(i)`, `inset(i)` (the mask on each side of item i),
|
||||
* `snap(i)` (the scroll offset that brings item i into focus) and `at(i)`; the script's value is
|
||||
* the result. Pest retries a failing assertion and gives each attempt a second: keep scripts well
|
||||
* inside it.
|
||||
* 3 multi-aspect, 4 full-screen), or of another page under `scope`, scrolled into view. `root`,
|
||||
* `scroller` and `items` are in scope, with `surface(i)`, `inset(i)` (the mask on each side of
|
||||
* item i), `open(i)` (item i is at the largest size any item has here), `snap(i)` (the scroll
|
||||
* offset that brings item i into focus) and `at(i)`; the script's value is the result. Pest
|
||||
* retries a failing assertion and gives each attempt a second: keep scripts well inside it.
|
||||
*
|
||||
* `open(i)` rather than a zero inset: M3's 16dp of content padding, the carousel's default since
|
||||
* containment.md C-12, shrinks every keyline near the ends (Compose's
|
||||
* createShiftedKeylineListForContentPadding), so a large item resting at the start or the end is
|
||||
* masked by that share of the padding — 1.33px on each side in the showcase's first carousel.
|
||||
*/
|
||||
function onCarousel(int $index, string $body, string $scope = '#carousel'): string
|
||||
{
|
||||
@@ -51,6 +56,7 @@ function onCarousel(int $index, string $body, string $scope = '#carousel'): stri
|
||||
const size = parseFloat(root.style.getPropertyValue('--material-carousel-slot'))
|
||||
const surface = (i) => items[i].querySelector('[data-material-carousel-surface]')
|
||||
const inset = (i) => parseFloat(surface(i).style.getPropertyValue('--material-carousel-inset'))
|
||||
const open = (i) => inset(i) - Math.min(...items.map((_, j) => inset(j))) < 0.5
|
||||
const snap = (i) => Math.min(Math.max(i * (size + gap) - parseFloat(items[i].style.scrollMarginInlineStart), 0), scroller.scrollWidth - scroller.clientWidth)
|
||||
const at = (i) => Math.abs(Math.abs(scroller.scrollLeft) - snap(i)) < 1.5
|
||||
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||
@@ -73,7 +79,7 @@ it('masks items by their place between the keylines, and changes the large one a
|
||||
$page = carouselShowcase()
|
||||
->assertNoJavaScriptErrors()
|
||||
->assertScript(onCarousel(0, <<<'JS'
|
||||
return size > 0 && inset(0) < 0.5 && inset(items.length - 1) > 0.5
|
||||
return size > 0 && open(0) && ! open(items.length - 1)
|
||||
&& getComputedStyle(surface(0)).clipPath.startsWith('inset(')
|
||||
JS));
|
||||
|
||||
@@ -122,7 +128,7 @@ it('moves one item with the arrow keys, and to the ends with Home and End', func
|
||||
->assertScript(onCarousel(0, 'return at(1)'));
|
||||
|
||||
$page->keys(':focus', 'End')
|
||||
->assertScript(onCarousel(0, 'return Math.abs(scroller.scrollLeft - (scroller.scrollWidth - scroller.clientWidth)) < 1.5 && inset(items.length - 1) < 0.5 && root.querySelector(\'[aria-label="Next"]\').disabled'));
|
||||
->assertScript(onCarousel(0, 'return Math.abs(scroller.scrollLeft - (scroller.scrollWidth - scroller.clientWidth)) < 1.5 && open(items.length - 1) && root.querySelector(\'[aria-label="Next"]\').disabled'));
|
||||
|
||||
$page->keys(':focus', 'Home')
|
||||
->assertScript(onCarousel(0, 'return at(0)'));
|
||||
@@ -141,11 +147,11 @@ it('snaps a scroll that stops between items onto an item', function () {
|
||||
|
||||
it('brings a partly hidden item into focus when it is pressed', function () {
|
||||
$page = carouselShowcase()
|
||||
->assertScript(onCarousel(0, 'return inset(4) > 0.5'));
|
||||
->assertScript(onCarousel(0, 'return ! open(4)'));
|
||||
|
||||
$page->script(onCarousel(0, 'items[4].querySelector(\'[data-material-carousel-content]\').click()'));
|
||||
|
||||
$page->assertScript(onCarousel(0, 'return inset(4) < 0.5 && scroller.scrollLeft > 0'));
|
||||
$page->assertScript(onCarousel(0, 'return open(4) && scroller.scrollLeft > 0'));
|
||||
});
|
||||
|
||||
it('scrolls instantly and leaves every item unmasked under reduced motion', function () {
|
||||
@@ -183,11 +189,11 @@ it('mirrors in a right-to-left page', function () {
|
||||
|
||||
$page = visit('/carousel-rtl-probe')->waitForEvent('networkidle')
|
||||
->assertNoJavaScriptErrors()
|
||||
->assertScript(onCarousel(0, 'return size > 0 && at(0) && inset(0) < 0.5 && inset(items.length - 1) > 0.5', 'body'));
|
||||
->assertScript(onCarousel(0, 'return size > 0 && at(0) && open(0) && ! open(items.length - 1)', 'body'));
|
||||
|
||||
$page->keys('[data-material-carousel-item] >> nth=0', 'ArrowLeft')
|
||||
->assertScript(onCarousel(0, <<<'JS'
|
||||
return scroller.scrollLeft < -1 && at(1) && inset(0) > 0.5 && inset(1) < 0.5
|
||||
return scroller.scrollLeft < -1 && at(1) && ! open(0) && open(1)
|
||||
&& parseFloat(surface(0).style.getPropertyValue('--material-carousel-shift')) < 0
|
||||
JS, 'body'));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user