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