From 597341a9e6a7e9e526884a73b08076ce3e96a8d7 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Sat, 8 Aug 2026 10:03:58 -0400 Subject: [PATCH 1/2] Return the nav item when creating one without a display name NavItem::display() is a fluent get-or-set, so passing null meant "get". Chaining create() off it returned the current display (null) instead of the item, and pushed that null into the registered items. Co-Authored-By: Claude Opus 5 (1M context) --- src/CP/Navigation/Nav.php | 4 +++- tests/CP/Navigation/NavTest.php | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/CP/Navigation/Nav.php b/src/CP/Navigation/Nav.php index 73b69d9c66a..b8e342debeb 100644 --- a/src/CP/Navigation/Nav.php +++ b/src/CP/Navigation/Nav.php @@ -26,7 +26,9 @@ public function extend(Closure $callback) */ public function create($name) { - $item = (new NavItem)->display($name); + $item = new NavItem; + + $item->display($name); $this->items[] = $item; diff --git a/tests/CP/Navigation/NavTest.php b/tests/CP/Navigation/NavTest.php index 29ce26e9720..9c8cced4891 100644 --- a/tests/CP/Navigation/NavTest.php +++ b/tests/CP/Navigation/NavTest.php @@ -69,6 +69,26 @@ public function it_can_more_explicitly_create_a_nav_item() $this->assertEquals('http://localhost/r2', $item->url()); } + #[Test] + public function it_returns_the_nav_item_when_created_without_a_display_name() + { + $item = Nav::create(null); + + $this->assertInstanceOf(NavItem::class, $item); + $this->assertNull($item->display()); + $this->assertEquals([$item], Nav::items()); + } + + #[Test] + public function it_returns_the_nav_item_when_created_without_a_display_name_using_the_item_alias() + { + $item = Nav::item(null); + + $this->assertInstanceOf(NavItem::class, $item); + $this->assertNull($item->display()); + $this->assertEquals([$item], Nav::items()); + } + #[Test] public function it_can_create_a_nav_item_with_a_more_custom_config() { From c314c34b0bffe02b9f771506b9e1b834adcd36cd Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Sat, 8 Aug 2026 10:04:02 -0400 Subject: [PATCH 2/2] Fall back to the handle when a form has no title Matches collections, taxonomies, asset containers, globals, roles, and user groups, which all humanize the handle when no title is set. Without it, a title-less form gave a null nav item display and broke the CP nav. Co-Authored-By: Claude Opus 5 (1M context) --- src/Forms/Form.php | 7 ++++++- tests/CP/Navigation/CoreNavTest.php | 12 ++++++++++++ tests/Forms/FormTest.php | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Forms/Form.php b/src/Forms/Form.php index f55244d94cb..c8833c92c3d 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -75,7 +75,12 @@ public function handle($handle = null) */ public function title($title = null) { - return $this->fluentlyGetOrSet('title')->args(func_get_args()); + return $this + ->fluentlyGetOrSet('title') + ->getter(function ($title) { + return $title ?? ucfirst($this->handle); + }) + ->args(func_get_args()); } /** diff --git a/tests/CP/Navigation/CoreNavTest.php b/tests/CP/Navigation/CoreNavTest.php index 804b4ba86f8..46f4cbaf4b4 100644 --- a/tests/CP/Navigation/CoreNavTest.php +++ b/tests/CP/Navigation/CoreNavTest.php @@ -221,6 +221,18 @@ public function it_doesnt_build_globals_children_from_sites_that_the_user_is_not $this->assertEqualsCanonicalizing($expected, $actual); } + #[Test] + public function it_builds_the_nav_when_a_form_has_no_title() + { + Facades\Form::make('contact_us')->save(); + + $this->actingAs(tap(User::make()->makeSuper())->save()); + + $forms = $this->build()->get('Tools')->keyBy->display()->get('Forms'); + + $this->assertEquals(['Contact_us'], $forms->children()->map->display()->all()); + } + protected function build() { return Nav::build()->pluck('items', 'display'); diff --git a/tests/Forms/FormTest.php b/tests/Forms/FormTest.php index 8401c3adb63..fe31e2da64b 100644 --- a/tests/Forms/FormTest.php +++ b/tests/Forms/FormTest.php @@ -11,6 +11,7 @@ use Statamic\Events\FormDeleting; use Statamic\Events\FormSaved; use Statamic\Events\FormSaving; +use Statamic\Facades\File; use Statamic\Facades\Form; use Statamic\Fields\Blueprint; use Tests\TestCase; @@ -24,6 +25,28 @@ public function setUp(): void Form::all()->each->delete(); } + #[Test] + public function it_falls_back_to_the_handle_for_the_title() + { + $form = Form::make('contact_us'); + + $this->assertEquals('Contact_us', $form->title()); + + $form->title('Contact Us'); + + $this->assertEquals('Contact Us', $form->title()); + } + + #[Test] + public function it_doesnt_save_the_fallback_title() + { + $form = Form::make('contact_us'); + + $form->save(); + + $this->assertStringNotContainsString('title', File::get($form->path())); + } + #[Test] public function it_saves_a_form() {