M3 Expressive's flexible navigation bar, the collapsed, expanded and modal navigation rail with its state applied before the first paint, and an adaptive app shell composing them. The head script now restores the theme and rail attributes that wire:navigate strips from <html>. Completes Phase 8. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
78 lines
3.8 KiB
PHP
78 lines
3.8 KiB
PHP
<?php
|
|
|
|
it('draws each mode, falling back to collapsible', function (string $mode, string $expected) {
|
|
expect((string) $this->blade('<x-navigation-rail :mode="$mode"><x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" /></x-navigation-rail>', ['mode' => $mode]))
|
|
->toContain("data-navigation-rail=\"{$expected}\"")
|
|
->toContain('<nav')
|
|
->toContain('aria-label="Main"');
|
|
})->with([
|
|
['collapsed', 'collapsed'],
|
|
['expanded', 'expanded'],
|
|
['collapsible', 'collapsible'],
|
|
['modal', 'modal'],
|
|
['adaptive', 'adaptive'],
|
|
['sideways', 'collapsible'],
|
|
]);
|
|
|
|
it('gives the collapsible, modal and adaptive rails a menu button and the store', function () {
|
|
$collapsible = (string) $this->blade('<x-navigation-rail />');
|
|
|
|
expect($collapsible)
|
|
->toContain('x-data="materialNavigationRail(\'collapsible\')"')
|
|
->toContain('data-navigation-rail-menu')
|
|
->toContain('aria-label="Collapse navigation"')
|
|
->toContain('aria-expanded="true"')
|
|
->not->toContain('data-navigation-rail-scrim')
|
|
->not->toContain('x-trap')
|
|
->and((string) $this->blade('<x-navigation-rail mode="modal" />'))
|
|
->toContain('aria-label="Expand navigation"')
|
|
->toContain('data-navigation-rail-scrim')
|
|
->toContain('x-trap.inert.noscroll="open"')
|
|
->and((string) $this->blade('<x-navigation-rail mode="expanded" />'))
|
|
->not->toContain('data-navigation-rail-menu')
|
|
->not->toContain('x-data')
|
|
->and((string) $this->blade('<x-navigation-rail mode="collapsible" :menu="false" />'))->not->toContain('data-navigation-rail-menu');
|
|
});
|
|
|
|
it('keeps the header and footer out of the scrolling destinations', function () {
|
|
$html = (string) $this->blade(<<<'BLADE'
|
|
<x-navigation-rail mode="expanded" width="20rem">
|
|
<x-slot:brand><span>Brand</span></x-slot:brand>
|
|
<x-slot:header><button>FAB</button></x-slot:header>
|
|
<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" />
|
|
<x-slot:footer><button>Account</button></x-slot:footer>
|
|
</x-navigation-rail>
|
|
BLADE);
|
|
|
|
expect($html)
|
|
->toContain('--navigation-rail-width: 20rem')
|
|
->toMatch('/data-navigation-rail-header.*Brand.*FAB.*data-navigation-rail-destinations.*Inbox.*data-navigation-rail-footer.*Account/s');
|
|
});
|
|
|
|
it('draws a destination in both shapes, with its count on the icon and at the end', function () {
|
|
$html = (string) $this->blade('<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" active badge="12" />');
|
|
|
|
expect($html)
|
|
->toContain('<a data-navigation-rail-item')
|
|
->toContain('wire:navigate')
|
|
->toContain('aria-current="page"')
|
|
->toContain('data-navigation-indicator')
|
|
->toContain('<span data-navigation-label>Inbox</span>')
|
|
->toContain('hidden rail-collapsed:contents')
|
|
->toContain('rail-collapsed:hidden')
|
|
->toContain('<span class="sr-only">, 12</span>')
|
|
->and(substr_count($html, '>12</span>'))->toBe(2)
|
|
->and((string) $this->blade('<x-navigation-rail-item label="Inbox" icon="inbox" link="/inbox" no-wire-navigate />'))->not->toContain('wire:navigate')
|
|
->and((string) $this->blade('<x-navigation-rail-item label="Inbox" icon="inbox" />'))->toContain('<button data-navigation-rail-item');
|
|
});
|
|
|
|
it('groups destinations under a heading that names the group', function () {
|
|
$html = (string) $this->blade('<x-navigation-rail-section label="Labels"><x-navigation-rail-item label="Travel" icon="flight" link="/travel" /></x-navigation-rail-section>');
|
|
|
|
preg_match('/aria-labelledby="([^"]+)"/', $html, $labelledBy);
|
|
|
|
expect($html)->toContain('role="group"')
|
|
->and($labelledBy[1] ?? null)->not->toBeNull()
|
|
->and($html)->toContain("<p id=\"{$labelledBy[1]}\" data-navigation-rail-heading>Labels</p>");
|
|
});
|