mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-9266] Make notification and user menu accessible with keyboard (#4394)
* [ACS-9266] Make notification and user menu accessible with keyboard * [ACS-9266] cr fixes * [ACS-9266] cr fix * [link-adf:dev-mmaliarchuk/ACS-9266-Notification-and-user-menu-are-not-accessible-with-keyboard][ci:force] * empty commit
This commit is contained in:
parent
a75ddc4cfb
commit
261cdaebed
@ -22,12 +22,12 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, ViewEncapsulation } from '@angular/core';
|
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
import { SetSelectedNodesAction } from '@alfresco/aca-shared/store';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||||
import { LogoutDirective } from '@alfresco/adf-core';
|
import { LogoutDirective } from '@alfresco/adf-core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -45,6 +45,9 @@ import { LogoutDirective } from '@alfresco/adf-core';
|
|||||||
export class LogoutComponent {
|
export class LogoutComponent {
|
||||||
constructor(private store: Store) {}
|
constructor(private store: Store) {}
|
||||||
|
|
||||||
|
@ViewChild(MatMenuItem)
|
||||||
|
menuItem: MatMenuItem;
|
||||||
|
|
||||||
onLogoutEvent() {
|
onLogoutEvent() {
|
||||||
this.store.dispatch(new SetSelectedNodesAction([]));
|
this.store.dispatch(new SetSelectedNodesAction([]));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div mat-menu-item class="aca-user-info" [routerLink]="['/profile']" title="{{ 'APP.TOOLTIPS.MY_PROFILE' | translate }}">
|
<button mat-menu-item class="aca-user-info" [routerLink]="['/profile']" title="{{ 'APP.TOOLTIPS.MY_PROFILE' | translate }}">
|
||||||
<ng-container *ngIf="user$ | async as user">
|
<ng-container *ngIf="user$ | async as user">
|
||||||
<div class="aca-user-info-content">
|
<div class="aca-user-info-content">
|
||||||
<button class="aca-user-info-button">
|
<button class="aca-user-info-button">
|
||||||
@ -10,4 +10,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</button>
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, ViewEncapsulation, inject } from '@angular/core';
|
import { Component, ViewEncapsulation, inject, ViewChild } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { UserProfileService } from '@alfresco/aca-shared';
|
import { UserProfileService } from '@alfresco/aca-shared';
|
||||||
|
|
||||||
@ -40,5 +40,8 @@ import { UserProfileService } from '@alfresco/aca-shared';
|
|||||||
export class UserInfoComponent {
|
export class UserInfoComponent {
|
||||||
private userProfileService = inject(UserProfileService);
|
private userProfileService = inject(UserProfileService);
|
||||||
|
|
||||||
|
@ViewChild(MatMenuItem)
|
||||||
|
menuItem: MatMenuItem;
|
||||||
|
|
||||||
user$ = this.userProfileService.userProfile$;
|
user$ = this.userProfileService.userProfile$;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { CoreTestingModule, UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
|
import { UserMenuComponent } from './user-menu.component';
|
||||||
|
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||||
|
|
||||||
|
describe('UserMenuComponent', () => {
|
||||||
|
let component: UserMenuComponent;
|
||||||
|
let fixture: ComponentFixture<UserMenuComponent>;
|
||||||
|
let testingUtils: UnitTestingUtils;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule, UserMenuComponent],
|
||||||
|
providers: [{ provide: AppExtensionService, useValue: { runActionById() {} } }]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UserMenuComponent);
|
||||||
|
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
|
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.data = { items: [{ id: 'id-1' }, { id: 'id-2' }] };
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should merge nested menu items to MatMenu in ngAfterViewInit', () => {
|
||||||
|
spyOn(component.menu, 'ngAfterContentInit').and.callThrough();
|
||||||
|
testingUtils.getByDirective(MatMenuTrigger).nativeElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.ngAfterViewInit();
|
||||||
|
|
||||||
|
expect(component.menu._allItems.length).toBe(2);
|
||||||
|
expect(component.menu.ngAfterContentInit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -22,24 +22,24 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
import { AfterViewInit, Component, inject, Input, OnInit, QueryList, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
|
||||||
import { ContentActionRef } from '@alfresco/adf-extensions';
|
import { ContentActionRef, DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenu, MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||||
import { ToolbarMenuItemComponent, UserProfileService } from '@alfresco/aca-shared';
|
import { ToolbarMenuItemComponent, UserProfileService } from '@alfresco/aca-shared';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, TranslateModule, MatButtonModule, MatMenuModule, ToolbarMenuItemComponent],
|
imports: [CommonModule, TranslateModule, MatButtonModule, MatMenuModule, ToolbarMenuItemComponent, DynamicExtensionComponent],
|
||||||
selector: 'aca-user-menu',
|
selector: 'aca-user-menu',
|
||||||
templateUrl: './user-menu.component.html',
|
templateUrl: './user-menu.component.html',
|
||||||
styleUrls: ['./user-menu.component.scss'],
|
styleUrls: ['./user-menu.component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'aca-user-menu' }
|
host: { class: 'aca-user-menu' }
|
||||||
})
|
})
|
||||||
export class UserMenuComponent implements OnInit {
|
export class UserMenuComponent implements OnInit, AfterViewInit {
|
||||||
private userProfileService = inject(UserProfileService);
|
private userProfileService = inject(UserProfileService);
|
||||||
|
|
||||||
user$ = this.userProfileService.userProfile$;
|
user$ = this.userProfileService.userProfile$;
|
||||||
@ -50,9 +50,24 @@ export class UserMenuComponent implements OnInit {
|
|||||||
@Input()
|
@Input()
|
||||||
data: { items: any[] };
|
data: { items: any[] };
|
||||||
|
|
||||||
|
@ViewChild(MatMenu)
|
||||||
|
menu: MatMenu;
|
||||||
|
|
||||||
|
@ViewChildren(ToolbarMenuItemComponent)
|
||||||
|
toolbarMenuItems: QueryList<ToolbarMenuItemComponent>;
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
if (this.data?.items) {
|
if (this.data?.items) {
|
||||||
this.data.items.sort((a, b) => a.order - b.order);
|
this.data.items.sort((a, b) => a.order - b.order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
const menuItems = this.toolbarMenuItems.map((toolbarMenuItem) => toolbarMenuItem.menuItem).filter((menuItem) => menuItem !== undefined);
|
||||||
|
|
||||||
|
const menuItemsQueryList = new QueryList<MatMenuItem>();
|
||||||
|
menuItemsQueryList.reset(menuItems);
|
||||||
|
this.menu._allItems = menuItemsQueryList;
|
||||||
|
this.menu.ngAfterContentInit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,14 @@ import { ToolbarMenuItemComponent } from './toolbar-menu-item.component';
|
|||||||
import { AppExtensionService } from '../../../services/app.extension.service';
|
import { AppExtensionService } from '../../../services/app.extension.service';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ContentActionRef, ContentActionType } from '@alfresco/adf-extensions';
|
import { ContentActionRef, ContentActionType, DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||||
import { LibTestingModule } from '@alfresco/aca-shared';
|
import { LibTestingModule } from '@alfresco/aca-shared';
|
||||||
|
import { MatMenuItem } from '@angular/material/menu';
|
||||||
|
import { UnitTestingUtils } from '@alfresco/adf-core';
|
||||||
|
|
||||||
describe('ToolbarMenuItemComponent', () => {
|
describe('ToolbarMenuItemComponent', () => {
|
||||||
let fixture: ComponentFixture<ToolbarMenuItemComponent>;
|
let fixture: ComponentFixture<ToolbarMenuItemComponent>;
|
||||||
|
let testingUtils: UnitTestingUtils;
|
||||||
let component: ToolbarMenuItemComponent;
|
let component: ToolbarMenuItemComponent;
|
||||||
let appExtensionService: AppExtensionService;
|
let appExtensionService: AppExtensionService;
|
||||||
|
|
||||||
@ -51,6 +54,7 @@ describe('ToolbarMenuItemComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ToolbarMenuItemComponent);
|
fixture = TestBed.createComponent(ToolbarMenuItemComponent);
|
||||||
|
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
appExtensionService = TestBed.inject(AppExtensionService);
|
appExtensionService = TestBed.inject(AppExtensionService);
|
||||||
});
|
});
|
||||||
@ -110,4 +114,17 @@ describe('ToolbarMenuItemComponent', () => {
|
|||||||
|
|
||||||
expect(component.trackByActionId(0, contentActionRef)).toBe('action1');
|
expect(component.trackByActionId(0, contentActionRef)).toBe('action1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should assign menuItem from dynamic component', () => {
|
||||||
|
component.actionRef = {
|
||||||
|
id: 'action1',
|
||||||
|
type: ContentActionType.custom
|
||||||
|
};
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const innerElement = testingUtils.getByDirective(DynamicExtensionComponent);
|
||||||
|
innerElement.componentInstance.menuItem = new MatMenuItem(null, null, null, null, null);
|
||||||
|
component.ngAfterViewInit();
|
||||||
|
expect(component.menuItem).toBeInstanceOf(MatMenuItem);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Input, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { AfterViewInit, Component, Input, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { ContentActionRef, DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
import { ContentActionRef, DynamicExtensionComponent } from '@alfresco/adf-extensions';
|
||||||
import { AppExtensionService } from '../../../services/app.extension.service';
|
import { AppExtensionService } from '../../../services/app.extension.service';
|
||||||
import { MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
import { MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||||
@ -40,7 +40,7 @@ import { IconComponent } from '@alfresco/adf-core';
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'app-toolbar-menu-item' }
|
host: { class: 'app-toolbar-menu-item' }
|
||||||
})
|
})
|
||||||
export class ToolbarMenuItemComponent {
|
export class ToolbarMenuItemComponent implements AfterViewInit {
|
||||||
@Input()
|
@Input()
|
||||||
actionRef: ContentActionRef;
|
actionRef: ContentActionRef;
|
||||||
@Input()
|
@Input()
|
||||||
@ -49,6 +49,9 @@ export class ToolbarMenuItemComponent {
|
|||||||
@ViewChild(MatMenuItem)
|
@ViewChild(MatMenuItem)
|
||||||
menuItem: MatMenuItem;
|
menuItem: MatMenuItem;
|
||||||
|
|
||||||
|
@ViewChild(DynamicExtensionComponent)
|
||||||
|
dynamicComponent: DynamicExtensionComponent;
|
||||||
|
|
||||||
constructor(private extensions: AppExtensionService) {}
|
constructor(private extensions: AppExtensionService) {}
|
||||||
|
|
||||||
runAction() {
|
runAction() {
|
||||||
@ -64,6 +67,12 @@ export class ToolbarMenuItemComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
if (this.dynamicComponent?.menuItem) {
|
||||||
|
this.menuItem = this.dynamicComponent.menuItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private hasClickAction(actionRef: ContentActionRef): boolean {
|
private hasClickAction(actionRef: ContentActionRef): boolean {
|
||||||
return !!actionRef?.actions?.click;
|
return !!actionRef?.actions?.click;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user