From 4810d1f40e4270cad75ce87bf3e43f7adf17a218 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 14 Aug 2026 10:49:45 +0100 Subject: [PATCH] prevent moved and hidden child items reappearing when reordering nav children Co-Authored-By: Claude Fable 5 --- src/CP/Navigation/NavBuilder.php | 4 ++ tests/CP/Navigation/NavPreferencesTest.php | 57 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/CP/Navigation/NavBuilder.php b/src/CP/Navigation/NavBuilder.php index 6acb2fbd36e..aef291e037e 100644 --- a/src/CP/Navigation/NavBuilder.php +++ b/src/CP/Navigation/NavBuilder.php @@ -20,6 +20,7 @@ class NavBuilder protected $itemsKeyedById = null; protected $withHidden = false; protected $itemsWithChildrenClosures = []; + protected $itemsRemovedFromChildren = []; protected $sections = []; protected $sectionsEmptiedByAuthorization = []; protected $sectionsOriginalItemIds = []; @@ -736,6 +737,7 @@ protected function userModifyItem($item, $config, $section) protected function userModifyItemChildren($item, $childrenOverrides, $section, $reorder) { $itemChildren = collect($item->original()->resolveChildren()->children()) + ->reject(fn ($child) => in_array($child->id(), $this->itemsRemovedFromChildren)) ->each(fn ($item, $index) => $item->order($index + 1000)) ->keyBy ->id(); @@ -884,6 +886,8 @@ protected function userRemoveItemFromChildren($item) return; } + $this->itemsRemovedFromChildren[] = $item->id(); + if ($this->urlsUnresolvedChildren->has($parent->id())) { $this->urlsUnresolvedChildren[$parent->id()] = collect($this->urlsUnresolvedChildren[$parent->id()]) ->reject(fn ($url) => $url === $item->url()) diff --git a/tests/CP/Navigation/NavPreferencesTest.php b/tests/CP/Navigation/NavPreferencesTest.php index b9ef643821c..56b10f5bd3a 100644 --- a/tests/CP/Navigation/NavPreferencesTest.php +++ b/tests/CP/Navigation/NavPreferencesTest.php @@ -1128,6 +1128,63 @@ public function it_can_move_items_out_of_the_children_of_an_item_in_the_same_sec $this->assertArrayNotHasKey('Pages', $nav->get('Content')->keyBy->display()->get('Collections')->resolveChildren()->children()->map->display()->all()); } + #[Test] + public function it_doesnt_show_moved_child_items_in_original_parent_when_reordering_remaining_children() + { + Facades\Collection::make('events')->title('Events')->save(); + + $nav = $this->buildNavWithPreferences([ + 'content' => [ + 'reorder' => true, + 'items' => [ + 'content::collections::pages' => '@move', + 'content::collections' => [ + 'action' => '@modify', + 'reorder' => true, + 'children' => [ + 'content::collections::events' => '@inherit', + 'content::collections::articles' => '@inherit', + ], + ], + ], + ], + ]); + + $contentItems = $nav->get('Content')->keyBy->display(); + + $this->assertEquals(['Pages', 'Collections', 'Navigation', 'Taxonomies', 'Assets', 'Globals'], $contentItems->keys()->all()); + $this->assertEquals(['Events', 'Articles'], $contentItems->get('Collections')->children()->map->display()->all()); + + Request::swap(Request::create('http://localhost/cp/collections/pages')); + + $this->assertTrue($contentItems->get('Pages')->isActive()); + $this->assertFalse($contentItems->get('Collections')->isActive()); + } + + #[Test] + public function it_doesnt_show_hidden_child_items_in_original_parent_when_reordering_remaining_children() + { + Facades\Collection::make('events')->title('Events')->save(); + + $nav = $this->buildNavWithPreferences([ + 'content' => [ + 'items' => [ + 'content::collections::pages' => '@hide', + 'content::collections' => [ + 'action' => '@modify', + 'reorder' => true, + 'children' => [ + 'content::collections::events' => '@inherit', + 'content::collections::articles' => '@inherit', + ], + ], + ], + ], + ]); + + $this->assertEquals(['Events', 'Articles'], $nav->get('Content')->keyBy->display()->get('Collections')->children()->map->display()->all()); + } + #[Test] public function it_can_move_child_items_into_another_items_children() {