mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACS-9266][ACS-9234] Make notification and user menu accessible with keyboard (#10647)
* [ACS-9266][ACS-9234] Make notification and user menu accessible with keyboard * [ACS-9266] cr fix * [ACS-9266] cr fixes * [ACS-9266] fix eslint * [ACS-9266] fix circular dependency * [ACS-9266] cr fix
This commit is contained in:
parent
c8aa27a87b
commit
e4feb68381
@ -15,12 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Output, QueryList, ViewChildren } from '@angular/core';
|
||||
import { LanguageService } from './service/language.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LanguageItem } from '../common/services/language-item.interface';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-language-menu',
|
||||
@ -37,6 +37,9 @@ export class LanguageMenuComponent {
|
||||
@Output()
|
||||
changedLanguage: EventEmitter<LanguageItem> = new EventEmitter<LanguageItem>();
|
||||
|
||||
@ViewChildren(MatMenuItem)
|
||||
menuItems: QueryList<MatMenuItem>;
|
||||
|
||||
languages$: Observable<LanguageItem[]>;
|
||||
|
||||
constructor(private languageService: LanguageService) {
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { LanguagePickerComponent } from './language-picker.component';
|
||||
import { MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
import { LanguageMenuComponent } from './language-menu.component';
|
||||
import { QueryList } from '@angular/core';
|
||||
import { CoreTestingModule, UnitTestingUtils } from '@alfresco/adf-core';
|
||||
|
||||
describe('LanguagePickerComponent', () => {
|
||||
let component: LanguagePickerComponent;
|
||||
let fixture: ComponentFixture<LanguagePickerComponent>;
|
||||
let testingUtils: UnitTestingUtils;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CoreTestingModule, LanguagePickerComponent]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LanguagePickerComponent);
|
||||
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should assign menuItems to MatMenu in ngAfterViewInit', () => {
|
||||
testingUtils.getByDirective(MatMenuTrigger).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
const languageMenuComponent = testingUtils.getByDirective(LanguageMenuComponent).componentInstance;
|
||||
const menuItem1 = new MatMenuItem(null, null, null, null, null);
|
||||
const menuItem2 = new MatMenuItem(null, null, null, null, null);
|
||||
|
||||
languageMenuComponent.menuItems = new QueryList<MatMenuItem>();
|
||||
languageMenuComponent.menuItems.reset([menuItem1, menuItem2]);
|
||||
spyOn(component.menu, 'ngAfterContentInit').and.callThrough();
|
||||
|
||||
component.ngAfterViewInit();
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
expect(component.menu._allItems.length).toBe(2);
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
expect(component.menu._allItems.toArray()).toEqual([menuItem1, menuItem2]);
|
||||
expect(component.menu.ngAfterContentInit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -15,10 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { AfterViewInit, Component, EventEmitter, Output, QueryList, ViewChild } from '@angular/core';
|
||||
import { LanguageItem } from '../common/services/language-item.interface';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatMenu, MatMenuItem, MatMenuModule } from '@angular/material/menu';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { LanguageMenuComponent } from './language-menu.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
@ -37,7 +37,25 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
</mat-menu>
|
||||
`
|
||||
})
|
||||
export class LanguagePickerComponent {
|
||||
export class LanguagePickerComponent implements AfterViewInit {
|
||||
@Output()
|
||||
public changedLanguage = new EventEmitter<LanguageItem>();
|
||||
|
||||
@ViewChild('langMenu')
|
||||
menu: MatMenu;
|
||||
|
||||
@ViewChild(MatMenuItem)
|
||||
menuItem: MatMenuItem;
|
||||
|
||||
@ViewChild(LanguageMenuComponent)
|
||||
languageMenuComponent: LanguageMenuComponent;
|
||||
|
||||
ngAfterViewInit() {
|
||||
const menuItems = this.languageMenuComponent.menuItems.filter((menuItem) => menuItem !== undefined);
|
||||
const menuItemsQueryList = new QueryList<MatMenuItem>();
|
||||
menuItemsQueryList.reset(menuItems);
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
this.menu._allItems = menuItemsQueryList;
|
||||
this.menu.ngAfterContentInit();
|
||||
}
|
||||
}
|
||||
|
@ -1,79 +1,75 @@
|
||||
<div (keyup)="onKeyPress($event)" tabindex="-1" role="button" class="adf-notification-history-container">
|
||||
<button mat-button
|
||||
[matMenuTriggerFor]="menu"
|
||||
aria-hidden="false"
|
||||
[attr.aria-label]="'NOTIFICATIONS.OPEN_HISTORY' | translate"
|
||||
title="{{ 'NOTIFICATIONS.OPEN_HISTORY' | translate }}"
|
||||
class="adf-notification-history-menu_button"
|
||||
id="adf-notification-history-open-button"
|
||||
(menuOpened)="onMenuOpened()">
|
||||
<mat-icon matBadge="⁠"
|
||||
[matBadgeHidden]="!notifications.length"
|
||||
class="adf-notification-history-menu_button-icon"
|
||||
matBadgeColor="accent"
|
||||
matBadgeSize="small">notifications
|
||||
</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu"
|
||||
[xPosition]="menuPositionX"
|
||||
[yPosition]="menuPositionY"
|
||||
id="adf-notification-history-menu"
|
||||
class="adf-notification-history-menu adf-notification-history-menu-panel">
|
||||
<div class="adf-notification-history-list"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(keyup.enter)="$event.stopPropagation()"
|
||||
(click)="$event.stopPropagation()">
|
||||
<div mat-subheader role="menuitem">
|
||||
<span class="adf-notification-history-menu-title">{{ 'NOTIFICATIONS.TITLE' | translate }}</span>
|
||||
<button *ngIf="notifications.length"
|
||||
id="adf-notification-history-mark-as-read"
|
||||
class="adf-notification-history-mark-as-read"
|
||||
mat-icon-button
|
||||
title="{{ 'NOTIFICATIONS.MARK_AS_READ' | translate }}"
|
||||
(click)="markAsRead()">
|
||||
<mat-icon>done_all</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-button
|
||||
[matMenuTriggerFor]="menu"
|
||||
aria-hidden="false"
|
||||
[attr.aria-label]="'NOTIFICATIONS.OPEN_HISTORY' | translate"
|
||||
title="{{ 'NOTIFICATIONS.OPEN_HISTORY' | translate }}"
|
||||
class="adf-notification-history-menu_button"
|
||||
id="adf-notification-history-open-button"
|
||||
(menuOpened)="onMenuOpened()">
|
||||
<mat-icon matBadge="⁠"
|
||||
[matBadgeHidden]="!notifications.length"
|
||||
class="adf-notification-history-menu_button-icon"
|
||||
matBadgeColor="accent"
|
||||
matBadgeSize="small">notifications
|
||||
</mat-icon>
|
||||
</button>
|
||||
|
||||
<mat-divider />
|
||||
<mat-menu #menu="matMenu"
|
||||
[xPosition]="menuPositionX"
|
||||
[yPosition]="menuPositionY"
|
||||
id="adf-notification-history-menu"
|
||||
class="adf-notification-history-menu adf-notification-history-menu-panel">
|
||||
<div class="adf-notification-history-list-header">
|
||||
<span class="adf-notification-history-menu-title">{{ 'NOTIFICATIONS.TITLE' | translate }}</span>
|
||||
<button mat-menu-item
|
||||
*ngIf="notifications.length"
|
||||
id="adf-notification-history-mark-as-read"
|
||||
class="adf-notification-history-mark-as-read"
|
||||
title="{{ 'NOTIFICATIONS.MARK_AS_READ' | translate }}"
|
||||
(click)="markAsRead()">
|
||||
<mat-icon class="adf-notification-history-mark-as-read-icon">done_all</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<mat-list role="menuitem">
|
||||
<ng-container *ngIf="notifications.length; else empty_list_template">
|
||||
<mat-list-item *ngFor="let notification of paginatedNotifications"
|
||||
class="adf-notification-history-menu-item"
|
||||
(click)="onNotificationClick(notification)">
|
||||
<div *ngIf="notification.initiator; else no_avatar"
|
||||
matListItemAvatar
|
||||
[outerHTML]="notification.initiator | usernameInitials : 'adf-notification-initiator-pic'"></div>
|
||||
<ng-template #no_avatar>
|
||||
<mat-icon matListItemLine
|
||||
class="adf-notification-history-menu-initiator">{{notification.icon}}</mat-icon>
|
||||
</ng-template>
|
||||
<div class="adf-notification-history-menu-item-content">
|
||||
<p class="adf-notification-history-menu-text adf-notification-history-menu-message"
|
||||
*ngFor="let message of notification.messages"
|
||||
matListItemLine [title]="message">{{ message }}</p>
|
||||
<p class="adf-notification-history-menu-text adf-notification-history-menu-date"
|
||||
matListItemLine> {{notification.datetime | adfTimeAgo}} </p>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</ng-container>
|
||||
<ng-template #empty_list_template>
|
||||
<mat-list-item id="adf-notification-history-component-no-message"
|
||||
class="adf-notification-history-menu-no-message">
|
||||
<p class="adf-notification-history-menu-no-message-text" matListItemLine>{{ 'NOTIFICATIONS.NO_MESSAGE' | translate }}</p>
|
||||
</mat-list-item>
|
||||
</ng-template>
|
||||
</mat-list>
|
||||
<mat-divider/>
|
||||
|
||||
<mat-divider />
|
||||
<div class="adf-notification-history-item-list">
|
||||
<ng-container *ngIf="notifications.length; else empty_list_template">
|
||||
<button mat-menu-item
|
||||
*ngFor="let notification of paginatedNotifications"
|
||||
(click)="onNotificationClick(notification, $event)"
|
||||
class="adf-notification-history-menu-item">
|
||||
<div class="adf-notification-history-menu-item-content">
|
||||
<div *ngIf="notification.initiator; else no_avatar"
|
||||
[outerHTML]="notification.initiator | usernameInitials : 'adf-notification-initiator-pic'"></div>
|
||||
<ng-template #no_avatar>
|
||||
<mat-icon class="adf-notification-history-menu-initiator">
|
||||
{{ notification.icon }}
|
||||
</mat-icon>
|
||||
</ng-template>
|
||||
<div class="adf-notification-history-menu-item-content-message">
|
||||
<p class="adf-notification-history-menu-text adf-notification-history-menu-message"
|
||||
*ngFor="let message of notification.messages"
|
||||
[title]="message">{{ message }}</p>
|
||||
<p class="adf-notification-history-menu-text adf-notification-history-menu-date"
|
||||
> {{ notification.datetime | adfTimeAgo }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-template #empty_list_template>
|
||||
<p mat-menu-item id="adf-notification-history-component-no-message"
|
||||
class="adf-notification-history-menu-no-message-text">
|
||||
{{ 'NOTIFICATIONS.NO_MESSAGE' | translate }}
|
||||
</p>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="adf-notification-history-load-more" role="menuitem" *ngIf="hasMoreNotifications()">
|
||||
<button mat-button (click)="loadMore()">
|
||||
{{ 'NOTIFICATIONS.LOAD_MORE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<mat-divider/>
|
||||
|
||||
<div class="adf-notification-history-load-more" *ngIf="hasMoreNotifications()">
|
||||
<button mat-menu-item (click)="loadMore($event)">
|
||||
{{ 'NOTIFICATIONS.LOAD_MORE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
@ -3,37 +3,6 @@
|
||||
$notification-item-height: 72px;
|
||||
|
||||
.adf {
|
||||
&-notification-history-container {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
&-notification-history-list {
|
||||
.adf-notification-history-menu-item-content-wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
.mdc-list-item__secondary-text::before {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-notification-history-menu-item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
padding: 0 0 0 16px;
|
||||
|
||||
p {
|
||||
line-height: 16px;
|
||||
margin-bottom: 0;
|
||||
color: var(--theme-sidenav-user-menu-color);
|
||||
}
|
||||
}
|
||||
|
||||
&-notification-history-menu-title {
|
||||
font-size: 14px;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
@ -44,6 +13,7 @@ $notification-item-height: 72px;
|
||||
padding: 0;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
margin-top: 1px;
|
||||
|
||||
.adf-notification-history-menu_button-icon {
|
||||
margin-right: 0;
|
||||
@ -54,18 +24,67 @@ $notification-item-height: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
&-notification-history-list #{$mat-subheader} {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-notification-history-menu:has(.adf-notification-history-list) {
|
||||
.adf-notification-history-menu-item {
|
||||
cursor: pointer;
|
||||
height: $notification-item-height;
|
||||
&-notification-history-menu {
|
||||
.adf-notification-history-list-header {
|
||||
padding: 10.5px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
|
||||
.adf-notification-history-mark-as-read {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
width: auto;
|
||||
margin: 4px 0;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: none;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-title {
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.adf-notification-history-item-list {
|
||||
padding-top: 8px;
|
||||
|
||||
.adf-notification-history-menu-item {
|
||||
cursor: pointer;
|
||||
height: $notification-item-height;
|
||||
align-items: center;
|
||||
display: block;
|
||||
padding: 0 14px;
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
padding: 0 0 0 16px;
|
||||
|
||||
p {
|
||||
line-height: normal;
|
||||
margin: 0;
|
||||
color: var(--theme-sidenav-user-menu-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-item:focus {
|
||||
@ -77,20 +96,29 @@ $notification-item-height: 72px;
|
||||
background-color: var(--adf-theme-background-hover-color);
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-message:is(p),
|
||||
.adf-notification-history-menu-no-message:is(p) {
|
||||
.adf-notification-history-menu-message:is(p) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-no-message-text {
|
||||
font-size: 16px;
|
||||
.adf-notification-history-item-list .adf-notification-history-menu-no-message-text {
|
||||
color: var(--theme-sidenav-user-menu-color);
|
||||
margin-bottom: 0;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
opacity: inherit;
|
||||
border: none;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-date.adf-notification-history-menu-text:is(p) {
|
||||
font-size: var(--theme-caption-font-size);
|
||||
text-indent: 3px;
|
||||
}
|
||||
|
||||
.adf-notification-history-menu-initiator {
|
||||
@ -119,13 +147,11 @@ $notification-item-height: 72px;
|
||||
padding: 10px;
|
||||
|
||||
button {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
&-notification-history-mark-as-read {
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
#{$mat-menu-panel}.adf-notification-history-menu.adf-notification-history-menu-panel {
|
||||
@ -134,18 +160,5 @@ $notification-item-height: 72px;
|
||||
|
||||
#{$mat-menu-content} {
|
||||
padding: 0;
|
||||
|
||||
#{$mat-list} {
|
||||
padding: 8px 0 0;
|
||||
|
||||
#{$mat-list-item-unscoped-content} {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#{$mat-list-item-content} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ describe('Notification History Component', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(overlayContainerElement.querySelector('#adf-notification-history-component-no-message')).toBeNull();
|
||||
expect(overlayContainerElement.querySelector('.adf-notification-history-list').innerHTML).toContain('Example Message');
|
||||
expect(overlayContainerElement.querySelector('.adf-notification-history-item-list').innerHTML).toContain('Example Message');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -15,17 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
DestroyRef,
|
||||
inject,
|
||||
Input,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { AfterViewInit, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { NotificationService } from '../services/notification.service';
|
||||
import { NOTIFICATION_TYPE, NotificationModel } from '../models/notification.model';
|
||||
import { MatMenuModule, MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
@ -121,22 +111,11 @@ export class NotificationHistoryComponent implements OnInit, AfterViewInit {
|
||||
this.createPagination();
|
||||
}
|
||||
|
||||
onKeyPress(event: KeyboardEvent) {
|
||||
this.closeUserModal(event);
|
||||
}
|
||||
|
||||
private closeUserModal($event: KeyboardEvent) {
|
||||
if ($event.keyCode === 27) {
|
||||
this.trigger.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
markAsRead() {
|
||||
this.notifications = [];
|
||||
this.paginatedNotifications = [];
|
||||
this.storageService.removeItem(NotificationHistoryComponent.NOTIFICATION_STORAGE);
|
||||
this.createPagination();
|
||||
this.trigger.closeMenu();
|
||||
}
|
||||
|
||||
createPagination() {
|
||||
@ -149,7 +128,8 @@ export class NotificationHistoryComponent implements OnInit, AfterViewInit {
|
||||
this.paginatedNotifications = this.notifications.slice(0, this.pagination.skipCount);
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
loadMore($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.pagination.skipCount = this.pagination.maxItems + this.pagination.skipCount;
|
||||
this.pagination.hasMoreItems = this.notifications.length > this.pagination.skipCount;
|
||||
this.paginatedNotifications = this.notifications.slice(0, this.pagination.skipCount);
|
||||
@ -159,7 +139,8 @@ export class NotificationHistoryComponent implements OnInit, AfterViewInit {
|
||||
return this.pagination?.hasMoreItems;
|
||||
}
|
||||
|
||||
onNotificationClick(notification: NotificationModel) {
|
||||
onNotificationClick(notification: NotificationModel, $event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
if (notification.clickCallBack) {
|
||||
notification.clickCallBack(notification.args);
|
||||
this.trigger.closeMenu();
|
||||
|
@ -87,7 +87,6 @@ $mat-calendar-table-header: '.mat-calendar-table-header';
|
||||
$mat-calendar-body-disabled: '.mat-calendar-body-disabled';
|
||||
$mat-toolbar: '.mat-toolbar';
|
||||
$mat-slide-toggle: '.mat-mdc-slide-toggle';
|
||||
$mat-list: '.mat-mdc-list';
|
||||
$mat-list-item-content: '.mdc-list-item__content';
|
||||
$mat-list-item-unscoped-content: '.mat-mdc-list-item-unscoped-content';
|
||||
$mat-text-field-no-label: '.mdc-text-field--no-label';
|
||||
|
@ -19,10 +19,11 @@
|
||||
|
||||
import { Component, Input, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DynamicExtensionComponent } from './dynamic.component';
|
||||
import { ComponentRegisterService } from '../../services/component-register.service';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { MatMenuItem } from '@angular/material/menu';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'test-component',
|
||||
@ -71,9 +72,10 @@ describe('DynamicExtensionComponent', () => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
const getInnerElement = () => fixture.debugElement.query(By.css('[data-automation-id="found-me"]'));
|
||||
|
||||
it('should load the TestComponent', () => {
|
||||
const innerElement = fixture.debugElement.query(By.css('[data-automation-id="found-me"]'));
|
||||
expect(innerElement).not.toBeNull();
|
||||
expect(getInnerElement()).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should pass through the data', () => {
|
||||
@ -90,6 +92,12 @@ describe('DynamicExtensionComponent', () => {
|
||||
const testComponent = fixture.debugElement.query(By.css('test-component')).componentInstance;
|
||||
expect(testComponent.data).toBe(data);
|
||||
});
|
||||
|
||||
it('should assign menuItem from dynamically generated component in ngAfterViewInit', () => {
|
||||
getInnerElement().componentInstance.menuItem = new MatMenuItem(null, null, null, null, null);
|
||||
component.ngAfterViewInit();
|
||||
expect(component.menuItem).toBeInstanceOf(MatMenuItem);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Angular life-cycle methods in sub-component', () => {
|
||||
|
@ -15,9 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, ComponentRef, ViewChild, ViewContainerRef, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, Input, ComponentRef, ViewChild, ViewContainerRef, OnDestroy, OnChanges, SimpleChanges, AfterViewInit } from '@angular/core';
|
||||
import { ExtensionService } from '../../services/extension.service';
|
||||
import { ExtensionComponent } from '../../services/component-register.service';
|
||||
import { MatMenuItem } from '@angular/material/menu';
|
||||
|
||||
// cSpell:words lifecycle
|
||||
@Component({
|
||||
@ -25,7 +26,7 @@ import { ExtensionComponent } from '../../services/component-register.service';
|
||||
standalone: true,
|
||||
template: `<div #content></div>`
|
||||
})
|
||||
export class DynamicExtensionComponent implements OnChanges, OnDestroy {
|
||||
export class DynamicExtensionComponent implements OnChanges, OnDestroy, AfterViewInit {
|
||||
@ViewChild('content', { read: ViewContainerRef, static: true })
|
||||
content: ViewContainerRef;
|
||||
|
||||
@ -35,6 +36,9 @@ export class DynamicExtensionComponent implements OnChanges, OnDestroy {
|
||||
/** Data for the dynamically-loaded component instance. */
|
||||
@Input() data: any;
|
||||
|
||||
/** Provides the menu item of dynamically-loaded component instance. */
|
||||
menuItem: MatMenuItem;
|
||||
|
||||
private componentRef: ComponentRef<ExtensionComponent>;
|
||||
private loaded: boolean = false;
|
||||
|
||||
@ -61,6 +65,10 @@ export class DynamicExtensionComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.menuItem = this.componentRef?.instance?.menuItem;
|
||||
}
|
||||
|
||||
private loadComponent() {
|
||||
const componentType = this.extensions.getComponentById<ExtensionComponent>(this.id);
|
||||
if (componentType) {
|
||||
|
@ -16,9 +16,11 @@
|
||||
*/
|
||||
|
||||
import { Injectable, Type } from '@angular/core';
|
||||
import { MatMenuItem } from '@angular/material/menu';
|
||||
|
||||
export interface ExtensionComponent {
|
||||
data: any;
|
||||
menuItem?: MatMenuItem;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
|
Loading…
x
Reference in New Issue
Block a user