diff --git a/src/CP/Navigation/NavItem.php b/src/CP/Navigation/NavItem.php index 89f2742be0..eca3577846 100644 --- a/src/CP/Navigation/NavItem.php +++ b/src/CP/Navigation/NavItem.php @@ -149,6 +149,7 @@ protected function generateActivePatternForCpUrl($url) $cpUrl = url(config('statamic.cp.route')).'/'; $relativeUrl = str_replace($cpUrl, '', URL::removeQueryAndFragment($url)); + $relativeUrl = rtrim($relativeUrl, '/'); return $relativeUrl.'(/(.*)?|$)'; } diff --git a/tests/CP/Navigation/ActiveNavItemTest.php b/tests/CP/Navigation/ActiveNavItemTest.php index 4dc1c30fc5..16ddce8015 100644 --- a/tests/CP/Navigation/ActiveNavItemTest.php +++ b/tests/CP/Navigation/ActiveNavItemTest.php @@ -12,6 +12,7 @@ use Statamic\Facades; use Statamic\Facades\Blink; use Statamic\Facades\CP\Nav; +use Statamic\Facades\URL; use Statamic\Facades\User; use Tests\FakesRoles; use Tests\PreventSavingStacheItemsToDisk; @@ -35,6 +36,13 @@ public function setUp(): void Facades\Form::shouldReceive('all')->andReturn(collect()); } + public function tearDown(): void + { + URL::enforceTrailingSlashes(false); + + parent::tearDown(); + } + protected function resolveApplicationConfiguration($app) { parent::resolveApplicationConfiguration($app); @@ -408,6 +416,34 @@ public function it_resolves_extension_children_closure_and_can_check_when_parent $this->assertTrue($this->getItemByDisplay($seoPro->children(), 'Section Defaults')->isActive()); } + #[Test] + public function it_properly_handles_is_active_checks_when_trailing_slashes_are_enforced() + { + URL::enforceTrailingSlashes(); + + Nav::clearCachedUrls(); + + $parent = Nav::create('parent') + ->section('test') + ->url('http://localhost/cp/parent') + ->children([ + $hello = Nav::create('hello')->url('http://localhost/cp/hello'), + $world = Nav::create('world')->url('http://localhost/cp/world'), + ]); + + // Test active status with trailing slash in request URL + Request::swap(Request::create('http://localhost/cp/hello/')); + $this->assertTrue($parent->isActive()); + $this->assertTrue($hello->isActive()); + $this->assertFalse($world->isActive()); + + // Test active status on descendant with trailing slash + Request::swap(Request::create('http://localhost/cp/hello/nested/path/')); + $this->assertTrue($parent->isActive()); + $this->assertTrue($hello->isActive()); + $this->assertFalse($world->isActive()); + } + #[Test] public function it_properly_handles_various_edge_cases_when_checking_is_active_on_descendants_of_nav_children() {