blade('a')) ->toContain('') ->and((string) $this->blade(''))->toContain('data-md-size="xs"') ->and((string) $this->blade(''))->toContain('data-md-size="sm"'); }); it('tightens a table only when the caller asks for it', function () { expect((string) $this->blade(''))->not->toContain('data-md-dense') ->and((string) $this->blade(''))->toContain('data-md-dense'); }); it('draws 52px rows, 36px dense ones, from its stylesheet in the components layer', function () { $css = (string) file_get_contents(__DIR__.'/../../../resources/css/components/table.css'); expect($css)->toContain('@layer material.components') ->toMatch('/\\[data-md-table\\] \\{\\s*--cell-x: 12px;\\s*--cell-y: var\\(--md-sys-measurement-space200\\);/') ->toMatch('/\\[data-md-table\\]\\[data-md-dense\\] \\{\\s*--cell-y: var\\(--md-sys-measurement-space100\\);/') ->toContain('[data-md-table] :where(tbody tr[aria-selected=\'true\'])') ->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/table.css';") ->and((string) file_get_contents(__DIR__.'/../../../resources/css/tailwind.css'))->not->toContain('table.css'); }); it('sorts by its column, ascending first and then flipping', function () { expect((string) $this->blade('Size')) ->not->toContain('aria-sort') ->toContain('wire:click="$set(\'sortBy\', {"column":"size","direction":"asc"})"') ->and((string) $this->blade('Size')) ->toContain('aria-sort="ascending"') ->toMatch('/
/') ->toContain('$set(\'order\', {"column":"size","direction":"desc"})') ->and((string) $this->blade('Size')) ->toContain('aria-sort="descending"') ->toContain('"direction":"asc"'); }); it('gives the sort button and every page control a 48px target, from the foundation\'s classes', function () { $pages = new LengthAwarePaginator(range(1, 10), 95, 10, 3, ['path' => '/shares']); expect((string) $this->blade('Size')) ->toMatch('/]*data-md-sort-header-button)(?=[^>]*class="md-focus-ring md-touch-target")[^>]*>/') ->toContain('--md-icon-size: 16px') ->not->toContain('data-md-active') ->and((string) $pages->links()) ->toContain('') ->and(ComponentStylesheet::read('pagination')->declarations('[data-md-pagination-step]'))->toMatchArray([ 'width' => 'var(--md-sys-measurement-space500)', 'height' => 'var(--md-sys-measurement-space500)', ]) ->and(ComponentStylesheet::read('pagination')->has(':is(a, button)[data-md-pagination-step]::after'))->toBeFalse() ->and(ComponentStylesheet::read('pagination')->has(':is(a, button)[data-md-pagination-step]:focus-visible'))->toBeFalse(); }); it('draws Laravel\'s paginators in M3', function () { $pages = new LengthAwarePaginator(range(1, 10), 95, 10, 3, ['path' => '/shares']); expect((string) $pages->links()) ->toContain('data-md-pagination') ->toContain('Page 3 of 10') ->toContain('21–30 of 95') // The current page is a plain span, not a live step: it carries no interaction class. ->toContain('3') ->toContain('href="/shares?page=2" rel="prev"') ->toContain('aria-label="Go to page 4"') ->toContain('data-md-mirror-rtl') ->not->toContain('text-gray') // A live step (a link or a button) carries only the foundation's interaction classes. ->not->toMatch('/class="(?!md-state-layer md-focus-ring md-touch-target")/'); expect((string) (new Paginator(range(1, 11), 10, 2, ['path' => '/shares']))->links()) ->toContain('data-md-pagination') ->toContain('href="/shares?page=1"') ->toContain('href="/shares?page=3"') ->toContain('Previous'); }); it('draws Livewire\'s paginators in M3, wired to its page actions', function () { Livewire::component('paging-probe', new class extends Component { use WithPagination; public function render(): string { return <<<'BLADE'
{{ (new \Illuminate\Pagination\LengthAwarePaginator(range(1, 10), 95, 10, $this->getPage(), ['path' => '/'])) ->links() }}
BLADE; } }); expect(Livewire::test('paging-probe')->html()) ->toContain('data-md-pagination') ->toContain('data-md-pagination-page') ->toContain('wire:click="gotoPage(2, \'page\')"') ->toContain('wire:click="nextPage(\'page\')"') ->not->toContain('text-gray') // A live step (a link or a button) carries only the foundation's interaction classes. ->not->toMatch('/class="(?!md-state-layer md-focus-ring md-touch-target")/'); }); it('draws the sort header from its stylesheet, its arrow hidden until the column sorts or is hovered', function () { expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/sort-header.css')) ->toContain('@layer material.components') ->toMatch('/\\[data-md-sort-header\\]:not\\(\\[data-md-active\\]\\) \\[data-md-sort-header-arrow\\] \\{\\s*opacity: 0;/') ->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/sort-header.css';"); }); it('draws the paginators from their stylesheet, trading the numbers for "Page n of m" below 600px', function () { expect((string) file_get_contents(__DIR__.'/../../../resources/css/components/pagination.css')) ->toContain('@layer material.components') ->toMatch('/@media \\(width < 600px\\) \\{\\s*\\[data-md-pagination-page\\],\\s*\\[data-md-pagination-range\\] \\{\\s*display: none;/') ->toMatch('/@media \\(width >= 600px\\) \\{\\s*\\[data-md-pagination-compact\\] \\{\\s*display: none;/') ->and((string) file_get_contents(__DIR__.'/../../../resources/css/all.css'))->toContain("@import './components/pagination.css';"); });