Files
livewire-material/tests/Feature/Components/NavigationBarTest.php
T
Andreas Reinhold / reiniandClaude Opus 5 c3bed743aa Hold the navigation bar item to its own label and state-layer tokens
NavigationBarTokens names one label font for both icon positions, label-medium;
the horizontal layout had taken label-large from the rail's item, a different
component with a token of its own. And the item's state layer washed in
on-surface over a secondary-container pill, where the only state-layer tokens M3
states for a navigation item — the rail's, which the rail already follows — are
on-secondary-container for all six states.
Plan: docs/plans/material-3-alignment.md, step 21 (navigation N-08, N-19).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 06:09:05 +02:00

63 lines
3.6 KiB
PHP

<?php
use Illuminate\Support\Str;
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');
});
it('keeps the bar item on the label and state-layer colours M3 tokens', function () {
$css = file_get_contents(__DIR__.'/../../../resources/css/components/navigation.css');
$horizontal = Str::of($css)->after('@container (width >= 37.5rem)')->before('/* ------')->toString();
expect($horizontal)
// NavigationBarTokens names one label font for both icon positions: label-medium (N-08).
->not->toContain('--md-sys-typescale-label-lg')
->and($css)
// The indicator and the pill wash in on-secondary-container, as the rail's do (N-19).
->toContain('[data-navigation-bar-item] :is([data-navigation-indicator], [data-navigation-pill])::before {');
});