mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1344 from ybnd/Pause-notification-countdown-on-hover
Pause notification countdown on hover
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
||||||
import { BrowserModule, By } from '@angular/platform-browser';
|
import { BrowserModule, By } from '@angular/platform-browser';
|
||||||
import { ChangeDetectorRef, DebugElement } from '@angular/core';
|
import { ChangeDetectorRef, DebugElement } from '@angular/core';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
@@ -16,6 +16,7 @@ import { Notification } from '../models/notification.model';
|
|||||||
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
|
import { TranslateLoaderMock } from '../../mocks/translate-loader.mock';
|
||||||
import { storeModuleConfig } from '../../../app.reducer';
|
import { storeModuleConfig } from '../../../app.reducer';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
describe('NotificationComponent', () => {
|
describe('NotificationComponent', () => {
|
||||||
|
|
||||||
@@ -83,6 +84,8 @@ describe('NotificationComponent', () => {
|
|||||||
deContent = fixture.debugElement.query(By.css('.notification-content'));
|
deContent = fixture.debugElement.query(By.css('.notification-content'));
|
||||||
elContent = deContent.nativeElement;
|
elContent = deContent.nativeElement;
|
||||||
elType = fixture.debugElement.query(By.css('.notification-icon')).nativeElement;
|
elType = fixture.debugElement.query(By.css('.notification-icon')).nativeElement;
|
||||||
|
|
||||||
|
spyOn(comp, 'remove');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create component', () => {
|
it('should create component', () => {
|
||||||
@@ -124,4 +127,51 @@ describe('NotificationComponent', () => {
|
|||||||
expect(elContent.innerHTML).toEqual(htmlContent);
|
expect(elContent.innerHTML).toEqual(htmlContent);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('dismiss countdown', () => {
|
||||||
|
const TIMEOUT = 5000;
|
||||||
|
let isPaused$: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
isPaused$ = new BehaviorSubject<boolean>(false);
|
||||||
|
comp.isPaused$ = isPaused$;
|
||||||
|
comp.notification = {
|
||||||
|
id: '1',
|
||||||
|
type: NotificationType.Info,
|
||||||
|
title: 'Notif. title',
|
||||||
|
content: 'test',
|
||||||
|
options: Object.assign(
|
||||||
|
new NotificationOptions(),
|
||||||
|
{ timeout: TIMEOUT }
|
||||||
|
),
|
||||||
|
html: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove notification after timeout', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(TIMEOUT);
|
||||||
|
expect(comp.remove).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('isPaused$', () => {
|
||||||
|
it('should pause countdown on true', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(TIMEOUT / 2);
|
||||||
|
isPaused$.next(true);
|
||||||
|
tick(TIMEOUT);
|
||||||
|
expect(comp.remove).not.toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should resume paused countdown on false', fakeAsync(() => {
|
||||||
|
comp.ngOnInit();
|
||||||
|
tick(TIMEOUT / 4);
|
||||||
|
isPaused$.next(true);
|
||||||
|
tick(TIMEOUT / 4);
|
||||||
|
isPaused$.next(false);
|
||||||
|
tick(TIMEOUT);
|
||||||
|
expect(comp.remove).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import {of as observableOf, Observable } from 'rxjs';
|
import { Observable, of as observableOf } from 'rxjs';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
@@ -23,6 +23,7 @@ import { fadeInEnter, fadeInState, fadeOutLeave, fadeOutState } from '../../anim
|
|||||||
import { NotificationAnimationsStatus } from '../models/notification-animations-type';
|
import { NotificationAnimationsStatus } from '../models/notification-animations-type';
|
||||||
import { isNotEmpty } from '../../empty.util';
|
import { isNotEmpty } from '../../empty.util';
|
||||||
import { INotification } from '../models/notification.model';
|
import { INotification } from '../models/notification.model';
|
||||||
|
import { filter, first } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-notification',
|
selector: 'ds-notification',
|
||||||
@@ -47,6 +48,11 @@ export class NotificationComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
@Input() public notification = null as INotification;
|
@Input() public notification = null as INotification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this notification's countdown should be paused
|
||||||
|
*/
|
||||||
|
@Input() public isPaused$: Observable<boolean> = observableOf(false);
|
||||||
|
|
||||||
// Progress bar variables
|
// Progress bar variables
|
||||||
public title: Observable<string>;
|
public title: Observable<string>;
|
||||||
public content: Observable<string>;
|
public content: Observable<string>;
|
||||||
@@ -99,17 +105,21 @@ export class NotificationComponent implements OnInit, OnDestroy {
|
|||||||
private instance = () => {
|
private instance = () => {
|
||||||
this.diff = (new Date().getTime() - this.start) - (this.count * this.speed);
|
this.diff = (new Date().getTime() - this.start) - (this.count * this.speed);
|
||||||
|
|
||||||
if (this.count++ === this.steps) {
|
this.isPaused$.pipe(
|
||||||
this.remove();
|
filter(paused => !paused),
|
||||||
// this.item.timeoutEnd!.emit();
|
first(),
|
||||||
} else if (!this.stopTime) {
|
).subscribe(() => {
|
||||||
if (this.showProgressBar) {
|
if (this.count++ === this.steps) {
|
||||||
this.progressWidth += 100 / this.steps;
|
this.remove();
|
||||||
}
|
} else if (!this.stopTime) {
|
||||||
|
if (this.showProgressBar) {
|
||||||
|
this.progressWidth += 100 / this.steps;
|
||||||
|
}
|
||||||
|
|
||||||
this.timer = setTimeout(this.instance, (this.speed - this.diff));
|
this.timer = setTimeout(this.instance, (this.speed - this.diff));
|
||||||
}
|
}
|
||||||
this.zone.run(() => this.cdr.detectChanges());
|
this.zone.run(() => this.cdr.detectChanges());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove() {
|
public remove() {
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
<div class="notifications-wrapper position-fixed" [ngClass]="position">
|
<div class="notifications-wrapper position-fixed"
|
||||||
|
[ngClass]="position"
|
||||||
|
(mouseenter)="this.isPaused$.next(true);"
|
||||||
|
(mouseleave)="this.isPaused$.next(false);">
|
||||||
<ds-notification
|
<ds-notification
|
||||||
class="notification"
|
class="notification"
|
||||||
*ngFor="let a of notifications; let i = index"
|
*ngFor="let a of notifications; let i = index"
|
||||||
[notification]="a">
|
[notification]="a" [isPaused$]="isPaused$">
|
||||||
</ds-notification>
|
</ds-notification>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule, By } from '@angular/platform-browser';
|
||||||
import { ChangeDetectorRef } from '@angular/core';
|
import { ChangeDetectorRef } from '@angular/core';
|
||||||
|
|
||||||
import { NotificationsService } from '../notifications.service';
|
import { NotificationsService } from '../notifications.service';
|
||||||
@@ -14,6 +14,9 @@ import { NotificationType } from '../models/notification-type';
|
|||||||
import { uniqueId } from 'lodash';
|
import { uniqueId } from 'lodash';
|
||||||
import { INotificationBoardOptions } from '../../../../config/notifications-config.interfaces';
|
import { INotificationBoardOptions } from '../../../../config/notifications-config.interfaces';
|
||||||
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
import { NotificationsServiceStub } from '../../testing/notifications-service.stub';
|
||||||
|
import { cold } from 'jasmine-marbles';
|
||||||
|
|
||||||
|
export const bools = { f: false, t: true };
|
||||||
|
|
||||||
describe('NotificationsBoardComponent', () => {
|
describe('NotificationsBoardComponent', () => {
|
||||||
let comp: NotificationsBoardComponent;
|
let comp: NotificationsBoardComponent;
|
||||||
@@ -67,6 +70,40 @@ describe('NotificationsBoardComponent', () => {
|
|||||||
|
|
||||||
it('should have two notifications', () => {
|
it('should have two notifications', () => {
|
||||||
expect(comp.notifications.length).toBe(2);
|
expect(comp.notifications.length).toBe(2);
|
||||||
|
expect(fixture.debugElement.queryAll(By.css('ds-notification')).length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('notification countdown', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = fixture.debugElement.query(By.css('div.notifications-wrapper'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be paused by default', () => {
|
||||||
|
expect(comp.isPaused$).toBeObservable(cold('f', bools));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pause on mouseenter', () => {
|
||||||
|
wrapper.triggerEventHandler('mouseenter');
|
||||||
|
|
||||||
|
expect(comp.isPaused$).toBeObservable(cold('t', bools));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resume on mouseleave', () => {
|
||||||
|
wrapper.triggerEventHandler('mouseenter');
|
||||||
|
wrapper.triggerEventHandler('mouseleave');
|
||||||
|
|
||||||
|
expect(comp.isPaused$).toBeObservable(cold('f', bools));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be passed to all notifications', () => {
|
||||||
|
fixture.debugElement.queryAll(By.css('ds-notification'))
|
||||||
|
.map(node => node.componentInstance)
|
||||||
|
.forEach(notification => {
|
||||||
|
expect(notification.isPaused$).toEqual(comp.isPaused$);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@@ -9,7 +9,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
import { Subscription } from 'rxjs';
|
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { difference } from 'lodash';
|
import { difference } from 'lodash';
|
||||||
|
|
||||||
import { NotificationsService } from '../notifications.service';
|
import { NotificationsService } from '../notifications.service';
|
||||||
@@ -44,6 +44,11 @@ export class NotificationsBoardComponent implements OnInit, OnDestroy {
|
|||||||
public rtl = false;
|
public rtl = false;
|
||||||
public animate: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
|
public animate: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to pause the dismiss countdown of all notifications on the board
|
||||||
|
*/
|
||||||
|
public isPaused$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
constructor(private service: NotificationsService,
|
constructor(private service: NotificationsService,
|
||||||
private store: Store<AppState>,
|
private store: Store<AppState>,
|
||||||
private cdr: ChangeDetectorRef) {
|
private cdr: ChangeDetectorRef) {
|
||||||
@@ -129,7 +134,6 @@ export class NotificationsBoardComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.sub) {
|
if (this.sub) {
|
||||||
this.sub.unsubscribe();
|
this.sub.unsubscribe();
|
||||||
|
Reference in New Issue
Block a user