Skip to content

Commit

Permalink
refactor(NotificationsMenuComponent): Convert to standalone (#2022)
Browse files Browse the repository at this point in the history
  • Loading branch information
hirokiterashima authored Dec 17, 2024
1 parent 346b33a commit 133de22
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 98 deletions.
2 changes: 1 addition & 1 deletion src/app/teacher/classroom-monitor.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import { GradingNodeService } from '../../assets/wise5/services/gradingNodeServi
NodeProgressViewComponent,
NotebookGradingComponent,
NotebookWorkgroupGradingComponent,
NotificationsMenuComponent,
StepItemComponent,
StudentGradingComponent,
StudentGradingToolsComponent,
Expand All @@ -64,6 +63,7 @@ import { GradingNodeService } from '../../assets/wise5/services/gradingNodeServi
ManageStudentsModule,
MilestoneModule,
NavItemScoreComponent,
NotificationsMenuComponent,
PauseScreensMenuComponent,
PeerGroupGradingModule,
PreviewComponentComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,57 @@
 
<span i18n>Alerts</span>
</span>
<button
mat-icon-button
*ngIf="newNotifications.length > 0"
(click)="confirmDismissAllNotifications()"
[disabled]="newNotifications.length === 0"
matTooltip="Clear all notifications"
matTooltipPosition="left"
i18n-matTooltip
aria-label="Clear all notifications"
i18n-aria-label
>
<mat-icon>clear_all</mat-icon>
</button>
@if (newNotifications.length > 0) {
<button
mat-icon-button
(click)="confirmDismissAllNotifications()"
[disabled]="newNotifications.length === 0"
matTooltip="Clear all notifications"
matTooltipPosition="left"
i18n-matTooltip
>
<mat-icon>clear_all</mat-icon>
</button>
}
</mat-toolbar>
<div class="account-menu__actions notifications-content" fxFlex>
<p class="no-notifications" *ngIf="newNotifications.length === 0" i18n>You have no new alerts at this time.</p>
<mat-list *ngIf="newNotifications.length > 0">
<ng-container *ngFor="let notification of newNotifications; let last = last">
<mat-list-item>
<div fxLayout="row" fxLayoutAlign="start center">
<div class="notification-content">
<div>
<a
(click)="visitNode(notification)"
tabindex="0"
matTooltip="Go to {{ getNodePositionAndTitle(notification.nodeId) }}"
matTooltipPosition="left"
i18n-matTooltip
>{{ notification.message }}</a
>
@if (newNotifications.length === 0) {
<p class="no-notifications" i18n>You have no new alerts at this time.</p>
} @else {
<mat-list>
@for (notification of newNotifications; track notification.id) {
<mat-list-item>
<div fxLayout="row" fxLayoutAlign="start center">
<div class="notification-content">
<div>
<a
(click)="visitNode(notification)"
tabindex="0"
matTooltip="Go to {{ getNodePositionAndTitle(notification.nodeId) }}"
matTooltipPosition="left"
i18n-matTooltip
>{{ notification.message }}</a
>
</div>
<div class="secondary-text mat-body-2">
{{ getNodePositionAndTitle(notification.nodeId) }}
</div>
</div>
<div class="secondary-text mat-body-2">{{ getNodePositionAndTitle(notification.nodeId) }}</div>
<span fxFlex></span>
<button
mat-icon-button
(click)="dismissNotification(notification)"
fxLayoutAlign="center center"
aria-label="Clear"
i18n-aria-label
>
<mat-icon>clear</mat-icon>
</button>
</div>
<span fxFlex></span>
<button
mat-icon-button
(click)="dismissNotification(notification)"
fxLayoutAlign="center center"
aria-label="Clear"
i18n-aria-label
>
<mat-icon>clear</mat-icon>
</button>
</div>
</mat-list-item>
<mat-divider></mat-divider>
</ng-container>
</mat-list>
</mat-list-item>
<mat-divider />
}
</mat-list>
}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatDialog } from '@angular/material/dialog';
import { of } from 'rxjs';
import { Notification } from '../../../../../../app/domain/notification';
import { DialogWithConfirmComponent } from '../../../../directives/dialog-with-confirm/dialog-with-confirm.component';
import { NotificationService } from '../../../../services/notificationService';
import { ClassroomMonitorTestingModule } from '../../../classroom-monitor-testing.module';
import { NotificationsMenuComponent } from './notifications-menu.component';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideRouter } from '@angular/router';

let component: NotificationsMenuComponent;
let dismissNotificationSpy: jasmine.Spy;
let fixture: ComponentFixture<NotificationsMenuComponent>;
const NODE_ID_1: string = 'node1';
const NODE_ID_2: string = 'node1';
const NODE_ROUTE = 'root.cm.unit.node';
const notification1 = new Notification({ nodeId: NODE_ID_1 });
const notification2 = new Notification({ nodeId: NODE_ID_2 });
let stateGoSpy: jasmine.Spy;

describe('NotificationsMenuComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DialogWithConfirmComponent, NotificationsMenuComponent],
imports: [ClassroomMonitorTestingModule,
MatDialogModule,
MatIconModule,
MatToolbarModule],
providers: [{ provide: MatDialog, useValue: { open: () => { } } }, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
}).compileComponents();
declarations: [DialogWithConfirmComponent],
imports: [ClassroomMonitorTestingModule, NotificationsMenuComponent],
providers: [
{ provide: MatDialog, useValue: { open: () => {} } },
provideHttpClientTesting(),
provideRouter([])
]
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(NotificationsMenuComponent);
component = fixture.componentInstance;
component.state = { go: () => {} };
dismissNotificationSpy = spyOn(TestBed.inject(NotificationService), 'dismissNotification');
stateGoSpy = spyOn(component.state, 'go');
fixture.detectChanges();
});

Expand All @@ -57,7 +52,7 @@ function confirmDismissAllNotifications() {
}
} as any);
component.newNotifications = [notification1, notification2];
component.confirmDismissAllNotifications();
component['confirmDismissAllNotifications']();
expect(dialogOpenSpy).toHaveBeenCalled();
expect(dismissNotificationSpy).toHaveBeenCalledTimes(2);
});
Expand All @@ -67,7 +62,7 @@ function confirmDismissAllNotifications() {
function dismissNotification() {
describe('dismissNotification', () => {
it('should dismiss notification', () => {
component.dismissNotification(notification1);
component['dismissNotification'](notification1);
expect(dismissNotificationSpy).toHaveBeenCalledWith(notification1);
});
});
Expand All @@ -76,8 +71,9 @@ function dismissNotification() {
function visitNode() {
describe('visitNode', () => {
it('should visit node', () => {
component.visitNode(notification1);
expect(stateGoSpy).toHaveBeenCalledWith(NODE_ROUTE, { nodeId: NODE_ID_1 });
const routerSpy = spyOn(component['router'], 'navigate');
component['visitNode'](notification1);
expect(routerSpy).toHaveBeenCalled();
});
});
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { NotificationService } from '../../../../services/notificationService';
import { TeacherProjectService } from '../../../../services/teacherProjectService';
import { DialogWithConfirmComponent } from '../../../../../../assets/wise5/directives/dialog-with-confirm/dialog-with-confirm.component';
import { Notification } from '../../../../../../app/domain/notification';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';
import { MatDividerModule } from '@angular/material/divider';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
encapsulation: ViewEncapsulation.None,
imports: [
CommonModule,
FlexLayoutModule,
MatButtonModule,
MatDividerModule,
MatIconModule,
MatListModule,
MatToolbarModule,
MatTooltipModule
],
selector: 'notifications-menu',
templateUrl: './notifications-menu.component.html',
styleUrls: ['./notifications-menu.component.scss'],
encapsulation: ViewEncapsulation.None
standalone: true,
styleUrl: './notifications-menu.component.scss',
templateUrl: './notifications-menu.component.html'
})
export class NotificationsMenuComponent implements OnInit {
export class NotificationsMenuComponent {
@Input() newNotifications: Notification[] = [];
@Input() state: any;
@Input() withPause: boolean;

constructor(
private dialog: MatDialog,
private notificationService: NotificationService,
private projectService: TeacherProjectService
private projectService: TeacherProjectService,
private route: ActivatedRoute,
private router: Router
) {}

ngOnInit(): void {}

getNodePositionAndTitle(nodeId: string): string {
protected getNodePositionAndTitle(nodeId: string): string {
return this.projectService.getNodePositionAndTitle(nodeId);
}

confirmDismissAllNotifications(): void {
protected confirmDismissAllNotifications(): void {
this.dialog
.open(DialogWithConfirmComponent, {
data: {
Expand All @@ -50,11 +69,13 @@ export class NotificationsMenuComponent implements OnInit {
});
}

dismissNotification(notification: Notification): void {
protected dismissNotification(notification: Notification): void {
this.notificationService.dismissNotification(notification);
}

visitNode(notification: Notification): void {
this.state.go('root.cm.unit.node', { nodeId: notification.nodeId });
protected visitNode(notification: Notification): void {
this.router.navigate(['node', notification.nodeId], {
relativeTo: this.route
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,7 @@ <h3 fxLayout="row" fxLayoutAlign="start center" class="title-header">
<mat-icon mat-menu-origin>notifications</mat-icon>
</button>
<mat-menu #notificationsMenu class="account-menu account-menu--fixed-width">
<notifications-menu
(click)="$event.stopPropagation()"
[newNotifications]="newNotifications"
[withPause]="true"
>
</notifications-menu>
<notifications-menu (click)="$event.stopPropagation()" [newNotifications]="newNotifications" />
</mat-menu>
<button
mat-icon-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ import { ClassroomMonitorTestingModule } from '../../../classroom-monitor-testin
import { PauseScreensMenuComponent } from '../../pause-screens-menu/pause-screens-menu.component';
import { NotificationsMenuComponent } from '../notifications-menu/notifications-menu.component';
import { TopBarComponent } from './top-bar.component';
import { provideRouter } from '@angular/router';

describe('TopBarComponent', () => {
let component: TopBarComponent;
let fixture: ComponentFixture<TopBarComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [NotificationsMenuComponent, TopBarComponent],
declarations: [TopBarComponent],
imports: [
ClassroomMonitorTestingModule,
NotificationsMenuComponent,
PauseScreensMenuComponent,
MatIconModule,
MatListModule,
MatMenuModule,
MatToolbarModule,
MatTooltipModule
]
],
providers: [provideRouter([])]
}).compileComponents();
});

Expand Down
Loading

0 comments on commit 133de22

Please sign in to comment.