The theme toggle and the account-menu avatar are drawn 40px and had no target extension; both now carry `touch-target`, and the avatar's clipping moves from the button (which would have cut the pseudo-target off) to the <img>. The rail's hand-rolled `after:` target becomes the same utility. Plan: docs/plans/material-3-alignment.md, step 14 (navigation N-01). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
80 lines
4.0 KiB
PHP
80 lines
4.0 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"')
|
|
// The 40px menu button reaches M3's 48px target through the shared utility (N-01).
|
|
->toContain('touch-target')
|
|
->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>");
|
|
});
|