Skip to content

Commit 26b2b1f

Browse files
authored
Fix the rows-per-page selector on the request listing (#167)
The selector's inline onchange handler built a URL with `new URL(location)`. Inline event handlers run inside `with (document)`, where the bare identifier `URL` resolves to `document.URL` — a string, not the constructor — so the handler threw "URL is not a constructor" and never navigated. Picking a page size silently did nothing. Make the selector a control of the filter form instead of a JS navigation: `name="per_page"` plus `form="ri-filters"` associates it across the DOM, and submitting the form drops the cursor, so a new page size lands on the first page while the active filters carry over. The form's hidden per_page input is gone, since the select now carries that value itself.
1 parent 9619187 commit 26b2b1f

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

publishable/views/index.blade.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
<h2 id="requests-heading" class="sr-only">Requests</h2>
5757

5858
{{-- Filters --}}
59-
<form method="get" class="mb-4 flex flex-wrap items-center gap-2">
59+
<form method="get" id="ri-filters" class="mb-4 flex flex-wrap items-center gap-2">
6060
<input name="trace_id" value="{{ old('trace_id') }}" placeholder="trace id" class="h-10 w-44 rounded-xl border bg-white px-3.5 font-mono text-sm text-slate-950 shadow-xs placeholder:text-slate-400 dark:bg-slate-900 dark:text-white">
6161
<input name="url" value="{{ old('url') }}" placeholder="url %like%" class="h-10 w-56 rounded-xl border bg-white px-3.5 font-mono text-sm text-slate-950 shadow-xs placeholder:text-slate-400 dark:bg-slate-900 dark:text-white">
6262
<input type="datetime-local" name="from" value="{{ old('from') }}" title="From" class="h-10 rounded-xl border bg-white px-3.5 font-mono text-sm text-slate-500 shadow-xs dark:bg-slate-900 dark:text-slate-400">
@@ -69,7 +69,6 @@
6969
</label>
7070
@endforeach
7171
</div>
72-
<input type="hidden" name="per_page" value="{{ $perPage }}">
7372
<div class="ml-auto flex gap-2">
7473
<button type="submit" class="h-10 rounded-xl bg-insurance px-4 text-sm font-bold text-white shadow-sm shadow-indigo-900/20 transition-transform hover:-translate-y-0.5 hover:bg-indigo-700 active:translate-y-0 motion-reduce:transform-none">Filter</button>
7574
<a href="{{ url()->current() }}" class="grid h-10 place-items-center rounded-xl px-4 text-sm font-bold text-slate-600 transition-colors hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800">Clear</a>
@@ -174,8 +173,9 @@ class="h-8 rounded-lg border border-red-500/40 px-3.5 text-xs font-bold text-red
174173
<div class="mt-4 flex flex-wrap items-center justify-between gap-3 font-mono text-xs text-slate-500 dark:text-slate-400">
175174
<span>priority is zero-based · 0 = highest</span>
176175
<div class="flex items-center gap-4">
176+
{{-- The filter form has no cursor field, so a new page size lands on the first page. --}}
177177
<label class="flex items-center gap-2">rows
178-
<select onchange="const u=new URL(location); u.searchParams.set('per_page', this.value); u.searchParams.delete('cursor'); location = u;"
178+
<select name="per_page" form="ri-filters" onchange="this.form.requestSubmit()"
179179
class="h-9 rounded-lg border bg-white px-2 text-ink shadow-xs dark:bg-slate-900 dark:text-white">
180180
@foreach([25, 50, 100, 250, 500, 1000] as $size)
181181
<option value="{{ $size }}" @selected($perPage === $size)>{{ $size }}</option>

tests/Unit/WebUiSmokeTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,35 @@ public function test_index_page_falls_back_to_default_page_size_for_invalid_per_
4040
->assertViewHas('perPage', 25);
4141
}
4242

43+
public function test_page_size_selector_submits_the_filter_form(): void
44+
{
45+
RequestInsurance::factory(30)->create(['state' => State::READY]);
46+
47+
$html = $this->get(route('request-insurances.index', ['per_page' => 50]))
48+
->assertOk()
49+
->getContent();
50+
51+
$this->assertStringContainsString('<form method="get" id="ri-filters"', $html);
52+
$this->assertMatchesRegularExpression('/<select[^>]*name="per_page"[^>]*form="ri-filters"/', $html);
53+
// The selector must be the only per_page control, or the form serializes a
54+
// stale page size alongside the chosen one.
55+
$this->assertSame(1, substr_count($html, 'name="per_page"'));
56+
$this->assertStringContainsString('<option value="50" selected>', $html);
57+
}
58+
59+
public function test_page_size_applies_when_submitted_with_blank_filter_fields(): void
60+
{
61+
RequestInsurance::factory(30)->create(['state' => State::READY]);
62+
63+
// The shape the filter form submits: every unused field present but empty.
64+
$paginator = $this->get(route('request-insurances.index', [
65+
'trace_id' => '', 'url' => '', 'from' => '', 'to' => '', 'per_page' => 50,
66+
]))->assertOk()->viewData('requestInsurances');
67+
68+
$this->assertSame(50, $paginator->perPage());
69+
$this->assertCount(30, $paginator->items());
70+
}
71+
4372
public function test_show_page_renders(): void
4473
{
4574
$this->authenticate();

0 commit comments

Comments
 (0)