diff --git a/templates/menu.html.twig b/templates/menu.html.twig index a18c842c6a..8bfc99b8c3 100644 --- a/templates/menu.html.twig +++ b/templates/menu.html.twig @@ -1,72 +1,124 @@ {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #} - +{% endset %} -{% macro render_html_attributes(item) %} - {% for attribute_name, attribute_value in item.htmlAttributes %} - {{ attribute_name }}="{{ attribute_value|e('html') }}" - {% endfor %} -{% endmacro %} + + {{ main_menu_contents }} + {% macro render_menu_badge(badge) %} {%- set badge_attributes = badge.htmlStyle ? badge.htmlAttributes|merge({style: badge.htmlStyle}) : badge.htmlAttributes -%} - {{ badge.content }} + {{ badge.content }} {%- endmacro %} - -{% macro render_menu_item(item) %} - {% if item.isMenuSection %} - - {% if item.icon is not empty %}{% endif %} - - {{ item.label|trans|raw }} - - {% if item.badge %} - {{ _self.render_menu_badge(item.badge) }} - {% endif %} - - {% else %} - - {% if item.icon is not empty %}{% endif %} - - {{ item.label|trans|raw }} - - {% if item.hasSubItems %}{% endif %} - {% if item.hasVisibleBadge %} - {{ _self.render_menu_badge(item.badge) }} - {% endif %} - - {% endif %} -{% endmacro %} diff --git a/tests/Functional/Apps/DefaultApp/src/Controller/Dashboard/MenuDashboardController.php b/tests/Functional/Apps/DefaultApp/src/Controller/Dashboard/MenuDashboardController.php index 62188011ac..b222d10b60 100644 --- a/tests/Functional/Apps/DefaultApp/src/Controller/Dashboard/MenuDashboardController.php +++ b/tests/Functional/Apps/DefaultApp/src/Controller/Dashboard/MenuDashboardController.php @@ -64,7 +64,7 @@ public function configureMenuItems(): iterable yield MenuItem::subMenu('Settings', 'fas fa-cog')->setSubItems([ MenuItem::linkToUrl('General', 'fas fa-sliders-h', 'https://example.com/settings/general'), MenuItem::linkToUrl('Security', 'fas fa-shield-alt', 'https://example.com/settings/security'), - ]); + ])->keepOpen(); // external link section yield MenuItem::section('External Links'); diff --git a/tests/Functional/Customization/Dashboard/RelativeUrlsTest.php b/tests/Functional/Customization/Dashboard/RelativeUrlsTest.php index b4e3f5ec2d..dcdb621409 100644 --- a/tests/Functional/Customization/Dashboard/RelativeUrlsTest.php +++ b/tests/Functional/Customization/Dashboard/RelativeUrlsTest.php @@ -40,7 +40,7 @@ public function testRelativeUrlsAreGenerated(): void static::assertResponseIsSuccessful(); // check that menu links are relative (not absolute with http://) - $menuLinks = $crawler->filter('.menu-item a, nav a'); + $menuLinks = $crawler->filter('.ea-sidebar-item a, nav a'); if ($menuLinks->count() > 0) { $firstHref = $menuLinks->first()->attr('href'); diff --git a/tests/Functional/Customization/Dashboard/TitleTest.php b/tests/Functional/Customization/Dashboard/TitleTest.php index 9141252b77..5cc772bd0f 100644 --- a/tests/Functional/Customization/Dashboard/TitleTest.php +++ b/tests/Functional/Customization/Dashboard/TitleTest.php @@ -52,7 +52,7 @@ public function testCustomTitleIsRenderedInHeader(): void static::assertResponseIsSuccessful(); // verify the title appears in the main header/logo area - $headerTitle = $crawler->filter('.main-header .logo-custom')->text(); + $headerTitle = $crawler->filter('.ea-sidebar-header .logo-custom')->text(); static::assertStringContainsString('Custom Dashboard Title', $headerTitle); } diff --git a/tests/Functional/Default/Dashboard/EntityTranslationsTest.php b/tests/Functional/Default/Dashboard/EntityTranslationsTest.php index 6815f2265b..4bcad25307 100644 --- a/tests/Functional/Default/Dashboard/EntityTranslationsTest.php +++ b/tests/Functional/Default/Dashboard/EntityTranslationsTest.php @@ -68,7 +68,7 @@ public function testMenuItemUsesEntityPluralTranslation(): void static::assertResponseIsSuccessful(); // when menu item label is null, it should use the entity's plural translation - $menuItems = $crawler->filter('.menu-item a'); + $menuItems = $crawler->filter('.ea-sidebar-item a'); $menuTexts = []; $menuItems->each(static function ($node) use (&$menuTexts) { $menuTexts[] = trim($node->text()); diff --git a/tests/Functional/Default/Dashboard/LayoutTest.php b/tests/Functional/Default/Dashboard/LayoutTest.php index 8b762fa6ff..428b1f222f 100644 --- a/tests/Functional/Default/Dashboard/LayoutTest.php +++ b/tests/Functional/Default/Dashboard/LayoutTest.php @@ -124,7 +124,7 @@ public function testLayoutContainsMainStructuralElements(): void // verify the main layout structure static::assertGreaterThan(0, $crawler->filter('.wrapper')->count(), 'Wrapper should exist'); static::assertGreaterThan(0, $crawler->filter('.sidebar-wrapper')->count(), 'Sidebar wrapper should exist'); - static::assertGreaterThan(0, $crawler->filter('.sidebar')->count(), 'Sidebar should exist'); + static::assertGreaterThan(0, $crawler->filter('.ea-sidebar')->count(), 'Sidebar should exist'); static::assertGreaterThan(0, $crawler->filter('.main-content')->count(), 'Main content should exist'); } diff --git a/tests/Functional/Default/Dashboard/MenuTest.php b/tests/Functional/Default/Dashboard/MenuTest.php index 146ddfe519..2d58493478 100644 --- a/tests/Functional/Default/Dashboard/MenuTest.php +++ b/tests/Functional/Default/Dashboard/MenuTest.php @@ -43,15 +43,18 @@ public function testMainMenuExists(): void static::assertCount(1, $mainMenu); } - public function testMenuContainsUnorderedList(): void + public function testMenuContainsGroupsWithUnorderedLists(): void { $crawler = $this->client->request('GET', $this->generateIndexUrl()); static::assertResponseIsSuccessful(); - // menu should have a ul.menu element - $menuList = $crawler->filter('#main-menu ul.menu'); - static::assertCount(1, $menuList); + // menu items are chunked into groups, each with its own list + $menuGroups = $crawler->filter('#main-menu .ea-sidebar-group'); + static::assertGreaterThan(0, $menuGroups->count()); + + $menuLists = $crawler->filter('#main-menu ul.ea-sidebar-group-items'); + static::assertSame($menuGroups->count(), $menuLists->count()); } public function testDashboardLinkIsRendered(): void @@ -61,7 +64,7 @@ public function testDashboardLinkIsRendered(): void static::assertResponseIsSuccessful(); // dashboard link should be present - $dashboardLink = $crawler->filter('.menu-item a.menu-item-contents'); + $dashboardLink = $crawler->filter('.ea-sidebar-item a.ea-sidebar-item-link'); static::assertGreaterThan(0, $dashboardLink->count()); $html = $crawler->html(); @@ -74,8 +77,8 @@ public function testMenuSectionsAreRendered(): void static::assertResponseIsSuccessful(); - // menu sections should be present - $sectionHeaders = $crawler->filter('.menu-header'); + // menu sections should be present as group labels + $sectionHeaders = $crawler->filter('.ea-sidebar-group-label'); static::assertGreaterThan(0, $sectionHeaders->count()); // check for specific section labels @@ -108,7 +111,7 @@ public function testLinkToMenuItemsAreRendered(): void // the linkTo(CategoryCrudController::class) item (auto-derived label) should also render // and its link should be clickable (have an href) - $menuLinks = $crawler->filter('.menu-item a.menu-item-contents:not(.submenu-toggle)'); + $menuLinks = $crawler->filter('.ea-sidebar-item a.ea-sidebar-item-link'); $hasLinkToItem = false; foreach ($menuLinks as $link) { $href = $link->getAttribute('href'); @@ -127,7 +130,7 @@ public function testLinkToMenuItemKeepsCustomQueryParameters(): void static::assertResponseIsSuccessful(); // the "Categories" linkTo() menu item has a custom query parameter set via setQueryParameter() - $categoriesLink = $crawler->filter('.menu-item a.menu-item-contents')->reduce( + $categoriesLink = $crawler->filter('.ea-sidebar-item a.ea-sidebar-item-link')->reduce( static fn ($node) => str_contains($node->text(), 'Categories') ); static::assertGreaterThan(0, $categoriesLink->count()); @@ -141,7 +144,7 @@ public function testLinkToMenuItemBadgeIsRendered(): void static::assertResponseIsSuccessful(); // the linkTo() Blog Posts item has a badge with 'New' - $badges = $crawler->filter('.menu-item-badge'); + $badges = $crawler->filter('.ea-sidebar-badge'); static::assertGreaterThan(0, $badges->count()); $html = $crawler->html(); @@ -155,7 +158,7 @@ public function testMenuItemBadgesAreRendered(): void static::assertResponseIsSuccessful(); // badge should be present for Blog Posts - $badges = $crawler->filter('.menu-item-badge'); + $badges = $crawler->filter('.ea-sidebar-badge'); static::assertGreaterThan(0, $badges->count()); $html = $crawler->html(); @@ -169,7 +172,7 @@ public function testSubmenusAreRendered(): void static::assertResponseIsSuccessful(); // submenu items should be present - $submenus = $crawler->filter('.has-submenu'); + $submenus = $crawler->filter('.ea-sidebar-item.has-submenu'); static::assertGreaterThan(0, $submenus->count()); // check for submenu labels @@ -185,7 +188,7 @@ public function testSubmenuItemsAreRendered(): void static::assertResponseIsSuccessful(); // submenu list should exist - $submenuLists = $crawler->filter('.submenu'); + $submenuLists = $crawler->filter('.ea-sidebar-submenu'); static::assertGreaterThan(0, $submenuLists->count()); // check for specific submenu items @@ -228,7 +231,7 @@ public function testMenuIconsAreRendered(): void static::assertResponseIsSuccessful(); // menu icons should be present - $menuIcons = $crawler->filter('.menu-icon'); + $menuIcons = $crawler->filter('.ea-sidebar-item-icon'); static::assertGreaterThan(0, $menuIcons->count()); } @@ -240,15 +243,14 @@ public function testActiveMenuItemClassCanBeApplied(): void static::assertResponseIsSuccessful(); // test that menu items can potentially have the active class - // the active class is applied via JavaScript or server-side based on URL matching + // the active class is applied server-side based on URL matching // here we verify the menu structure supports the active state - $menuItems = $crawler->filter('li.menu-item'); + $menuItems = $crawler->filter('li.ea-sidebar-item'); static::assertGreaterThan(0, $menuItems->count()); - // check that the menu contains items that can receive active class - // the menu item HTML structure includes class attribute where 'active' can be added + // check that the menu contains items that can receive the 'is-active' class $html = $crawler->html(); - static::assertStringContainsString('menu-item', $html); + static::assertStringContainsString('ea-sidebar-item', $html); } public function testMenuItemsAreClickable(): void @@ -257,8 +259,8 @@ public function testMenuItemsAreClickable(): void $crawler = $this->client->request('GET', $this->generateIndexUrl()); static::assertResponseIsSuccessful(); - // find menu item links (excluding submenu toggles that have # hrefs) - $menuLinks = $crawler->filter('.menu-item a.menu-item-contents:not(.submenu-toggle)'); + // find menu item links (items with submenus render a