Files
livewire-material/tests/Browser/DataTest.php
T
Andreas Reinhold / reiniandClaude Opus 5 2acd9ab492 Draw the paginators without Tailwind
Laravel's and Livewire's paginator views render data-md-pagination
hooks; pagination.css draws the row, the 40px steps with their 48px
targets, the selected page and the swap to "Page n of m" below 600px
(plan step 36, inputs). It imports button.css for the simple
paginators, now that the button is rewritten.

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

135 lines
5.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
use Illuminate\Contracts\View\View;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Route;
use Livewire\Component;
use Livewire\Livewire;
use Livewire\WithPagination;
class DataProbe extends Component
{
use WithPagination;
/** @var array{column: string, direction: string} */
public array $sortBy = ['column' => 'name', 'direction' => 'asc'];
public function render(): View
{
$files = collect(range(1, 25))->map(fn (int $n): array => ['name' => sprintf('file-%02d.zip', $n), 'size' => $n * 3 % 17]);
$files = $files->sortBy($this->sortBy['column'], SORT_REGULAR, $this->sortBy['direction'] === 'desc')->values();
$page = $this->getPage();
return view('probe::data', [
'files' => new LengthAwarePaginator($files->forPage($page, 10)->values(), $files->count(), 10, $page),
]);
}
}
function dataProbe()
{
$views = sys_get_temp_dir().'/livewire-material-data-probe';
@mkdir($views);
file_put_contents($views.'/data.blade.php', <<<'BLADE'
<div class="p-4">
<x-table>
<thead>
<tr>
<x-sort-header column="name" :sort-by="$sortBy" id="by-name">Name</x-sort-header>
<x-sort-header column="size" :sort-by="$sortBy" id="by-size">Size</x-sort-header>
</tr>
</thead>
<tbody>
@foreach ($files as $file)
<tr wire:key="file-{{ $file['name'] }}"><td>{{ $file['name'] }}</td><td>{{ $file['size'] }}</td></tr>
@endforeach
</tbody>
</x-table>
{{ $files->links() }}
<x-table dense id="dense-table">
<tbody>
<tr><td>dense-a.zip</td><td>1</td></tr>
<tr aria-selected="true"><td>dense-b.zip</td><td>2</td></tr>
</tbody>
</x-table>
</div>
BLADE);
app('view')->addNamespace('probe', $views);
Livewire::component('data-probe', DataProbe::class);
Route::middleware('web')->get('/data-probe', fn () => Blade::render(<<<'BLADE'
<!DOCTYPE html>
<html>
<head>
<x-theme-script />
@vite(config('livewire-material.showcase.vite'))
@livewireStyles
</head>
<body class="bg-surface">
<livewire:data-probe />
@livewireScripts
</body>
</html>
BLADE));
return visit('/data-probe')->waitForEvent('networkidle')
->assertScript("document.readyState === 'complete' && typeof window.Alpine !== 'undefined' && typeof window.Livewire !== 'undefined'");
}
it('sorts by a column and flips the direction on a second press', function () {
$first = "document.querySelector('tbody tr td').textContent.trim()";
$page = dataProbe()
->assertAttribute('#by-name', 'aria-sort', 'ascending')
->assertScript("{$first} === 'file-01.zip'");
$page->click('#by-name button')
->assertAttribute('#by-name', 'aria-sort', 'descending')
->assertScript("{$first} === 'file-25.zip'");
$page->click('#by-size button')
->assertAttribute('#by-size', 'aria-sort', 'ascending')
->assertScript("! document.querySelector('#by-name').hasAttribute('aria-sort')");
});
it('pages through Livewire results and marks the current page', function () {
$page = dataProbe()
->assertSee('110 of 25')
->assertAttribute('[data-md-pagination] [aria-current="page"]', 'aria-current', 'page')
->click('[data-md-pagination] button[aria-label="Go to page 3"]')
->assertSee('2125 of 25')
->assertSeeIn('[data-md-pagination] [aria-current="page"]', '3');
$page->click('[data-md-pagination] button[aria-label="Previous"]')
->assertSee('1120 of 25');
$page->resize(400, 800)
->assertSee('Page 2 of 3');
});
it('draws 52px rows, and 36px ones only in a table that asks to be dense', function () {
$row = fn (string $table): string => "document.querySelector('{$table} tbody tr').getBoundingClientRect().height";
dataProbe()
->assertScript("Math.abs(({$row('[data-md-table]:not([data-md-dense])')}) - 52) <= 1")
->assertScript("Math.abs(({$row('#dense-table')}) - 36) <= 1")
->assertScript("getComputedStyle(document.querySelector('#dense-table tr[aria-selected=\"true\"]')).backgroundColor !== 'rgba(0, 0, 0, 0)'")
->assertScript("getComputedStyle(document.querySelector('[data-md-table] thead th')).borderBottomWidth === '1px'");
});
it('lets the sort button be pressed anywhere in its 48px target', function () {
dataProbe()
->assertScript("(() => { const button = document.querySelector('#by-size [data-md-sort-header-button]'); const after = getComputedStyle(button, '::after'); return button.getBoundingClientRect().height < 48 && after.minHeight === '48px' && after.minWidth === '48px'; })()")
->assertScript("(() => { const button = document.querySelector('#by-size [data-md-sort-header-button]'); const box = button.getBoundingClientRect(); const probe = document.elementFromPoint(box.left + box.width / 2, box.top + box.height / 2 - 22); return probe === button; })()");
});
it('draws each page step 40px and lets it be pressed anywhere in its 48px target', function () {
dataProbe()
->assertScript("(() => { const step = document.querySelector('[data-md-pagination] button[aria-label=\"Go to page 2\"]'); const box = step.getBoundingClientRect(); return box.width === 40 && box.height === 40 && document.elementFromPoint(box.left + box.width / 2, box.top - 3) === step; })()")
->assertScript("getComputedStyle(document.querySelector('[data-md-pagination] [aria-current=\"page\"]')).backgroundColor !== 'rgba(0, 0, 0, 0)'");
});