diff --git a/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.html b/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.html index ba165602c..a9f88f5d0 100644 --- a/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.html +++ b/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.html @@ -2,9 +2,7 @@ @@ -21,7 +19,3 @@ - - - {{ 'APP.ACTIONS.SHARE' | translate }} - diff --git a/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.ts b/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.ts index efda59840..1ba33f0b3 100644 --- a/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.ts +++ b/projects/aca-content/src/lib/components/common/toggle-shared/toggle-shared.component.ts @@ -22,13 +22,13 @@ * from Hyland Software. If not, see . */ -import { Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { SelectionState } from '@alfresco/adf-extensions'; import { AppStore, getAppSelection, ShareNodeAction } from '@alfresco/aca-shared/store'; import { CommonModule } from '@angular/common'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; import { TranslatePipe } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; @@ -46,6 +46,9 @@ export class ToggleSharedComponent implements OnInit { iconButton?: string; }; + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + selection$: Observable; selectionState: SelectionState; selectionLabel = ''; diff --git a/projects/aca-content/src/lib/components/context-menu/context-menu.component.spec.ts b/projects/aca-content/src/lib/components/context-menu/context-menu.component.spec.ts index 67c891617..53fe858ff 100644 --- a/projects/aca-content/src/lib/components/context-menu/context-menu.component.spec.ts +++ b/projects/aca-content/src/lib/components/context-menu/context-menu.component.spec.ts @@ -26,18 +26,35 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppTestingModule } from '../../testing/app-testing.module'; import { ContextMenuComponent } from './context-menu.component'; import { ContextMenuOverlayRef } from './context-menu-overlay'; -import { ContentActionType } from '@alfresco/adf-extensions'; +import { ContentActionRef, ContentActionType, ExtensionService } from '@alfresco/adf-extensions'; import { of } from 'rxjs'; import { Store } from '@ngrx/store'; import { AppExtensionService } from '@alfresco/aca-shared'; +import { Component, ViewChild } from '@angular/core'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; +import { UnitTestingUtils } from '@alfresco/adf-core'; + +@Component({ + selector: 'aca-custom-menu-component', + standalone: true, + imports: [MatMenuModule], + // eslint-disable-next-line @alfresco/eslint-angular/no-angular-material-selectors + template: '' +}) +class TestCustomMenuComponent { + data: any; + @ViewChild(MatMenuItem) menuItem: MatMenuItem; +} describe('ContextMenuComponent', () => { let fixture: ComponentFixture; let component: ContextMenuComponent; let extensionsService: AppExtensionService; + let extensionService: ExtensionService; + let unitTestingUtils: UnitTestingUtils; - const contextItem = { + const contextItem: ContentActionRef = { type: ContentActionType.button, id: 'action-button', title: 'Test Button', @@ -48,7 +65,7 @@ describe('ContextMenuComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [AppTestingModule], + imports: [AppTestingModule, TestCustomMenuComponent], providers: [ { provide: ContextMenuOverlayRef, @@ -70,6 +87,8 @@ describe('ContextMenuComponent', () => { component = fixture.componentInstance; extensionsService = TestBed.inject(AppExtensionService); + extensionService = TestBed.inject(ExtensionService); + unitTestingUtils = new UnitTestingUtils(fixture.debugElement); }); it('should load context menu actions on init', () => { @@ -84,11 +103,13 @@ describe('ContextMenuComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const contextMenuElements = document.body.querySelector('.aca-context-menu')?.querySelectorAll('button'); - const actionButtonLabel: HTMLElement = contextMenuElements?.[0].querySelector(`[data-automation-id="${contextItem.id}-label"]`); + const contextMenuButtons = unitTestingUtils.getAllByCSS('.aca-context-menu button'); + const actionButtonLabel = unitTestingUtils.getInnerTextByCSS( + `.aca-context-menu button:first-child [data-automation-id="${contextItem.id}-label"]` + ); - expect(contextMenuElements?.length).toBe(1); - expect(actionButtonLabel.innerText).toBe(contextItem.title); + expect(contextMenuButtons?.length).toBe(1); + expect(actionButtonLabel).toBe(contextItem.title); }); it('should not render context menu if no actions items', async () => { @@ -96,8 +117,41 @@ describe('ContextMenuComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const contextMenuElements = document.body.querySelector('.aca-context-menu'); + const contextMenuElements = unitTestingUtils.getByCSS('.aca-context-menu'); expect(contextMenuElements).toBeNull(); }); + + it('should append menu items in the correct order according to actions array', async () => { + const customComponentAction: ContentActionRef = { + type: ContentActionType.custom, + component: 'test-custom-component', + id: 'custom-action', + data: { testProp: 'test-value' } + }; + + const buttonAction2: ContentActionRef = { + type: ContentActionType.button, + id: 'button-action-2', + title: 'Button 2', + actions: { + click: 'EVENT_2' + } + }; + + const orderedActions = [contextItem, customComponentAction, buttonAction2]; + + spyOn(extensionsService, 'getAllowedContextMenuActions').and.returnValue(of(orderedActions)); + spyOn(extensionService, 'getComponentById').and.returnValue(TestCustomMenuComponent); + + fixture.detectChanges(); + await fixture.whenStable(); + + const menuItems = component.menu._allItems.toArray(); + expect(menuItems.length).toBe(3); + + const menuItemsIds = menuItems.map((item) => item._getHostElement().getAttribute('id')); + const domIds: string[] = Array.from(unitTestingUtils.getAllByCSS('button')).map((button) => button.nativeElement.getAttribute('id')); + expect(domIds).toEqual(menuItemsIds); + }); }); diff --git a/projects/aca-content/src/lib/components/context-menu/context-menu.component.ts b/projects/aca-content/src/lib/components/context-menu/context-menu.component.ts index fe275c8d3..70c11b9b1 100644 --- a/projects/aca-content/src/lib/components/context-menu/context-menu.component.ts +++ b/projects/aca-content/src/lib/components/context-menu/context-menu.component.ts @@ -22,9 +22,9 @@ * from Hyland Software. If not, see . */ -import { AfterViewInit, Component, DestroyRef, inject, Inject, OnInit, ViewEncapsulation } from '@angular/core'; -import { MatMenuModule } from '@angular/material/menu'; -import { DynamicExtensionComponent } from '@alfresco/adf-extensions'; +import { AfterViewInit, Component, DestroyRef, inject, Inject, OnInit, QueryList, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { MatMenu, MatMenuItem, MatMenuModule } from '@angular/material/menu'; +import { ContentActionType, DynamicExtensionComponent } from '@alfresco/adf-extensions'; import { ContextMenuOverlayRef } from './context-menu-overlay'; import { CONTEXT_MENU_DIRECTION } from './direction.token'; import { Direction } from '@angular/cdk/bidi'; @@ -58,6 +58,15 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; encapsulation: ViewEncapsulation.None }) export class ContextMenuComponent extends BaseContextMenuDirective implements OnInit, AfterViewInit { + @ViewChildren(DynamicExtensionComponent) + dynamicExtensionComponents: QueryList; + + @ViewChild(MatMenu) + menu: MatMenu; + + @ViewChildren(MatMenuItem) + matMenuItems: QueryList; + private readonly destroyRef = inject(DestroyRef); constructor(contextMenuOverlayRef: ContextMenuOverlayRef, extensions: AppExtensionService, @Inject(CONTEXT_MENU_DIRECTION) direction: Direction) { @@ -77,5 +86,41 @@ export class ContextMenuComponent extends BaseContextMenuDirective implements On if (this.actions.length) { setTimeout(() => this.trigger.openMenu(), 0); } + + const itemsById = this.createMenuItemsLookup(); + const orderedItems = this.createOrderedItemsList(itemsById); + + const menuItemsQueryList = new QueryList(); + menuItemsQueryList.reset(orderedItems); + this.menu._allItems = menuItemsQueryList; + this.menu.ngAfterContentInit(); + } + + private createMenuItemsLookup(): Map { + const itemsById = new Map(); + this.matMenuItems.forEach((item) => { + itemsById.set(item._getHostElement()?.getAttribute('id'), item); + }); + + this.dynamicExtensionComponents.forEach((component) => { + if (component.menuItem && component.id) { + itemsById.set(component.id, component.menuItem); + } + }); + return itemsById; + } + + private createOrderedItemsList(itemsById: Map): MatMenuItem[] { + const orderedItems: MatMenuItem[] = []; + + this.actions.forEach((action) => { + const lookupId = action.type === ContentActionType.custom ? action.component : action.id; + const item = lookupId ? itemsById.get(lookupId) : undefined; + + if (item) { + orderedItems.push(item); + } + }); + return orderedItems; } } diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts index b99dae132..35ec6b1c9 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts @@ -24,14 +24,14 @@ import { AppStore, DownloadNodesAction, EditOfflineAction, SetSelectedNodesAction, getAppSelection } from '@alfresco/aca-shared/store'; import { NodeEntry, SharedLinkEntry, Node, NodesApi } from '@alfresco/js-api'; -import { Component, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppExtensionService, isLocked } from '@alfresco/aca-shared'; import { NotificationService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { CommonModule } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; @Component({ @@ -47,6 +47,9 @@ import { MatIconModule } from '@angular/material/icon'; host: { class: 'app-toggle-edit-offline' } }) export class ToggleEditOfflineComponent implements OnInit { + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + private notificationService = inject(NotificationService); private nodesApi: NodesApi; diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts index 30a54efe3..a10a3cb35 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-favorite-library/toggle-favorite-library.component.ts @@ -22,7 +22,7 @@ * from Hyland Software. If not, see . */ -import { Component, DestroyRef, inject, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, DestroyRef, inject, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppHookService } from '@alfresco/aca-shared'; import { AppStore, getAppSelection } from '@alfresco/aca-shared/store'; @@ -33,7 +33,7 @@ import { CommonModule } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; import { LibraryFavoriteDirective } from '@alfresco/adf-content-services'; import { MatIconModule } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ @@ -46,8 +46,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; [adf-favorite-library]="library" [attr.title]="library.isFavorite ? ('APP.ACTIONS.REMOVE_FAVORITE' | translate) : ('APP.ACTIONS.FAVORITE' | translate)" > - star - star_border + {{ library.isFavorite ? 'star' : 'star_border' }} {{ (library.isFavorite ? 'APP.ACTIONS.REMOVE_FAVORITE' : 'APP.ACTIONS.FAVORITE') | translate }} `, @@ -57,6 +56,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; export class ToggleFavoriteLibraryComponent implements OnInit { library; + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + private readonly destroyRef = inject(DestroyRef); constructor( diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-favorite/toggle-favorite.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-favorite/toggle-favorite.component.ts index 7b9543dc1..82e4e0ac1 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-favorite/toggle-favorite.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-favorite/toggle-favorite.component.ts @@ -22,7 +22,7 @@ * from Hyland Software. If not, see . */ -import { Component, inject, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, inject, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { SelectionState } from '@alfresco/adf-extensions'; @@ -32,15 +32,14 @@ import { CommonModule } from '@angular/common'; import { DocumentListService, NodeFavoriteDirective } from '@alfresco/adf-content-services'; import { MatIconModule } from '@angular/material/icon'; import { TranslatePipe } from '@ngx-translate/core'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; @Component({ imports: [CommonModule, TranslatePipe, MatIconModule, MatMenuModule, NodeFavoriteDirective], selector: 'app-toggle-favorite', template: ` `, @@ -54,6 +53,9 @@ export class ToggleFavoriteComponent implements OnInit { selection$: Observable; private reloadOnRoutes: string[] = []; + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + constructor( private store: Store, private router: Router diff --git a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts index a20264bd6..0ecfff65b 100644 --- a/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/toggle-join-library/toggle-join-library-button.component.ts @@ -25,7 +25,7 @@ import { AppStore, SetSelectedNodesAction, getAppSelection } from '@alfresco/aca-shared/store'; import { AppHookService, UserProfileService } from '@alfresco/aca-shared'; import { SelectionState } from '@alfresco/adf-extensions'; -import { Component, inject, ViewEncapsulation } from '@angular/core'; +import { Component, inject, ViewChild, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { LibraryMembershipDirective, LibraryMembershipErrorEvent, LibraryMembershipToggleEvent } from '@alfresco/adf-content-services'; @@ -34,6 +34,7 @@ import { MatButtonModule } from '@angular/material/button'; import { TranslatePipe } from '@ngx-translate/core'; import { MatIconModule } from '@angular/material/icon'; import { NotificationService } from '@alfresco/adf-core'; +import { MatMenuItem } from '@angular/material/menu'; @Component({ imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule, LibraryMembershipDirective], @@ -57,6 +58,9 @@ import { NotificationService } from '@alfresco/adf-core'; host: { class: 'app-toggle-join-library' } }) export class ToggleJoinLibraryButtonComponent { + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + private userProfileService = inject(UserProfileService); private notificationService = inject(NotificationService); private appHookService = inject(AppHookService); diff --git a/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.ts b/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.ts index 3eefd72cb..7681220f6 100644 --- a/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.ts +++ b/projects/aca-content/src/lib/components/toolbar/view-node/view-node.component.ts @@ -22,7 +22,7 @@ * from Hyland Software. If not, see . */ -import { Component, inject, Input, ViewEncapsulation } from '@angular/core'; +import { Component, inject, Input, ViewChild, ViewEncapsulation } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppStore, getAppSelection, ViewNodeAction } from '@alfresco/aca-shared/store'; import { ActivatedRoute, Router } from '@angular/router'; @@ -33,7 +33,7 @@ import { CommonModule } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; +import { MatMenuItem, MatMenuModule } from '@angular/material/menu'; import { MatDialogModule } from '@angular/material/dialog'; @Component({ @@ -63,6 +63,9 @@ export class ViewNodeComponent { @Input() data: { title?: string; menuButton?: boolean; iconButton?: boolean }; + @ViewChild(MatMenuItem) + menuItem: MatMenuItem; + constructor( private store: Store, private router: Router, diff --git a/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.spec.ts b/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.spec.ts index c585acc4d..2b888c2b1 100644 --- a/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.spec.ts +++ b/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.spec.ts @@ -24,9 +24,9 @@ import { ToolbarMenuComponent } from './toolbar-menu.component'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions'; -import { QueryList } from '@angular/core'; +import { ContentActionRef, ContentActionType, DynamicExtensionComponent } from '@alfresco/adf-extensions'; import { LibTestingModule } from '@alfresco/aca-shared'; +import { ToolbarMenuItemComponent } from '../toolbar-menu-item/toolbar-menu-item.component'; describe('ToolbarMenuComponent', () => { let fixture: ComponentFixture; @@ -53,16 +53,6 @@ describe('ToolbarMenuComponent', () => { expect(component.matTrigger.closeMenu).toHaveBeenCalled(); }); - it('should populate underlying menu with toolbar items', () => { - component.toolbarMenuItems = new QueryList(); - component.toolbarMenuItems.reset([{ menuItem: {} } as any]); - expect(component.toolbarMenuItems.length).toBe(1); - - expect(component.menu._allItems.length).toBe(0); - component.ngAfterViewInit(); - expect(component.menu._allItems.length).toBe(1); - }); - it('should track elements by content action id', () => { const contentActionRef: ContentActionRef = { id: 'action1', @@ -71,4 +61,70 @@ describe('ToolbarMenuComponent', () => { expect(component.trackByActionId(0, contentActionRef)).toBe('action1'); }); + + describe('ngAfterViewInit', () => { + it('should set mixed custom and standard menu items in right order', () => { + const customMenuItem = jasmine.createSpyObj('MatMenuItem', ['focus'], { id: 'customMenuItem' }); + const dynamicComponent = { id: 'comp1', menuItem: customMenuItem } as DynamicExtensionComponent; + + component.dynamicExtensionComponents.reset([dynamicComponent]); + + const standardMenuItem = jasmine.createSpyObj('MatMenuItem', ['focus'], { id: 'standardMenuItem' }); + const toolbarItem = { actionRef: { id: 'item1' }, menuItem: standardMenuItem } as ToolbarMenuItemComponent; + + component.toolbarMenuItems.reset([toolbarItem]); + + component.actionRef = { + id: 'parent', + children: [ + { id: 'custom1', type: ContentActionType.custom, component: 'comp1' }, + { id: 'item1', type: ContentActionType.button }, + { id: 'custom2', type: ContentActionType.custom, component: 'comp1' } + ] + } as ContentActionRef; + + component.ngAfterViewInit(); + + expect(component.menu._allItems.length).toBe(3); + expect(component.menu._allItems.toArray()).toEqual([customMenuItem, standardMenuItem, customMenuItem]); + }); + + it('should skip items without menuItem property', () => { + const customMenuItem = jasmine.createSpyObj('MatMenuItem', ['focus'], { id: 'menuItem1' }); + const toolbarItem1 = { actionRef: { id: 'item1' }, menuItem: customMenuItem } as ToolbarMenuItemComponent; + const toolbarItem2 = { actionRef: { id: 'item2' } } as ToolbarMenuItemComponent; + + component.toolbarMenuItems.reset([toolbarItem1, toolbarItem2]); + + component.actionRef = { + id: 'parent', + children: [ + { id: 'item1', type: ContentActionType.button }, + { id: 'item2', type: ContentActionType.button } + ] + } as ContentActionRef; + + component.ngAfterViewInit(); + + expect(component.menu._allItems.length).toBe(1); + expect(component.menu._allItems.toArray()).toEqual([customMenuItem]); + }); + + it('should use component ID when component property is not specified for custom item', () => { + const menuItem = jasmine.createSpyObj('MatMenuItem', ['focus'], { id: 'menuItem' }); + const dynamicComponent = { id: 'custom1', menuItem: menuItem } as DynamicExtensionComponent; + + component.dynamicExtensionComponents.reset([dynamicComponent]); + + component.actionRef = { + id: 'parent', + children: [{ id: 'custom1', type: ContentActionType.custom }] + } as ContentActionRef; + + component.ngAfterViewInit(); + + expect(component.menu._allItems.length).toBe(1); + expect(component.menu._allItems.first).toBe(menuItem); + }); + }); }); diff --git a/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.ts b/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.ts index 8569f2310..bb8414541 100644 --- a/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.ts +++ b/projects/aca-shared/src/lib/components/toolbar/toolbar-menu/toolbar-menu.component.ts @@ -23,7 +23,7 @@ */ import { Component, Input, ViewEncapsulation, HostListener, ViewChild, ViewChildren, QueryList, AfterViewInit, OnInit } from '@angular/core'; -import { ContentActionRef, DynamicExtensionComponent } from '@alfresco/adf-extensions'; +import { ContentActionRef, ContentActionType, DynamicExtensionComponent } from '@alfresco/adf-extensions'; import { MatMenu, MatMenuItem, MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { ThemePalette } from '@angular/material/core'; import { ToolbarMenuItemComponent } from '../toolbar-menu-item/toolbar-menu-item.component'; @@ -55,6 +55,9 @@ export class ToolbarMenuComponent implements OnInit, AfterViewInit { @ViewChildren(ToolbarMenuItemComponent) toolbarMenuItems: QueryList; + @ViewChildren(DynamicExtensionComponent) + dynamicExtensionComponents: QueryList; + @Input() data: { menuType?: string; @@ -73,13 +76,34 @@ export class ToolbarMenuComponent implements OnInit, AfterViewInit { } ngAfterViewInit(): void { - const menuItems: MatMenuItem[] = []; - this.toolbarMenuItems.forEach((toolbarMenuItem: ToolbarMenuItemComponent) => { - if (toolbarMenuItem.menuItem !== undefined) { - menuItems.push(toolbarMenuItem.menuItem); + const dynamicComponentMap = new Map( + this.dynamicExtensionComponents.map((component) => [component.id, component]) + ); + + const toolbarItemMap = new Map(); + this.toolbarMenuItems.forEach((item) => { + if (item.actionRef?.id) { + toolbarItemMap.set(item.actionRef.id, item); } }); - const menuItemsQueryList: QueryList = new QueryList(); + + const menuItems: MatMenuItem[] = []; + this.actionRef.children?.forEach((child) => { + if (child.type === ContentActionType.custom) { + const componentId = child.component || child.id; + const component = dynamicComponentMap.get(componentId); + if (component?.menuItem) { + menuItems.push(component.menuItem); + } + } else { + const toolbarItem = toolbarItemMap.get(child.id); + if (toolbarItem?.menuItem) { + menuItems.push(toolbarItem.menuItem); + } + } + }); + + const menuItemsQueryList = new QueryList(); menuItemsQueryList.reset(menuItems); this.menu._allItems = menuItemsQueryList; this.menu.ngAfterContentInit();