fix(versions): keep the changelog series stable on reload (0.10 -> 0.1 bug)
The series rail synced ?series=<key> via Livewire #[Url] with a bare numeric key like "0.10". Livewire type-juggles that to 0.1 (the trailing zero is lost), so reloading while viewing the 0.10 series silently switched to 0.1 and rewrote the URL. Prefix the series key with a non-numeric "v" (?series=v0.10) so it round-trips as a string; the rail label still shows the dotted "0.10". Surfaced now that a 0.10 series exists. Browser-verified: selecting 0.10 -> ?series=v0.10, and it survives a reload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/v1-foundation
parent
d8da46151e
commit
8dfc11fd5d
|
|
@ -382,7 +382,10 @@ class Index extends Component
|
||||||
if ($rel['unreleased']) {
|
if ($rel['unreleased']) {
|
||||||
$key = 'unreleased';
|
$key = 'unreleased';
|
||||||
} elseif (preg_match('/^v?(\d+)\.(\d+)\./', $rel['version'], $m)) {
|
} elseif (preg_match('/^v?(\d+)\.(\d+)\./', $rel['version'], $m)) {
|
||||||
$key = $m[1].'.'.$m[2];
|
// Prefix the series key with a non-numeric 'v' so the ?series= URL value is never a
|
||||||
|
// bare decimal — Livewire's #[Url] otherwise type-juggles "0.10" → 0.1 (the trailing
|
||||||
|
// zero is lost), silently switching the selected series on reload. The label strips it.
|
||||||
|
$key = 'v'.$m[1].'.'.$m[2];
|
||||||
} else {
|
} else {
|
||||||
$key = 'other';
|
$key = 'other';
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +400,7 @@ class Index extends Component
|
||||||
'label' => match ($key) {
|
'label' => match ($key) {
|
||||||
'unreleased' => __('versions.unreleased'),
|
'unreleased' => __('versions.unreleased'),
|
||||||
'other' => __('versions.series_other'),
|
'other' => __('versions.series_other'),
|
||||||
default => $key,
|
default => ltrim($key, 'v'), // display the dotted series (the 'v' is only for the URL)
|
||||||
},
|
},
|
||||||
'unreleased' => $key === 'unreleased',
|
'unreleased' => $key === 'unreleased',
|
||||||
'count' => count($rels),
|
'count' => count($rels),
|
||||||
|
|
@ -415,7 +418,7 @@ class Index extends Component
|
||||||
return $ra <=> $rb;
|
return $ra <=> $rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ra === 1 ? version_compare($b['key'].'.0', $a['key'].'.0') : 0;
|
return $ra === 1 ? version_compare(ltrim($b['key'], 'v').'.0', ltrim($a['key'], 'v').'.0') : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
return $series;
|
return $series;
|
||||||
|
|
@ -628,7 +631,7 @@ class Index extends Component
|
||||||
|
|
||||||
// Mark the series that holds the running version (a small "installed" cue in the rail).
|
// Mark the series that holds the running version (a small "installed" cue in the rail).
|
||||||
$installed = ltrim((string) config('clusev.version'), 'vV');
|
$installed = ltrim((string) config('clusev.version'), 'vV');
|
||||||
$currentSeries = preg_match('/^(\d+)\.(\d+)\./', $installed, $m) ? $m[1].'.'.$m[2] : null;
|
$currentSeries = preg_match('/^(\d+)\.(\d+)\./', $installed, $m) ? 'v'.$m[1].'.'.$m[2] : null;
|
||||||
|
|
||||||
// Passive (no network): cached remote result if a check has run, else local .git.
|
// Passive (no network): cached remote result if a check has run, else local .git.
|
||||||
$latestTag = $this->resolveLatestTag($this->channel());
|
$latestTag = $this->resolveLatestTag($this->channel());
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ class VersionsChangelogTest extends TestCase
|
||||||
// …selecting the 0.1 series brings it into view and jumps back to page 1.
|
// …selecting the 0.1 series brings it into view and jumps back to page 1.
|
||||||
Livewire::test(Index::class)
|
Livewire::test(Index::class)
|
||||||
->set('changelogPage', 3)
|
->set('changelogPage', 3)
|
||||||
->call('selectSeries', '0.1')
|
->call('selectSeries', 'v0.1')
|
||||||
->assertSet('series', '0.1')
|
->assertSet('series', 'v0.1')
|
||||||
->assertSet('changelogPage', 1)
|
->assertSet('changelogPage', 1)
|
||||||
->assertSee('v0.1.0');
|
->assertSee('v0.1.0');
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ class VersionsChangelogTest extends TestCase
|
||||||
public function test_a_long_series_is_paginated_to_the_per_page_limit(): void
|
public function test_a_long_series_is_paginated_to_the_per_page_limit(): void
|
||||||
{
|
{
|
||||||
Livewire::test(Index::class)
|
Livewire::test(Index::class)
|
||||||
->call('selectSeries', '0.9')
|
->call('selectSeries', 'v0.9')
|
||||||
->assertViewHas('totalPages', fn ($total): bool => $total > 1)
|
->assertViewHas('totalPages', fn ($total): bool => $total > 1)
|
||||||
->assertViewHas('pagedReleases', fn ($releases): bool => is_array($releases) && count($releases) <= 8);
|
->assertViewHas('pagedReleases', fn ($releases): bool => is_array($releases) && count($releases) <= 8);
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ class VersionsChangelogTest extends TestCase
|
||||||
$pageOne = null;
|
$pageOne = null;
|
||||||
|
|
||||||
Livewire::test(Index::class)
|
Livewire::test(Index::class)
|
||||||
->call('selectSeries', '0.9')
|
->call('selectSeries', 'v0.9')
|
||||||
->assertViewHas('pagedReleases', function ($releases) use (&$pageOne): bool {
|
->assertViewHas('pagedReleases', function ($releases) use (&$pageOne): bool {
|
||||||
$pageOne = array_column($releases, 'version');
|
$pageOne = array_column($releases, 'version');
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ class VersionsChangelogTest extends TestCase
|
||||||
});
|
});
|
||||||
|
|
||||||
Livewire::test(Index::class)
|
Livewire::test(Index::class)
|
||||||
->call('selectSeries', '0.9')
|
->call('selectSeries', 'v0.9')
|
||||||
->call('gotoChangelogPage', 2)
|
->call('gotoChangelogPage', 2)
|
||||||
->assertViewHas('pagedReleases', function ($releases) use (&$pageOne): bool {
|
->assertViewHas('pagedReleases', function ($releases) use (&$pageOne): bool {
|
||||||
$pageTwo = array_column($releases, 'version');
|
$pageTwo = array_column($releases, 'version');
|
||||||
|
|
@ -121,7 +121,7 @@ class VersionsChangelogTest extends TestCase
|
||||||
public function test_an_out_of_range_page_is_clamped(): void
|
public function test_an_out_of_range_page_is_clamped(): void
|
||||||
{
|
{
|
||||||
Livewire::test(Index::class)
|
Livewire::test(Index::class)
|
||||||
->call('selectSeries', '0.9')
|
->call('selectSeries', 'v0.9')
|
||||||
->call('gotoChangelogPage', 9999)
|
->call('gotoChangelogPage', 9999)
|
||||||
->assertViewHas('changelogPage', fn ($page): bool => $page >= 1)
|
->assertViewHas('changelogPage', fn ($page): bool => $page >= 1)
|
||||||
->assertViewHas('pagedReleases', fn ($releases): bool => is_array($releases) && count($releases) >= 1)
|
->assertViewHas('pagedReleases', fn ($releases): bool => is_array($releases) && count($releases) >= 1)
|
||||||
|
|
@ -132,7 +132,7 @@ class VersionsChangelogTest extends TestCase
|
||||||
{
|
{
|
||||||
config()->set('clusev.version', '0.7.3');
|
config()->set('clusev.version', '0.7.3');
|
||||||
|
|
||||||
Livewire::test(Index::class)->assertViewHas('currentSeries', '0.7');
|
Livewire::test(Index::class)->assertViewHas('currentSeries', 'v0.7');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_total_release_count_is_passed_for_the_panel_subtitle(): void
|
public function test_total_release_count_is_passed_for_the_panel_subtitle(): void
|
||||||
|
|
@ -143,12 +143,22 @@ class VersionsChangelogTest extends TestCase
|
||||||
public function test_series_and_page_are_deep_linkable_via_the_query_string(): void
|
public function test_series_and_page_are_deep_linkable_via_the_query_string(): void
|
||||||
{
|
{
|
||||||
// ?series= + ?page= drive the view so a series/page is shareable and survives reload.
|
// ?series= + ?page= drive the view so a series/page is shareable and survives reload.
|
||||||
Livewire::withQueryParams(['series' => '0.9', 'page' => 2])
|
Livewire::withQueryParams(['series' => 'v0.9', 'page' => 2])
|
||||||
->test(Index::class)
|
->test(Index::class)
|
||||||
->assertSet('series', '0.9')
|
->assertSet('series', 'v0.9')
|
||||||
->assertSet('changelogPage', 2);
|
->assertSet('changelogPage', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_a_trailing_zero_series_like_0_10_survives_a_deep_link(): void
|
||||||
|
{
|
||||||
|
// The 'v' prefix keeps the ?series= value non-numeric, so "0.10" is NOT type-juggled to
|
||||||
|
// "0.1" on reload (which used to silently switch the selected series).
|
||||||
|
Livewire::withQueryParams(['series' => 'v0.10'])
|
||||||
|
->test(Index::class)
|
||||||
|
->assertSet('series', 'v0.10')
|
||||||
|
->assertViewHas('activeKey', 'v0.10');
|
||||||
|
}
|
||||||
|
|
||||||
public function test_chevron_right_icon_renders_a_real_path(): void
|
public function test_chevron_right_icon_renders_a_real_path(): void
|
||||||
{
|
{
|
||||||
// The accordion disclosure caret and the pagination "Next" button both use x-icon
|
// The accordion disclosure caret and the pagination "Next" button both use x-icon
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue