Cover the datepicker's shared classes, hover layer and 20px root font

Browser-test coverage the reviewer asked for on the step 36 batch: the
year, menu-button and list-option state layer/focus ring (with the
option's ring drawn inset in its row), a selected day's hover layer
over its primary fill versus none on a disabled or blank day, and the
40px day indicator holding its size at a 20px root font size while
only its (rem-sized) text grows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 20:04:40 +02:00
co-authored by Claude Sonnet 5
parent c72908458b
commit 0b7dd7d46a
+96
View File
@@ -581,3 +581,99 @@ it('reaches the month and year dropdowns with Shift+M and Shift+Y', function ()
$page->keys(':focus', 'Shift+Y') $page->keys(':focus', 'Shift+Y')
->assertScript("document.activeElement.dataset.mdDatepickerMenuButton === 'years'"); ->assertScript("document.activeElement.dataset.mdDatepickerMenuButton === 'years'");
}); });
it('draws the shared state layer and focus ring on the menu buttons, the year and the list options, the option\'s ring drawn inset', function () {
$monthButton = '#expires-field-picker [data-md-datepicker-menu-button="months"]';
$yearButton = '#expires-field-picker [data-md-datepicker-menu-button="years"]';
$modalYearButton = '#birthday-field-picker [data-md-datepicker-nav]:not([data-md-docked]) [data-md-datepicker-menu-button]';
$selectedOption = "document.querySelector('#expires-field-picker [data-md-datepicker-option][aria-selected=\"true\"]')";
$selectedYear = "document.querySelector('#birthday-field-picker [data-md-datepicker-year][aria-selected=\"true\"]')";
$hasBoth = fn (string $expr): string => "({$expr}).classList.contains('md-state-layer') && ({$expr}).classList.contains('md-focus-ring')";
$page = dateProbe()
->click('[aria-controls="expires-field-picker"][data-md-datepicker-toggle]')
->assertScript($hasBoth("document.querySelector('{$monthButton}')"))
->assertScript($hasBoth("document.querySelector('{$yearButton}')"))
->click($monthButton)
// toggleView() scrolls the selected option into view and focuses it once settled.
->assertScript("document.activeElement === {$selectedOption}")
->assertScript($hasBoth($selectedOption))
// A real keydown (rather than the script focus above) puts the browser in keyboard
// modality, so :focus-visible — and the ring the foundation draws inside the row rather
// than round the control — actually applies.
->keys(':focus', 'ArrowDown')
->assertScript("document.activeElement.matches('[data-md-datepicker-option]')")
->assertScript("getComputedStyle(document.activeElement).outlineOffset === '-3px'");
// Close the docked popover, or it sits over the next field and swallows the click below.
$page->keys(':focus', 'Escape')
->assertScript("! document.querySelector('#expires-field-picker').matches(':popover-open')");
$page->click('#birthday-field')
->assertScript("document.querySelector('#birthday-field-picker').matches(':modal')")
->click($modalYearButton)
->assertScript($hasBoth("document.querySelector('{$modalYearButton}')"))
->assertScript("document.activeElement === {$selectedYear}")
->assertScript($hasBoth($selectedYear));
});
it('shows a state layer over a selected day\'s primary fill on hover, and none on a disabled or blank day', function () {
$selectedDay = day('expires-field', '2026-09-13');
$selectedSpan = "document.querySelector('{$selectedDay} > span')";
$disabledDay = day('expires-field', '2026-09-09');
$disabledSpan = "document.querySelector('{$disabledDay} > span')";
$page = dateProbe()
->click('[aria-controls="expires-field-picker"][data-md-datepicker-toggle]')
->assertScript("document.querySelector('{$selectedDay}').hasAttribute('data-md-selected')")
->assertAttribute($disabledDay, 'aria-disabled', 'true')
// The day's own fill transitions in when the popover opens: let it settle before reading
// a resting value, or the "unchanged by hover" comparison below race against that instead.
->wait(0.3);
$fill = $page->script("getComputedStyle({$selectedSpan}).backgroundColor");
$page->hover($selectedDay)
// The layer draws over the same primary fill: hovering never changes the day's own background.
->assertScript("getComputedStyle({$selectedSpan}).backgroundColor === '{$fill}'")
->assertScript("getComputedStyle({$selectedSpan}, '::before').opacity !== '0'");
$page->hover($disabledDay)
->assertScript("getComputedStyle({$disabledSpan}, '::before').display === 'none'");
$page->script("document.querySelector('{$disabledDay}').focus()");
$page->assertScript("document.activeElement === document.querySelector('{$disabledDay}')")
->assertScript("getComputedStyle({$disabledSpan}, '::before').display === 'none'");
// Close the docked popover, or it sits over the next field and swallows the click below.
$page->keys(':focus', 'Escape')
->assertScript("! document.querySelector('#expires-field-picker').matches(':popover-open')");
// A blank cell (no date, outside the shown month in modal presentation) can't be focused at
// all — the same rule hides its layer regardless.
$page->click('#birthday-field')
->assertScript("document.querySelector('#birthday-field-picker').matches(':modal')")
->hover('#birthday-field-picker [data-md-blank] > span >> nth=0')
->assertScript("getComputedStyle([...document.querySelectorAll('#birthday-field-picker [data-md-blank] > span')][0], '::before').display === 'none'");
});
it('keeps the day indicator its fixed 40px size at a 20px root font size, growing only its text', function () {
$daySpan = "document.querySelector('".day('expires-field', '2026-09-13')." > span')";
$width = "Math.round({$daySpan}.getBoundingClientRect().width)";
$font = "getComputedStyle({$daySpan}).fontSize";
$picker = "document.querySelector('#expires-field-picker')";
$page = dateProbe()->click('[aria-controls="expires-field-picker"][data-md-datepicker-toggle]');
$restingWidth = (int) $page->script($width);
$restingFont = $page->script($font);
$page->script("document.documentElement.style.fontSize = '20px'");
$page->assertScript("({$width}) === {$restingWidth}")
->assertScript("({$font}) !== '{$restingFont}'")
// The px-sized grid sets the popover's size, not the (rem-sized) text now growing inside it.
->assertScript("{$picker}.scrollWidth <= {$picker}.clientWidth + 1 && {$picker}.scrollHeight <= {$picker}.clientHeight + 1");
});