Files
livewire-material/tests/Feature/Components/ListTest.php
T
Andreas Reinhold / reiniandClaude Opus 5 dcd28707dd Let a segmented row answer, and inset a list's dividers
Plan step 19, containment.md C-08, C-21, C-22 and C-16. The segmented
fill was declared unlayered, so it beat every state layer in
@layer components and a segmented row lit up for nothing: it now sits in
the layer, `:where()`d to weigh nothing, ahead of the state rules. Its
tone is `surface`, which is ListTokens' ItemSegmentedContainerColor, and
a keyboard-focused row morphs to the large corner like a pressed one.
`<x-list dividers>` draws ListTokens' 16dp-inset rule instead of a
full-bleed `divide-y`.

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

81 lines
4.7 KiB
PHP

<?php
it('draws a plain list, with dividers on request', function () {
expect((string) $this->blade('<x-list label="Files" dividers><x-list-item title="a.zip" /></x-list>'))
->toContain('role="list"')
->toContain('aria-label="Files"')
->toContain('data-list="plain"')
->toContain('data-dividers')
->not->toContain('divide-y')
->toContain('role="listitem"')
->and((string) $this->blade('<x-list><x-list-item title="a.zip" /></x-list>'))->not->toContain('data-dividers')
->and((string) $this->blade('<x-list segmented dividers><x-list-item title="a.zip" /></x-list>'))->not->toContain('data-dividers');
});
it('draws M3 Expressive\'s segmented list', function () {
expect((string) $this->blade('<x-list segmented><x-list-item title="a.zip" /></x-list>'))
->toContain('data-list="segmented"')
->toContain('gap-0.5');
});
it('grows an item from one to three lines', function () {
expect((string) $this->blade('<x-list-item title="a.zip" />'))->toContain('min-h-14')
->and((string) $this->blade('<x-list-item title="a.zip" description="248 MB" />'))->toContain('min-h-18')->toContain('248 MB')
->and((string) $this->blade('<x-list-item title="a.zip" overline="Protected" description="248 MB" />'))->toContain('min-h-22')->toContain('Protected');
});
it('leads with an icon, an avatar or an image, and trails with text or an icon', function () {
expect((string) $this->blade('<x-list-item title="a" icon="folder_zip" trailing="3 days" icon-right="chevron_right" />'))
->toContain('3 days')
->and(substr_count((string) $this->blade('<x-list-item title="a" icon="folder_zip" icon-right="chevron_right" />'), '<svg'))->toBe(2)
->and((string) $this->blade('<x-list-item title="Anna" avatar="AM" />'))->toContain('bg-primary-container')->toContain('>AM</span>')
->and((string) $this->blade('<x-list-item title="Anna" avatar="/anna.jpg" />'))->toContain('<img src="/anna.jpg"')
->and((string) $this->blade('<x-list-item title="Photo" image="/p.jpg" />'))->toContain('size-14');
});
it('makes a linked item a row that opens from anywhere', function () {
expect((string) $this->blade('<x-list-item title="Settings" link="/settings" />'))
->toContain('data-list-row')
->toContain('href="/settings"')
->toContain('data-list-open')
->toContain('wire:navigate');
});
it('is a list box of options when it is one a person chooses from', function () {
expect((string) $this->blade('<x-list selectable label="Recipients"><x-list-item title="Anna" :selected="true" /><x-list-item title="Ben" /></x-list>'))
->toContain('role="listbox"')
->toMatch('/role="option"\s+aria-selected="true"/')
->toMatch('/role="option"\s+aria-selected="false"/')
->not->toContain('aria-multiselectable')
->and((string) $this->blade('<x-list selection="multi"><x-list-item title="Anna" /></x-list>'))
->toContain('role="listbox"')
->toContain('aria-multiselectable="true"')
->and((string) $this->blade('<x-list><x-list-item title="Anna" :selected="true" /></x-list>'))
->toContain('role="list"')
->toMatch('/role="listitem"\s+aria-current="true"/')
->not->toContain('aria-selected');
});
it('draws a second cue on a selected option, which icon-right replaces', function () {
expect(substr_count((string) $this->blade('<x-list selectable><x-list-item title="Anna" :selected="true" /></x-list>'), '<svg'))->toBe(1)
->and(substr_count((string) $this->blade('<x-list selectable><x-list-item title="Anna" :selected="true" icon-right="chevron_right" /></x-list>'), '<svg'))->toBe(1)
->and(substr_count((string) $this->blade('<x-list selectable><x-list-item title="Anna" /></x-list>'), '<svg'))->toBe(0)
->and(substr_count((string) $this->blade('<x-list><x-list-item title="Anna" :selected="true" /></x-list>'), '<svg'))->toBe(0);
});
it('leaves a disabled item out of the keyboard and announces it disabled', function () {
expect((string) $this->blade('<x-list-item title="Settings" link="/settings" icon="settings" disabled />'))
->toContain('aria-disabled="true"')
->toContain('pointer-events-none text-on-surface/38')
->not->toContain('href="/settings"')
->not->toContain('data-list-open')
->not->toContain('data-list-row');
});
it('marks a selected item and takes controls in its slots', function () {
expect((string) $this->blade('<x-list-item title="Anna" :selected="true"><x-slot:leading><input type="checkbox"></x-slot:leading><x-slot:end><button>⋮</button></x-slot:end></x-list-item>'))
->toContain('data-selected')
->toContain('<input type="checkbox">')
->toContain('<button>⋮</button>');
});