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
49 lines
2.9 KiB
PHP
49 lines
2.9 KiB
PHP
<?php
|
|
|
|
it('draws a navigation landmark around its items', function () {
|
|
expect((string) $this->blade('<x-navigation-bar><x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" /></x-navigation-bar>'))
|
|
->toContain('<nav aria-label="Main" data-navigation-bar')
|
|
->toContain('data-navigation-bar-items')
|
|
->and((string) $this->blade('<x-navigation-bar label="Sections" />'))->toContain('aria-label="Sections"');
|
|
});
|
|
|
|
it('links a destination through wire:navigate unless told not to', function () {
|
|
expect((string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" />'))
|
|
->toContain('<a data-navigation-bar-item')
|
|
->toContain('href="/inbox"')
|
|
->toContain('wire:navigate')
|
|
->and((string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" no-wire-navigate />'))->not->toContain('wire:navigate')
|
|
->and((string) $this->blade('<x-navigation-bar-item label="Help" icon="help" link="https://example.com" external />'))
|
|
->toContain('target="_blank"')
|
|
->not->toContain('wire:navigate');
|
|
});
|
|
|
|
it('is a button without a link', function () {
|
|
expect((string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" wire:click="show(\'inbox\')" />'))
|
|
->toContain('<button data-navigation-bar-item')
|
|
->toContain('type="button"')
|
|
->not->toContain('href=');
|
|
});
|
|
|
|
it('marks the current destination with aria-current and the filled icon', function () {
|
|
$active = (string) $this->blade('<x-navigation-bar-item label="Starred" icon="star" link="/starred" active />');
|
|
$inactive = (string) $this->blade('<x-navigation-bar-item label="Starred" icon="star" link="/starred" />');
|
|
|
|
$symbol = fn (string $html): string => preg_match('/<svg.*?<\/svg>/s', $html, $svg) ? $svg[0] : '';
|
|
|
|
expect($active)->toContain('aria-current="page"')->toContain('data-active')
|
|
->and($inactive)->not->toContain('aria-current')->not->toContain('data-active')
|
|
->and($symbol($active))->toBe(trim((string) $this->blade('<x-icon name="star" filled class="size-6" />')))
|
|
->and($symbol($inactive))->toBe(trim((string) $this->blade('<x-icon name="star" class="size-6" />')))
|
|
->and($symbol($active))->not->toBe($symbol($inactive));
|
|
});
|
|
|
|
it('badges the icon with a dot or a count that screen readers hear', function () {
|
|
$count = (string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" badge="1200" />');
|
|
$dot = (string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" :badge="true" badge-label="New mail" />');
|
|
|
|
expect($count)->toContain('999+')->toContain('<span class="sr-only">, 1200</span>')
|
|
->and($dot)->toContain('size-1.5')->toContain('<span class="sr-only">, New mail</span>')
|
|
->and((string) $this->blade('<x-navigation-bar-item label="Inbox" icon="inbox" link="/inbox" />'))->not->toContain('sr-only');
|
|
});
|