blade(<<<'BLADE' Anna BLADE); expect($html) ->toContain('role="tablist"') ->toContain('data-variant="primary"') ->toContain('id="share-people"') ->toContain('aria-controls="share-people-panel"') ->toMatch('/data-tab="people"\s+aria-controls="share-people-panel"\s+aria-selected="true"\s+tabindex="0"/') ->toMatch('/data-tab="files"\s+aria-controls="share-files-panel"\s+aria-selected="false"\s+tabindex="-1"/') ->toContain('disabled') ->toContain('data-tab-indicator') ->toContain('--tabs-indicator: share-indicator') ->toContain('x-modelable="selected"') ->toContain('role="tabpanel"') ->toContain('>3'); }); it('takes a variant, stacks icons and scrolls on request', function () { expect((string) $this->blade('')) ->toContain('data-variant="secondary"') ->not->toContain('data-stacked') ->toContain('data-scrollable') ->and((string) $this->blade('')) ->toContain('data-stacked') ->toMatch('/data-tab="b"\s+aria-controls="[^"]+"\s+aria-selected="true"/'); }); it('entangles the chosen tab with Livewire and renders the one the property names', function () { Livewire::component('tabs-probe', new class extends Component { public string $tab = 'people'; public function render(): string { return '
'; } }); expect(Livewire::test('tabs-probe')->html()) ->toContain(".entangle('tab').live") ->not->toContain('x-modelable') ->toMatch('/data-tab="people"\s+aria-controls="[^"]+"\s+aria-selected="true"/'); }); it('draws section navigation as secondary tabs and a picker', function () { $html = (string) $this->blade(<<<'BLADE' BLADE); expect($html) ->toContain('aria-label="Settings"') ->toContain('data-section-picker') ->toContain('data-variant="secondary"') ->toContain('sm:flex') ->toMatch('/href="\/settings\/security"\s+data-tab\s+aria-current="page"\s+wire:navigate/') ->not->toMatch('/href="\/settings\/profile"\s+data-tab\s+aria-current/') ->toContain('role="menuitemcheckbox"'); }); it('marks the section whose url is the request\'s, and wraps many sections onto a grid', function () { $this->get('/'); $items = collect(range(1, 7))->map(fn (int $n): array => ['title' => "S{$n}", 'url' => $n === 3 ? url('/') : "/s/{$n}"])->all(); expect((string) $this->blade('', ['items' => $items])) ->toContain('sm:grid sm:grid-cols-4 xl:flex') ->toMatch('/data-tab\s+aria-current="page"\s*>\s*\s*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 '
'; } }); $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); });