Keep the current section lit while a Livewire component updates

<x-section-nav> and <x-app-shell> mark an item current when its URL is
the request's. While a Livewire component on the page re-renders, the
request is Livewire's update endpoint, so no section or destination
stayed lit after the morph. Both now compare with the page's URL from
Livewire::originalUrl() during a Livewire update request; a normal
request compares exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
This commit is contained in:
Andreas Reinhold / reini
2026-09-13 18:18:39 +02:00
co-authored by Claude Opus 5
parent d77ecf5e2a
commit fdb13313f7
5 changed files with 61 additions and 6 deletions
+25
View File
@@ -2,6 +2,8 @@
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Route;
use Livewire\Component;
use Livewire\Livewire;
function shellDestinations(): array
{
@@ -54,6 +56,29 @@ it('marks the destination at the current URL when none says it is active', funct
->and(substr_count($html, 'aria-current="page"'))->toBe(2);
});
it('keeps the destination at the page\'s URL current while a Livewire component on it updates', function () {
Livewire::component('app-shell-probe', new class extends Component
{
public string $page = '';
public function mount(): void
{
$this->page = url()->current();
}
public function render(): string
{
return '<div><x-app-shell :destinations="[[\'title\' => \'Inbox\', \'icon\' => \'inbox\', \'url\' => $page], [\'title\' => \'Sent\', \'icon\' => \'send\', \'url\' => \'/sent\']]" /></div>';
}
});
$probe = Livewire::test('app-shell-probe');
expect(substr_count($probe->html(), 'aria-current="page"'))->toBe(2)
->and(substr_count($probe->call('$refresh')->html(), 'aria-current="page"'))->toBe(2)
->and($probe->html())->toMatch('/href="[^"]*\/livewire-unit-test-endpoint\/[^"]*"[^>]*aria-current="page"/');
});
it('lifts the snackbar above the bar only when there is a bar', function () {
expect((string) $this->blade('<x-app-shell :destinations="$destinations" />', ['destinations' => shellDestinations()]))
->toContain('max-sm:[--material-bottom-bar:calc(4rem+var(--material-safe-bottom,env(safe-area-inset-bottom))+var(--material-bottom-extra,0px))]')
+25
View File
@@ -80,3 +80,28 @@ it('marks the section whose url is the request\'s, and wraps many sections onto
->toMatch('/data-tab\s+aria-current="page"\s*>\s*<span data-tab-content>\s*<span class="truncate">S3/')
->not->toContain('wire:navigate');
});
it('keeps the page\'s section current while a Livewire component on it updates', function () {
Livewire::component('section-nav-probe', new class extends Component
{
public string $page = '';
public function mount(): void
{
$this->page = url()->current();
}
public function render(): string
{
return '<div><x-section-nav :items="[[\'title\' => \'Here\', \'url\' => $page], [\'title\' => \'Elsewhere\', \'url\' => \'/elsewhere\']]" no-wire-navigate /></div>';
}
});
$current = '/href="[^"]*\/livewire-unit-test-endpoint\/[^"]*"\s+data-tab\s+aria-current="page"/';
$probe = Livewire::test('section-nav-probe');
expect($probe->html())->toMatch($current)
->and($probe->call('$refresh')->html())->toMatch($current)
->and(substr_count($probe->html(), 'aria-current="page"'))->toBe(1);
});