mirror of https://github.com/Squidex/squidex.git
45 changed files with 448 additions and 205 deletions
@ -0,0 +1,53 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; |
|||
|
|||
import { DialogService } from './../services/dialog.service'; |
|||
|
|||
@Directive({ |
|||
selector: '[sqxConfirmClick]' |
|||
}) |
|||
export class ConfirmClickDirective { |
|||
@Input() |
|||
public confirmTitle: string; |
|||
|
|||
@Input() |
|||
public confirmText: string; |
|||
|
|||
@Output('sqxConfirmClick') |
|||
public click = new EventEmitter(); |
|||
|
|||
constructor( |
|||
private readonly dialogService: DialogService |
|||
) { |
|||
} |
|||
|
|||
@HostListener('click', ['$event']) |
|||
public onClick(event: Event) { |
|||
if (this.confirmTitle && |
|||
this.confirmTitle.length > 0 && |
|||
this.confirmText && |
|||
this.confirmText.length > 0) { |
|||
|
|||
let subscription = |
|||
this.dialogService.confirm(this.confirmTitle, this.confirmText) |
|||
.subscribe(result => { |
|||
if (result) { |
|||
this.click.emit(); |
|||
} |
|||
|
|||
subscription.unsubscribe(); |
|||
}); |
|||
} else { |
|||
this.click.emit(); |
|||
} |
|||
|
|||
event.stopPropagation(); |
|||
event.preventDefault(); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<div class="modal" *sqxModalView="dialogView;onRoot:true" [@fade]> |
|||
<div class="modal-backdrop"></div> |
|||
<div class="modal-dialog"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h4 class="modal-title">{{dialogRequest.title}}</h4> |
|||
</div> |
|||
|
|||
<div class="modal-body"> |
|||
{{dialogRequest.text}} |
|||
</div> |
|||
|
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-danger" (click)="confirm()">Yes</button> |
|||
<button type="button" class="btn btn-secondary" (click)="cancel()">No</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="notification-container notification-container-{{position}}"> |
|||
<div class="alert alert-dismissible alert-{{notification.messageType}}" *ngFor="let notification of notifications" (click)="close(notification)" [@fade]> |
|||
<button type="button" class="close" data-dismiss="alert" (close)="close(notification)">×</button> |
|||
|
|||
{{notification.message}} |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,28 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
.notification-container { |
|||
& { |
|||
margin: .625rem; |
|||
max-width: 20rem; |
|||
min-width: 20rem; |
|||
position: fixed; |
|||
z-index: 100000; |
|||
} |
|||
|
|||
&-righttop { |
|||
@include fixed(0, 0, auto, auto); |
|||
} |
|||
|
|||
&-rightbottom { |
|||
@include fixed(auto, 0, 0, auto); |
|||
} |
|||
|
|||
&-lefttop { |
|||
@include fixed(0, auto, auto, 0); |
|||
} |
|||
|
|||
&-leftbottom { |
|||
@include fixed(auto, auto, 0, 0); |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights r vbeserved |
|||
*/ |
|||
|
|||
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; |
|||
import { Subscription } from 'rxjs'; |
|||
|
|||
import { fadeAnimation } from './animations'; |
|||
|
|||
import { |
|||
DialogRequest, |
|||
DialogService, |
|||
Notification |
|||
} from './../services/dialog.service'; |
|||
|
|||
import { ModalView } from './../utils/modal-view'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-dialog-renderer', |
|||
styleUrls: ['./dialog-renderer.component.scss'], |
|||
templateUrl: './dialog-renderer.component.html', |
|||
animations: [ |
|||
fadeAnimation |
|||
] |
|||
}) |
|||
export class DialogRendererComponent implements OnDestroy, OnInit { |
|||
private dialogsSubscription: Subscription; |
|||
private notificationsSubscription: Subscription; |
|||
|
|||
public dialogView = new ModalView(false, true); |
|||
public dialogRequest: DialogRequest; |
|||
|
|||
public notifications: Notification[] = []; |
|||
|
|||
@Input() |
|||
public position = 'bottomright'; |
|||
|
|||
constructor( |
|||
private readonly dialogService: DialogService |
|||
) { |
|||
} |
|||
|
|||
public ngOnDestroy() { |
|||
this.notificationsSubscription.unsubscribe(); |
|||
this.dialogsSubscription.unsubscribe(); |
|||
} |
|||
|
|||
public ngOnInit() { |
|||
this.notificationsSubscription = |
|||
this.dialogService.notifications.subscribe(notification => { |
|||
this.notifications.push(notification); |
|||
|
|||
if (notification.displayTime > 0) { |
|||
setTimeout(() => { |
|||
this.close(notification); |
|||
}, notification.displayTime); |
|||
} |
|||
}); |
|||
|
|||
this.dialogsSubscription = |
|||
this.dialogService.dialogs |
|||
.subscribe(request => { |
|||
this.cancel(); |
|||
|
|||
this.dialogRequest = request; |
|||
this.dialogView.show(); |
|||
}); |
|||
} |
|||
|
|||
public cancel() { |
|||
if (this.dialogRequest) { |
|||
this.dialogRequest.complete(false); |
|||
this.dialogRequest = null; |
|||
this.dialogView.hide(); |
|||
} |
|||
} |
|||
|
|||
public confirm() { |
|||
if (this.dialogRequest) { |
|||
this.dialogRequest.complete(true); |
|||
this.dialogRequest = null; |
|||
this.dialogView.hide(); |
|||
} |
|||
} |
|||
|
|||
public close(notification: Notification) { |
|||
this.notifications.splice(this.notifications.indexOf(notification), 1); |
|||
} |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { |
|||
Notification, |
|||
DialogRequest, |
|||
DialogService, |
|||
DialogServiceFactory |
|||
} from './../'; |
|||
|
|||
describe('DialogService', () => { |
|||
it('should instantiate from factory', () => { |
|||
const dialogService = DialogServiceFactory(); |
|||
|
|||
expect(dialogService).toBeDefined(); |
|||
}); |
|||
|
|||
it('should instantiate', () => { |
|||
const dialogService = new DialogService(); |
|||
|
|||
expect(dialogService).toBeDefined(); |
|||
}); |
|||
|
|||
it('should create error notification', () => { |
|||
const notification = Notification.error('MyError'); |
|||
|
|||
expect(notification.displayTime).toBe(5000); |
|||
expect(notification.message).toBe('MyError'); |
|||
expect(notification.messageType).toBe('danger'); |
|||
}); |
|||
|
|||
it('should create info notification', () => { |
|||
const notification = Notification.info('MyInfo'); |
|||
|
|||
expect(notification.displayTime).toBe(5000); |
|||
expect(notification.message).toBe('MyInfo'); |
|||
expect(notification.messageType).toBe('info'); |
|||
}); |
|||
|
|||
it('should create dialog request', () => { |
|||
const dialog = new DialogRequest('MyTitle', 'MyText'); |
|||
|
|||
expect(dialog.title).toBe('MyTitle'); |
|||
expect(dialog.text).toBe('MyText'); |
|||
}); |
|||
|
|||
it('should confirm dialog', () => { |
|||
const dialog = new DialogRequest('MyTitle', 'MyText'); |
|||
|
|||
let isCompleted = false; |
|||
let isNext = false; |
|||
|
|||
dialog.closed.subscribe(result => { |
|||
isNext = result; |
|||
}, undefined, () => { |
|||
isCompleted = true; |
|||
}) |
|||
expect(isCompleted).toBeTruthy(); |
|||
expect(isNext).toBeTruthy(); |
|||
}); |
|||
|
|||
it('should publish notification', () => { |
|||
const dialogService = new DialogService(); |
|||
const notification = Notification.error('Message'); |
|||
|
|||
let publishedNotification: Notification | null = null; |
|||
|
|||
dialogService.notifications.subscribe(result => { |
|||
publishedNotification = result; |
|||
}); |
|||
|
|||
dialogService.notify(notification); |
|||
|
|||
expect(publishedNotification).toBe(notification); |
|||
}); |
|||
|
|||
it('should publish dialog request', () => { |
|||
const dialogService = new DialogService(); |
|||
|
|||
let pushedDialog: DialogRequest | null = null; |
|||
|
|||
dialogService.dialogs.subscribe(result => { |
|||
pushedDialog = result; |
|||
}); |
|||
|
|||
dialogService.confirm('MyTitle', 'MyText'); |
|||
|
|||
expect(pushedDialog).toEqual(new DialogRequest('MyTitle', 'MyText')); |
|||
}); |
|||
}); |
|||
@ -1,57 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { |
|||
Notification, |
|||
NotificationService, |
|||
NotificationServiceFactory |
|||
} from './../'; |
|||
|
|||
describe('NotificationService', () => { |
|||
it('should instantiate from factory', () => { |
|||
const notificationService = NotificationServiceFactory(); |
|||
|
|||
expect(notificationService).toBeDefined(); |
|||
}); |
|||
|
|||
it('should instantiate', () => { |
|||
const notificationService = new NotificationService(); |
|||
|
|||
expect(notificationService).toBeDefined(); |
|||
}); |
|||
|
|||
it('should create error', () => { |
|||
const notification = Notification.error('MyError'); |
|||
|
|||
expect(notification.displayTime).toBe(5000); |
|||
expect(notification.message).toBe('MyError'); |
|||
expect(notification.messageType).toBe('danger'); |
|||
}); |
|||
|
|||
it('should create info', () => { |
|||
const notification = Notification.info('MyInfo'); |
|||
|
|||
expect(notification.displayTime).toBe(5000); |
|||
expect(notification.message).toBe('MyInfo'); |
|||
expect(notification.messageType).toBe('info'); |
|||
}); |
|||
|
|||
it('should publish notification', () => { |
|||
const notificationService = new NotificationService(); |
|||
const notification = Notification.error('Message'); |
|||
|
|||
let publishedNotification: Notification | null = null; |
|||
|
|||
notificationService.notifications.subscribe(result => { |
|||
publishedNotification = result; |
|||
}); |
|||
|
|||
notificationService.notify(notification); |
|||
|
|||
expect(publishedNotification).toBe(notification); |
|||
}); |
|||
}); |
|||
Loading…
Reference in new issue