From 2899ecac8ccc60b57a7736aead007259bd9b69c1 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 8 Mar 2023 11:13:18 +0800 Subject: [PATCH 1/2] Define groups for menu items --- .../Volo/Abp/Ui/Navigation/ApplicationMenu.cs | 19 +++++- .../Navigation/ApplicationMenuExtensions.cs | 50 ++++++++++++++ .../Abp/Ui/Navigation/ApplicationMenuGroup.cs | 67 +++++++++++++++++++ .../Ui/Navigation/ApplicationMenuGroupList.cs | 36 ++++++++++ .../Abp/Ui/Navigation/ApplicationMenuItem.cs | 7 ++ .../Volo/Abp/Ui/Navigation/IHasMenuGroups.cs | 9 +++ .../Volo/Abp/Ui/Navigation/MenuManager.cs | 18 +++++ .../Navigation/AbpUiNavigationTestModule.cs | 1 + .../Abp/Ui/Navigation/MenuManager_Tests.cs | 46 ++++++++++++- 9 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs create mode 100644 framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs create mode 100644 framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs index 1a69de7f2c..9f48aebf97 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using JetBrains.Annotations; using Volo.Abp.Data; @@ -5,7 +6,7 @@ using Volo.Abp.UI.Navigation; namespace Volo.Abp.UI.Navigation; -public class ApplicationMenu : IHasMenuItems +public class ApplicationMenu : IHasMenuItems, IHasMenuGroups { /// /// Unique name of the menu in the application. @@ -31,6 +32,10 @@ public class ApplicationMenu : IHasMenuItems [NotNull] public ApplicationMenuItemList Items { get; } + /// + [NotNull] + public ApplicationMenuGroupList Groups { get; } + /// /// Can be used to store a custom object related to this menu. /// @@ -47,6 +52,7 @@ public class ApplicationMenu : IHasMenuItems DisplayName = displayName ?? Name; Items = new ApplicationMenuItemList(); + Groups = new ApplicationMenuGroupList(); } /// @@ -60,6 +66,17 @@ public class ApplicationMenu : IHasMenuItems return this; } + /// + /// Adds a to . + /// + /// to be added + /// This object + public ApplicationMenu AddGroup([NotNull] ApplicationMenuGroup group) + { + Groups.Add(group); + return this; + } + /// /// Adds a custom data item to with given key & value. /// diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs index eec549cf9c..624abde425 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs @@ -64,4 +64,54 @@ public static class ApplicationMenuExtensions return menuWithItems; } + + [NotNull] + public static ApplicationMenuGroup GetMenuGroup( + [NotNull] this IHasMenuGroups menuWithGroups, + string groupName) + { + var menuGroup = menuWithGroups.GetMenuGroupOrNull(groupName); + if (menuGroup == null) + { + throw new AbpException($"Could not find a group item with given name: {groupName}"); + } + + return menuGroup; + } + + [CanBeNull] + public static ApplicationMenuGroup GetMenuGroupOrNull( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + return menuWithGroups.Groups.FirstOrDefault(group => group.Name == menuGroupName); + } + + public static bool TryRemoveMenuGroup( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + return menuWithGroups.Groups.RemoveAll(group => group.Name == menuGroupName) > 0; + } + + [NotNull] + public static IHasMenuGroups SetMenuGroupOrder( + [NotNull] this IHasMenuGroups menuWithGroups, + string menuGroupName, + int order) + { + Check.NotNull(menuWithGroups, nameof(menuWithGroups)); + + var menuGroup = menuWithGroups.GetMenuGroupOrNull(menuGroupName); + if (menuGroup != null) + { + menuGroup.Order = order; + } + + return menuWithGroups; + } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs new file mode 100644 index 0000000000..469c0ad355 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroup.cs @@ -0,0 +1,67 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.UI.Navigation; + +public class ApplicationMenuGroup +{ + private string _displayName; + + /// + /// Default value of a group item. + /// + public const int DefaultOrder = 1000; + + /// + /// Unique name of the group in the application. + /// + [NotNull] + public string Name { get; } + + /// + /// Display name of the group. + /// + [NotNull] + public string DisplayName { + get { return _displayName; } + set { + Check.NotNullOrWhiteSpace(value, nameof(value)); + _displayName = value; + } + } + + /// + /// Can be used to render the element with a specific Id for DOM selections. + /// + public string ElementId { get; set; } + + /// + /// The Display order of the group. + /// Default value: 1000. + /// + public int Order { get; set; } + + public ApplicationMenuGroup( + [NotNull] string name, + [NotNull] string displayName, + string elementId = null, + int order = DefaultOrder) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + Check.NotNullOrWhiteSpace(displayName, nameof(displayName)); + + Name = name; + DisplayName = displayName; + ElementId = elementId; + Order = order; + } + + private string GetDefaultElementId() + { + return "MenuGroup_" + Name; + } + + public override string ToString() + { + return $"[ApplicationMenuGroup] Name = {Name}"; + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs new file mode 100644 index 0000000000..f280f5d4f9 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuGroupList.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.UI.Navigation; + +public class ApplicationMenuGroupList: List +{ + public ApplicationMenuGroupList() + { + + } + + public ApplicationMenuGroupList(int capacity) + : base(capacity) + { + + } + + public ApplicationMenuGroupList(IEnumerable collection) + : base(collection) + { + + } + + public void Normalize() + { + Order(); + } + + private void Order() + { + var orderedItems = this.OrderBy(item => item.Order).ToArray(); + Clear(); + AddRange(orderedItems); + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs index 32241ba7ac..dc1614e3e5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs @@ -92,6 +92,11 @@ public class ApplicationMenuItem : IHasMenuItems, IHasSimpleStateCheckers public string CssClass { get; set; } + /// + /// Can be used to group menu items. + /// + public string GroupName { get; set; } + public ApplicationMenuItem( [NotNull] string name, [NotNull] string displayName, @@ -101,6 +106,7 @@ public class ApplicationMenuItem : IHasMenuItems, IHasSimpleStateCheckers>(); Items = new ApplicationMenuItemList(); diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs new file mode 100644 index 0000000000..fbbe7a63a4 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IHasMenuGroups.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.UI.Navigation; + +public interface IHasMenuGroups +{ + /// + /// Menu groups. + /// + ApplicationMenuGroupList Groups { get; } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs index aa933c6e09..4f655509b9 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs @@ -96,6 +96,7 @@ public class MenuManager : IMenuManager, ITransientDependency } NormalizeMenu(menu); + NormalizeMenuGroup(menu); return menu; } @@ -159,4 +160,21 @@ public class MenuManager : IMenuManager, ITransientDependency menuWithItems.Items.Normalize(); } + + protected virtual void NormalizeMenuGroup(ApplicationMenu applicationMenu) + { + foreach (var menuGroup in applicationMenu.Items.Where(x => !x.GroupName.IsNullOrWhiteSpace()).GroupBy(x => x.GroupName)) + { + var group = applicationMenu.GetMenuGroupOrNull(menuGroup.First().GroupName); + if (group != null) + { + continue; + } + + foreach (var menuItem in menuGroup) + { + menuItem.GroupName = null; + } + } + } } diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs index 6088984cb9..c482963d75 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AbpUiNavigationTestModule.cs @@ -16,6 +16,7 @@ public class AbpUiNavigationTestModule : AbpModule options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor1()); options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor2()); options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor3()); + options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor4()); options.MainMenuNames.Add(MenuManager_Tests.TestMenuContributor3.MenuName); }); diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs index b27de60c10..421e072b0f 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/MenuManager_Tests.cs @@ -46,7 +46,7 @@ public class MenuManager_Tests : AbpIntegratedTest mainMenu.Name.ShouldBe(StandardMenus.Main); mainMenu.DisplayName.ShouldBe("Main Menu"); - mainMenu.Items.Count.ShouldBe(2); + mainMenu.Items.Count.ShouldBe(5); mainMenu.Items[0].Name.ShouldBe("Dashboard"); mainMenu.Items[1].Name.ShouldBe(DefaultMenuNames.Application.Main.Administration); mainMenu.Items[1].Items[0].Name.ShouldBe("Administration.UserManagement"); @@ -63,12 +63,29 @@ public class MenuManager_Tests : AbpIntegratedTest mainMenu.Name.ShouldBe(StandardMenus.Main); - mainMenu.Items.Count.ShouldBe(3); + mainMenu.Items.Count.ShouldBe(6); mainMenu.Items.ShouldContain(x => x.Name == "Products"); mainMenu.Items.ShouldContain(x => x.Name == "Dashboard"); } + [Fact] + public async Task GetMainMenuAsync_GroupMenuItems() + { + var mainMenu = await _menuManager.GetMainMenuAsync(); + + mainMenu.Name.ShouldBe(StandardMenus.Main); + mainMenu.Items.Count.ShouldBe(6); + + mainMenu.Items[2].GroupName.ShouldBe("Layouts"); + mainMenu.Items[3].GroupName.ShouldBe("Layouts"); + mainMenu.Items[4].GroupName.ShouldBe(null); // No group defined + + var layoutsGroup = mainMenu.GetMenuGroup("Layouts"); + layoutsGroup.Name.ShouldBe("Layouts"); + layoutsGroup.DisplayName.ShouldBe("Layouts"); + } + /* Adds menu items: * - Administration * - User Management @@ -149,4 +166,29 @@ public class MenuManager_Tests : AbpIntegratedTest return Task.CompletedTask; } } + + /* Adds group and menu items: + * - Layouts + * - Toolbars + * - Page Header + */ + public class TestMenuContributor4 : IMenuContributor + { + public Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name != StandardMenus.Main) + { + return Task.CompletedTask; + } + + context.Menu.AddGroup(new ApplicationMenuGroup("Layouts", "Layouts")); + + context.Menu.AddItem(new ApplicationMenuItem("Toolbars", "Toolbars", url: "/layouts/toolbars", groupName: "Layouts")); + context.Menu.AddItem(new ApplicationMenuItem("PageHeader", "Page Header", url: "/layouts/page-header", groupName: "Layouts")); + + context.Menu.AddItem(new ApplicationMenuItem("Branding", "Branding", url: "/layouts/branding", groupName: "NotDefinedGroup")); + + return Task.CompletedTask; + } + } } From c6a9bec5fe0ed3e58675c489dd0f35c23ba03348 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 8 Mar 2023 11:20:58 +0800 Subject: [PATCH 2/2] Update MenuManager.cs --- .../Volo/Abp/Ui/Navigation/MenuManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs index 4f655509b9..349aa8e5a5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuManager.cs @@ -176,5 +176,7 @@ public class MenuManager : IMenuManager, ITransientDependency menuItem.GroupName = null; } } + + applicationMenu.Groups.Normalize(); } }