Fix BarsTest's overflow selector and reopen timing, add reviewer coverage
The overflow menu's trigger selector matched its own popover's menu items too (they sit inside the same wrapper), so a click resolved to 5 elements instead of 1 — scoped to [data-md-menu-trigger]. Escape's close is the browser's own light dismiss (menu.js), so a click right after it needs to be past the 250ms reopen guard, as the same pattern already is elsewhere (ActionsTest.php); the reopen test now waits. Also adds the reviewer's requested coverage: the section nav's list items share the row's width equally regardless of label length, and a plain wire:model tab switches its panel locally without reaching the server per click while wire:model.live does, one request that also flushes the deferred property along with it (plan step 36). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bd2bb07106
commit
98f861c124
@@ -9,6 +9,17 @@ class BarsProbe extends Component
|
||||
{
|
||||
public string $tab = 'files';
|
||||
|
||||
public string $deferredTab = 'files';
|
||||
|
||||
public int $deferredUpdates = 0;
|
||||
|
||||
// Only runs while the server processes a request that carries $deferredTab along — never on
|
||||
// the click alone, since a plain wire:model (no `.live`) only changes it locally until then.
|
||||
public function updatedDeferredTab(): void
|
||||
{
|
||||
$this->deferredUpdates++;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
return <<<'BLADE'
|
||||
@@ -27,6 +38,13 @@ class BarsProbe extends Component
|
||||
<x-tab name="activity">Two downloads.</x-tab>
|
||||
</x-tabs>
|
||||
|
||||
<p>deferred tab: <span id="deferred-tab">{{ $deferredTab }}</span>, updates: <span id="deferred-updates">{{ $deferredUpdates }}</span></p>
|
||||
|
||||
<x-tabs id="deferred" wire:model="deferredTab" :tabs="[['name' => 'files', 'label' => 'Files'], ['name' => 'people', 'label' => 'People']]">
|
||||
<x-tab name="files">Deferred files.</x-tab>
|
||||
<x-tab name="people">Deferred people.</x-tab>
|
||||
</x-tabs>
|
||||
|
||||
<x-toolbar label="Formatting">
|
||||
<button type="button" id="bold">B</button>
|
||||
<button type="button" id="italic">I</button>
|
||||
@@ -36,7 +54,7 @@ class BarsProbe extends Component
|
||||
<x-theme-toggle mode="cycle" id="cycle" />
|
||||
<x-theme-toggle mode="picker" />
|
||||
|
||||
<x-section-nav :items="[['title' => 'Profile', 'url' => '#profile', 'active' => true], ['title' => 'Security', 'url' => '#security']]" no-wire-navigate />
|
||||
<x-section-nav :items="[['title' => 'Profile', 'url' => '#profile', 'active' => true], ['title' => 'Security', 'url' => '#security'], ['title' => 'Advanced notification settings', 'url' => '#notifications']]" no-wire-navigate />
|
||||
|
||||
<div style="height: 200vh"></div>
|
||||
</div>
|
||||
@@ -89,6 +107,30 @@ it('moves between tabs with the arrow keys, skipping disabled ones, and tells Li
|
||||
->assertSeeIn('#tab', 'activity');
|
||||
});
|
||||
|
||||
it('sends a request per click with wire:model.live, but nothing until then with plain wire:model', function () {
|
||||
// updatedDeferredTab() on the probe only runs while the server processes a request that
|
||||
// carries $deferredTab with it — never from the click alone — so its counter (not a browser
|
||||
// network hook, which a plain property commit does not reliably fire) tells whether a click
|
||||
// reached the server at all.
|
||||
$page = barsProbe();
|
||||
|
||||
// Plain wire:model is entangled locally: the click switches the panel and its aria-selected at
|
||||
// once, but the property itself only reaches the server on the next request from anything else.
|
||||
$page->click('#deferred-people')
|
||||
->assertScript("document.querySelector('#deferred-people').getAttribute('aria-selected') === 'true'")
|
||||
->assertSee('Deferred people.')
|
||||
->assertSeeIn('#deferred-tab', 'files')
|
||||
->assertSeeIn('#deferred-updates', '0');
|
||||
|
||||
// wire:model.live sends its own request, which carries the deferred property above along with
|
||||
// it — the one request updates both.
|
||||
$page->click('#share-people')
|
||||
->assertSeeIn('#tab', 'people')
|
||||
->assertSeeIn('#deferred-tab', 'people')
|
||||
->assertSeeIn('#deferred-updates', '1')
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
it('collapses a medium app bar into its row as the page scrolls, and fades its small title in', function () {
|
||||
$headline = "getComputedStyle(document.querySelector('#bar [data-md-app-bar-row] [data-md-app-bar-headline]')).opacity";
|
||||
|
||||
@@ -146,6 +188,15 @@ it('turns section tabs into a picker on a phone', function () {
|
||||
->assertScript("! document.querySelector('[data-md-section-nav-picker] [role=\"menuitem\"][href=\"#security\"]').hasAttribute('aria-current')");
|
||||
});
|
||||
|
||||
it('shares the row width equally between the section nav\'s items, whatever their label', function () {
|
||||
$widths = "[...document.querySelectorAll('[data-md-section-nav] [data-md-tabs-bar] > li')].map((li) => Math.round(li.getBoundingClientRect().width))";
|
||||
|
||||
barsProbe()
|
||||
->assertScript("{$widths}.length === 3")
|
||||
->assertScript("new Set({$widths}).size === 1")
|
||||
->assertNoJavaScriptErrors();
|
||||
});
|
||||
|
||||
class AppBarOverflowProbe extends Component
|
||||
{
|
||||
public int $stars = 0;
|
||||
@@ -217,7 +268,7 @@ it('shows the app bar overflow at exactly 599 and 600px', function () {
|
||||
it('moves the keyboard through an app bar\'s overflow menu, and closes it once the width no longer shows it', function () {
|
||||
$page = appBarOverflowProbe()->resize(599, 800);
|
||||
|
||||
$page->click('#bar [data-md-app-bar-overflow="compact"] button')
|
||||
$page->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
|
||||
->assertScript("document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
|
||||
->keys(':focus', 'ArrowDown')
|
||||
->assertScript("document.activeElement.textContent.includes('Star')")
|
||||
@@ -226,9 +277,12 @@ it('moves the keyboard through an app bar\'s overflow menu, and closes it once t
|
||||
->keys(':focus', 'Escape')
|
||||
->assertScript("! document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')");
|
||||
|
||||
// Past the reopen guard of the Escape above (menu.js), so the click below is on its own.
|
||||
$page->wait(0.3);
|
||||
|
||||
// Reopen it, then resize across 600px: the compact menu's trigger is no longer shown, so the
|
||||
// menu it opened closes rather than staying open anchored to nothing.
|
||||
$page->click('#bar [data-md-app-bar-overflow="compact"] button')
|
||||
$page->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
|
||||
->assertScript("document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
|
||||
->resize(900, 800)
|
||||
->assertScript("! document.querySelector('#bar [data-md-app-bar-overflow=\"compact\"] [data-md-menu-popover]').matches(':popover-open')")
|
||||
@@ -242,7 +296,7 @@ it('runs a wire:click action from either the icon button or its overflow menu it
|
||||
->assertSeeIn('#stars', '1');
|
||||
|
||||
$page->resize(599, 800)
|
||||
->click('#bar [data-md-app-bar-overflow="compact"] button')
|
||||
->click('#bar [data-md-app-bar-overflow="compact"] [data-md-menu-trigger] button')
|
||||
->click('[role="menuitem"]:has-text("Star")')
|
||||
->assertSeeIn('#stars', '2');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user