ACS-3640 a 11 y aca 881740 snackbar messages disappear without option to adjust timing (#7916)

* ACS-3640 Increasing time of autoclosing snackbar and adding possibility to add icon to snackbar content

* ACS-3640 Fix lint issues

* ACS-3640 Addressing PR comments and adding possibility to display icon together with label

* ACS-3640 Fix lint issues

* ACS-3640 Added possibility to change showAction for different types of notifications

* ACS-3640 Addressed PR comments

* ACS-3640 Corrected title for snackbar content md file and remove redundant test for snackbar content

* ACS-3640 Fixed lint issues

* ACS-3640 Fixed unit tests

* ACS-3640 Fixed e2e tests

* ACS-3640 Correction after rebasing
This commit is contained in:
AleksanderSklorz 2022-11-12 20:49:03 +01:00 committed by GitHub
parent ba05d3a1df
commit 4d76ebe1ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 443 additions and 51 deletions

View File

@ -49,8 +49,7 @@ export class CommunityStartProcessCloudDemoComponent implements OnInit {
openSnackMessage(event: any) { openSnackMessage(event: any) {
this.notificationService.openSnackMessage( this.notificationService.openSnackMessage(
event.response.body.message, event.response.body.message
4000
); );
} }
} }

View File

@ -42,8 +42,7 @@ export class CommunityStartTaskCloudDemoComponent {
openSnackMessage(event: any) { openSnackMessage(event: any) {
this.notificationService.openSnackMessage( this.notificationService.openSnackMessage(
event.response.body.message, event.response.body.message
4000
); );
} }
} }

View File

@ -17,8 +17,14 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router, PRIMARY_OUTLET } from '@angular/router'; import { ActivatedRoute, Router, PRIMARY_OUTLET } from '@angular/router';
import { ContentService, AllowableOperationsEnum, PermissionsEnum, NodesApiService, FileUploadErrorEvent } from '@alfresco/adf-core'; import {
import { MatSnackBar } from '@angular/material/snack-bar'; ContentService,
AllowableOperationsEnum,
PermissionsEnum,
NodesApiService,
FileUploadErrorEvent,
NotificationService
} from '@alfresco/adf-core';
import { PreviewService } from '../../services/preview.service'; import { PreviewService } from '../../services/preview.service';
@Component({ @Component({
@ -64,10 +70,10 @@ export class FileViewComponent implements OnInit {
constructor(private router: Router, constructor(private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private snackBar: MatSnackBar,
private nodeApiService: NodesApiService, private nodeApiService: NodesApiService,
private contentServices: ContentService, private contentServices: ContentService,
private preview: PreviewService) { private preview: PreviewService,
private notificationService: NotificationService) {
} }
ngOnInit() { ngOnInit() {
@ -105,7 +111,7 @@ export class FileViewComponent implements OnInit {
onUploadError(event: FileUploadErrorEvent) { onUploadError(event: FileUploadErrorEvent) {
const errorMessage = event.error; const errorMessage = event.error;
this.snackBar.open(errorMessage, '', { duration: 4000 }); this.notificationService.showError(errorMessage);
} }
toggleEmptyMetadata() { toggleEmptyMetadata() {

View File

@ -18,9 +18,8 @@
import { Component, Inject, ViewEncapsulation } from '@angular/core'; import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MinimalNodeEntryEntity } from '@alfresco/js-api'; import { MinimalNodeEntryEntity } from '@alfresco/js-api';
import { MatSnackBar } from '@angular/material/snack-bar';
import { PreviewService } from '../../services/preview.service'; import { PreviewService } from '../../services/preview.service';
import { FileUploadErrorEvent } from '@alfresco/adf-core'; import {FileUploadErrorEvent, NotificationService} from '@alfresco/adf-core';
@Component({ @Component({
templateUrl: './version-manager-dialog-adapter.component.html', templateUrl: './version-manager-dialog-adapter.component.html',
@ -37,8 +36,8 @@ export class VersionManagerDialogAdapterComponent {
showVersionComparison = false; showVersionComparison = false;
constructor(private previewService: PreviewService, constructor(private previewService: PreviewService,
private notificationService: NotificationService,
@Inject(MAT_DIALOG_DATA) data: any, @Inject(MAT_DIALOG_DATA) data: any,
private snackBar: MatSnackBar,
private containingDialog?: MatDialogRef<VersionManagerDialogAdapterComponent>) { private containingDialog?: MatDialogRef<VersionManagerDialogAdapterComponent>) {
this.contentEntry = data.contentEntry; this.contentEntry = data.contentEntry;
this.newFileVersion = data.hasOwnProperty('newFileVersion') ? data.newFileVersion : this.newFileVersion; this.newFileVersion = data.hasOwnProperty('newFileVersion') ? data.newFileVersion : this.newFileVersion;
@ -48,7 +47,7 @@ export class VersionManagerDialogAdapterComponent {
uploadError(event: FileUploadErrorEvent) { uploadError(event: FileUploadErrorEvent) {
const errorMessage = event.error; const errorMessage = event.error;
this.snackBar.open(errorMessage, '', { duration: 4000 }); this.notificationService.showError(errorMessage);
} }
close() { close() {

View File

@ -116,6 +116,7 @@ for more information about installing and using the source code.
| [Sidebar action menu component](core/components/sidebar-action-menu.component.md) | Displays a sidebar-action menu information panel. | [Source](../lib/core/layout/components/sidebar-action/sidebar-action-menu.component.ts) | | [Sidebar action menu component](core/components/sidebar-action-menu.component.md) | Displays a sidebar-action menu information panel. | [Source](../lib/core/layout/components/sidebar-action/sidebar-action-menu.component.ts) |
| [Sidenav Layout component](core/components/sidenav-layout.component.md) | Displays the standard three-region ADF application layout. | [Source](../lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts) | | [Sidenav Layout component](core/components/sidenav-layout.component.md) | Displays the standard three-region ADF application layout. | [Source](../lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts) |
| [Sorting Picker Component](core/components/sorting-picker.component.md) | Selects from a set of predefined sorting definitions and directions. | [Source](../lib/core/sorting-picker/sorting-picker.component.ts) | | [Sorting Picker Component](core/components/sorting-picker.component.md) | Selects from a set of predefined sorting definitions and directions. | [Source](../lib/core/sorting-picker/sorting-picker.component.ts) |
| [Snackbar Content Component](core/components/snackbar-content.component.md) | Custom content for Snackbar which allows use icon as action.| [Source](../lib/core/src/lib/snackbar-content/snackbar-content.component.ts) |
| [Start Form component](core/components/start-form.component.md) | Displays the Start Form for a process. | [Source](../lib/process-services/src/lib/form/start-form.component.ts) | | [Start Form component](core/components/start-form.component.md) | Displays the Start Form for a process. | [Source](../lib/process-services/src/lib/form/start-form.component.ts) |
| [Text Mask directive](core/components/text-mask.component.md) | Implements text field input masks. | [Source](../lib/core/form/components/widgets/text/text-mask.component.ts) | | [Text Mask directive](core/components/text-mask.component.md) | Implements text field input masks. | [Source](../lib/core/form/components/widgets/text/text-mask.component.ts) |
| [Toolbar Divider Component](core/components/toolbar-divider.component.md) | Divides groups of elements in a Toolbar with a visual separator. | [Source](../lib/core/toolbar/toolbar-divider.component.ts) | | [Toolbar Divider Component](core/components/toolbar-divider.component.md) | Divides groups of elements in a Toolbar with a visual separator. | [Source](../lib/core/toolbar/toolbar-divider.component.ts) |

View File

@ -0,0 +1,46 @@
---
Title: Snackbar Content Component
Added: v5.1.0
Status: Active
Last reviewed: 2022-11-08
---
# [Snackbar Content Component](lib/core/src/lib/snackbar-content/snackbar-content.component.ts "Defined in snackbar-content.component.ts")
Custom content for Snackbar which allows use icon as action.
## Basic Usage
```ts
snackBar.openFromComponent(SnackbarContentComponent, {
data: {
message: 'Some message',
actionLabel: "Some action label",
showAction: true
}
});
```
## Class members
### Properties
| Name | Type | Default value | Description |
|------|----------------|---------------|------------------------------------------------------------------|
| data | `SnackbarData` | false | Object which is injected into snackbar's content with it's data. |
### Snackbar Data
| Name | Type | Default value | Description |
|-------------|-----------|---------------|-------------------------------------------------------------------------|
| actionLabel | `string` | false | Displayed action as a text. |
| actionIcon | `string` | false | Displayed action as an material icon. |
| actionIconAriaLabel | `string` | false | Sets aria-label attribute for button with icon action. |
| message | `string` | false | Visible snackbar's message for user. |
| showAction | `boolean` | false | True if action should be visible, false in other case. |
| callActionOnIconClick | `boolean` | false | True if clicking on icon should to trigger action, false in other case. |
## Details
Snackbar allows using action as string by default which causes that there is no possibility to use mat-icon inside snackbar's content.
That custom content for Angular material Snackbar allows for that.

View File

@ -38,18 +38,21 @@ Shows a notification message with optional feedback.
- _message:_ `string` - Text message or translation key for the message. - _message:_ `string` - Text message or translation key for the message.
- _action:_ `string` - (Optional) Action name - _action:_ `string` - (Optional) Action name
- _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation - _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation
- _showError:_ `boolean` - (Optional) True if action should be visible, false if not. Default: true.
- **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` - - **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` -
- **showInfo**(message: `string`, action?: `string`, interpolateArgs?: `any`): [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>`<br/> - **showInfo**(message: `string`, action?: `string`, interpolateArgs?: `any`): [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>`<br/>
Rase info message Rase info message
- _message:_ `string` - Text message or translation key for the message. - _message:_ `string` - Text message or translation key for the message.
- _action:_ `string` - (Optional) Action name - _action:_ `string` - (Optional) Action name
- _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation - _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation
- _showError:_ `boolean` - (Optional) True if action should be visible, false if not. Default: true.
- **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` - - **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` -
- **showWarning**(message: `string`, action?: `string`, interpolateArgs?: `any`): [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>`<br/> - **showWarning**(message: `string`, action?: `string`, interpolateArgs?: `any`): [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>`<br/>
Rase warning message Rase warning message
- _message:_ `string` - Text message or translation key for the message. - _message:_ `string` - Text message or translation key for the message.
- _action:_ `string` - (Optional) Action name - _action:_ `string` - (Optional) Action name
- _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation - _interpolateArgs:_ `any` - (Optional) The interpolation parameters to add for the translation
- _showError:_ `boolean` - (Optional) True if action should be visible, false if not. Default: true.
- **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` - - **Returns** [`MatSnackBarRef`](https://material.angular.io/components/snack-bar/overview)`<any>` -
## Details ## Details

View File

@ -12,6 +12,7 @@ backend services have been tested with each released version of ADF.
## Versions ## Versions
- [v5.1.0](#v510)
- [v4.7.0](#v470) - [v4.7.0](#v470)
- [v4.6.0](#v460) - [v4.6.0](#v460)
- [v4.5.0](#v450) - [v4.5.0](#v450)
@ -39,6 +40,12 @@ backend services have been tested with each released version of ADF.
- [v2.1.0](#v210) - [v2.1.0](#v210)
- [v2.0.0](#v200) - [v2.0.0](#v200)
## v5.1.0
<!--v510 start-->
- [Snackbar Content](core/components/snackbar-content.component.md)
<!--v510 end-->
## v4.7.0 ## v4.7.0
<!--v470 start--> <!--v470 start-->

View File

@ -92,7 +92,7 @@ export class NotificationDemoPage {
} }
async clickActionButton(): Promise<void> { async clickActionButton(): Promise<void> {
await browser.executeScript(`document.querySelector("simple-snack-bar > div > button").click();`); await browser.executeScript(`document.querySelector(".adf-snackbar-message-content-action-label").click();`);
} }
async clearMessage(): Promise<void> { async clearMessage(): Promise<void> {

View File

@ -180,7 +180,7 @@ describe('PermissionListComponent', () => {
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON'); .toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
expect(element.querySelector('span[title="total"]').textContent.trim()) expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE'); .toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(document.querySelector('simple-snack-bar').textContent) expect(document.querySelector('.adf-snackbar-message-content').textContent)
.toContain('PERMISSION_MANAGER.ERROR.NOT-ALLOWED'); .toContain('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
}); });

View File

@ -75,7 +75,7 @@ export class UploadDragAreaComponent extends UploadBase implements NodeAllowable
const messageTranslate = this.translationService.instant('FILE_UPLOAD.MESSAGES.PROGRESS'); const messageTranslate = this.translationService.instant('FILE_UPLOAD.MESSAGES.PROGRESS');
const actionTranslate = this.translationService.instant('FILE_UPLOAD.ACTION.UNDO'); const actionTranslate = this.translationService.instant('FILE_UPLOAD.ACTION.UNDO');
this.notificationService.openSnackMessageAction(messageTranslate, actionTranslate, 3000).onAction().subscribe(() => { this.notificationService.openSnackMessageAction(messageTranslate, actionTranslate).onAction().subscribe(() => {
this.uploadService.cancelUpload(...latestFilesAdded); this.uploadService.cancelUpload(...latestFilesAdded);
}); });
} }

View File

@ -66,6 +66,7 @@ import { LegacyApiClientModule } from './api-factories/legacy-api-client.module'
import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module'; import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module';
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthenticationService } from './services/authentication.service'; import { AuthenticationService } from './services/authentication.service';
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
@NgModule({ @NgModule({
imports: [ imports: [
@ -177,7 +178,13 @@ export class CoreModule {
multi: true multi: true
}, },
{ provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: AuthenticationInterceptor, multi: true },
{ provide: Authentication, useClass: AuthenticationService } { provide: Authentication, useClass: AuthenticationService },
{
provide: MAT_SNACK_BAR_DEFAULT_OPTIONS,
useValue: {
duration: 10000
}
}
] ]
}; };
} }

View File

@ -21,6 +21,7 @@
"UNCLAIM": "RELEASE", "UNCLAIM": "RELEASE",
"START PROCESS": "START PROCESS", "START PROCESS": "START PROCESS",
"DATA_LOADING": "Data is loading", "DATA_LOADING": "Data is loading",
"CLOSE": "CLOSE",
"NOTIFICATIONS": { "NOTIFICATIONS": {
"NO_MESSAGE": "You have no notifications at this time.", "NO_MESSAGE": "You have no notifications at this time.",
"TITLE": "Notifications", "TITLE": "Notifications",

View File

@ -129,7 +129,7 @@ describe('Notification History Component', () => {
expect(callBackSpy).toHaveBeenCalled(); expect(callBackSpy).toHaveBeenCalled();
done(); done();
}); });
}); }, 10000);
it('should show load more button when there are more notifications', (done) => { it('should show load more button when there are more notifications', (done) => {
fixture.detectChanges(); fixture.detectChanges();
@ -164,7 +164,7 @@ describe('Notification History Component', () => {
expect(notification).toBeDefined(); expect(notification).toBeDefined();
done(); done();
}); });
}); }, 10000);
it('should be able to change the maximum number of notifications displayed', (done) => { it('should be able to change the maximum number of notifications displayed', (done) => {
component.maxNotifications = 10; component.maxNotifications = 10;

View File

@ -20,7 +20,7 @@ import { OverlayModule } from '@angular/cdk/overlay';
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ComponentFixture, TestBed} from '@angular/core/testing';
import { MatSnackBar, MatSnackBarModule, MatSnackBarConfig } from '@angular/material/snack-bar'; import {MatSnackBar, MatSnackBarConfig, MatSnackBarModule} from '@angular/material/snack-bar';
import {NotificationService} from './notification.service'; import {NotificationService} from './notification.service';
import {TranslationService} from '../../services/translation.service'; import {TranslationService} from '../../services/translation.service';
import {setupTestBed} from '../../testing/setup-test-bed'; import {setupTestBed} from '../../testing/setup-test-bed';

View File

@ -18,10 +18,11 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarRef, MatSnackBarConfig } from '@angular/material/snack-bar'; import { MatSnackBar, MatSnackBarRef, MatSnackBarConfig } from '@angular/material/snack-bar';
import { TranslationService } from '../../services/translation.service'; import { TranslationService } from '../../services/translation.service';
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { NotificationModel } from '../models/notification.model'; import { NotificationModel } from '../models/notification.model';
import { info, warning, error } from '../helpers/notification.factory'; import { info, warning, error } from '../helpers/notification.factory';
import {SnackbarContentComponent} from '../../snackbar-content';
import {SnackBarData} from '../../snackbar-content/snack-bar-data';
const INFO_SNACK_CLASS = 'adf-info-snackbar'; const INFO_SNACK_CLASS = 'adf-info-snackbar';
const WARN_SNACK_CLASS = 'adf-warning-snackbar'; const WARN_SNACK_CLASS = 'adf-warning-snackbar';
@ -31,15 +32,10 @@ const ERROR_SNACK_CLASS = 'adf-error-snackbar';
providedIn: 'root' providedIn: 'root'
}) })
export class NotificationService { export class NotificationService {
DEFAULT_DURATION_MESSAGE: number = 5000;
notifications$: Subject<NotificationModel> = new Subject(); notifications$: Subject<NotificationModel> = new Subject();
constructor(private snackBar: MatSnackBar, constructor(private snackBar: MatSnackBar,
private translationService: TranslationService, private translationService: TranslationService) {
private appConfigService: AppConfigService) {
this.DEFAULT_DURATION_MESSAGE = this.appConfigService.get<number>(AppConfigValues.NOTIFY_DURATION) || this.DEFAULT_DURATION_MESSAGE;
} }
/** /**
@ -50,7 +46,7 @@ export class NotificationService {
* @param interpolateArgs The interpolation parameters to add for the translation * @param interpolateArgs The interpolation parameters to add for the translation
* @returns Information/control object for the SnackBar * @returns Information/control object for the SnackBar
*/ */
openSnackMessage(message: string, config?: number | MatSnackBarConfig, interpolateArgs?: any): MatSnackBarRef<any> { openSnackMessage(message: string, config?: number | MatSnackBarConfig<Omit<SnackBarData, 'actionLabel' | 'message'>>, interpolateArgs?: any): MatSnackBarRef<any> {
return this.dispatchNotification(message, null, config, interpolateArgs); return this.dispatchNotification(message, null, config, interpolateArgs);
} }
@ -63,7 +59,7 @@ export class NotificationService {
* @param interpolateArgs The interpolation parameters to add for the translation * @param interpolateArgs The interpolation parameters to add for the translation
* @returns Information/control object for the SnackBar * @returns Information/control object for the SnackBar
*/ */
openSnackMessageAction(message: string, action: string, config?: number | MatSnackBarConfig, interpolateArgs?: any): MatSnackBarRef<any> { openSnackMessageAction(message: string, action: string, config?: number | MatSnackBarConfig<Omit<SnackBarData, 'actionLabel' | 'message'>>, interpolateArgs?: any): MatSnackBarRef<any> {
return this.dispatchNotification(message, action, config, interpolateArgs); return this.dispatchNotification(message, action, config, interpolateArgs);
} }
@ -73,9 +69,15 @@ export class NotificationService {
* @param message Text message or translation key for the message. * @param message Text message or translation key for the message.
* @param action Action name * @param action Action name
* @param interpolateArgs The interpolation parameters to add for the translation * @param interpolateArgs The interpolation parameters to add for the translation
* @param showAction True if action should be visible, false if not. Default: true.
*/ */
showError(message: string, action?: string, interpolateArgs?: any): MatSnackBarRef<any> { showError(message: string, action?: string, interpolateArgs?: any, showAction = true): MatSnackBarRef<any> {
return this.dispatchNotification(message, action, { panelClass: ERROR_SNACK_CLASS }, interpolateArgs); return this.dispatchNotification(message, action, {
panelClass: ERROR_SNACK_CLASS,
data: {
showAction
}
}, interpolateArgs);
} }
/** /**
@ -84,9 +86,15 @@ export class NotificationService {
* @param message Text message or translation key for the message. * @param message Text message or translation key for the message.
* @param action Action name * @param action Action name
* @param interpolateArgs The interpolation parameters to add for the translation * @param interpolateArgs The interpolation parameters to add for the translation
* @param showAction True if action should be visible, false if not. Default: true.
*/ */
showInfo(message: string, action?: string, interpolateArgs?: any): MatSnackBarRef<any> { showInfo(message: string, action?: string, interpolateArgs?: any, showAction = true): MatSnackBarRef<any> {
return this.dispatchNotification(message, action, { panelClass: INFO_SNACK_CLASS }, interpolateArgs); return this.dispatchNotification(message, action, {
panelClass: INFO_SNACK_CLASS,
data: {
showAction
}
}, interpolateArgs);
} }
/** /**
@ -95,9 +103,15 @@ export class NotificationService {
* @param message Text message or translation key for the message. * @param message Text message or translation key for the message.
* @param action Action name * @param action Action name
* @param interpolateArgs The interpolation parameters to add for the translation * @param interpolateArgs The interpolation parameters to add for the translation
* @param showAction True if action should be visible, false if not. Default: true.
*/ */
showWarning(message: string, action?: string, interpolateArgs?: any): MatSnackBarRef<any> { showWarning(message: string, action?: string, interpolateArgs?: any, showAction = true): MatSnackBarRef<any> {
return this.dispatchNotification(message, action, { panelClass: WARN_SNACK_CLASS }, interpolateArgs); return this.dispatchNotification(message, action, {
panelClass: WARN_SNACK_CLASS,
data: {
showAction
}
}, interpolateArgs);
} }
/** /**
@ -116,20 +130,28 @@ export class NotificationService {
this.notifications$.next(notification); this.notifications$.next(notification);
} }
private dispatchNotification(message: string, action?: string, config?: number | MatSnackBarConfig, interpolateArgs?: any): MatSnackBarRef<any> { private dispatchNotification(message: string, action?: string, config?: number | MatSnackBarConfig<Omit<SnackBarData, 'actionLabel' | 'message'>>, interpolateArgs?: any): MatSnackBarRef<any> {
const translatedMessage: string = this.translationService.instant(message, interpolateArgs); const translatedMessage: string = this.translationService.instant(message, interpolateArgs);
const translatedAction: string = this.translationService.instant(action, interpolateArgs); const translatedAction: string = this.translationService.instant(action, interpolateArgs);
const createNotification = this.getNotificationCreator(config); const createNotification = this.getNotificationCreator(config);
this.notifications$.next(createNotification(translatedMessage)); this.notifications$.next(createNotification(translatedMessage));
return this.snackBar.openFromComponent<SnackbarContentComponent, SnackBarData>(SnackbarContentComponent, {
return this.snackBar.open(translatedMessage, translatedAction, { ...(typeof config === 'number' && {duration: config}),
duration: (typeof config === 'number') ? config : this.DEFAULT_DURATION_MESSAGE,
panelClass: INFO_SNACK_CLASS, panelClass: INFO_SNACK_CLASS,
...( (typeof config === 'object') ? config : {} ) ...( (typeof config === 'object') ? config : {} ),
data: {
actionLabel: translatedAction,
actionIcon: 'close',
actionIconAriaLabel: 'CLOSE',
message: translatedMessage,
showAction: true,
callActionOnIconClick: false,
...( (typeof config === 'object') ? config.data : {} )
}
}); });
} }
private getNotificationCreator(config?: number | MatSnackBarConfig) { private getNotificationCreator(config?: number | MatSnackBarConfig<Omit<SnackBarData, 'actionLabel' | 'message'>>) {
let panelClass: string = null; let panelClass: string = null;
if (typeof config === 'object') { if (typeof config === 'object') {
panelClass = Array.isArray(config.panelClass) ? config.panelClass[0] : config.panelClass; panelClass = Array.isArray(config.panelClass) ? config.panelClass[0] : config.panelClass;

View File

@ -0,0 +1,18 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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.
*/
export * from './public-api';

View File

@ -0,0 +1,20 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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.
*/
export * from './snackbar-content.component';
export * from './snackbar-content.module';
export * from './snack-bar-data';

View File

@ -0,0 +1,25 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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.
*/
export interface SnackBarData {
actionLabel?: string;
actionIcon?: string;
actionIconAriaLabel?: string;
message: string;
showAction?: boolean;
callActionOnIconClick?: boolean;
}

View File

@ -0,0 +1,10 @@
<p class="adf-snackbar-message-content">{{ data.message }}</p>
<div *ngIf="data.showAction" class="adf-snackbar-message-content-action">
<button mat-button (click)="snackBarRef.dismissWithAction()" *ngIf="data.actionLabel" class="adf-snackbar-message-content-action-label">
{{data.actionLabel}}
</button>
<button mat-button *ngIf="data.actionIcon" (click)="onIconClicked()" class="adf-snackbar-message-content-action-icon"
[attr.aria-label]="data.actionIconAriaLabel | translate">
<mat-icon>{{ data.actionIcon }}</mat-icon>
</button>
</div>

View File

@ -0,0 +1,26 @@
:host {
display: flex;
align-items: center;
justify-content: space-between;
.adf-snackbar-message-content {
margin: 0;
}
.adf-snackbar-message-content-action {
margin-left: 24px;
.mat-button {
min-width: 0;
margin: -8px;
&.adf-snackbar-message-content-action-label {
margin-right: 8px;
}
&.adf-snackbar-message-content-action-icon {
padding: 0;
}
}
}
}

View File

@ -0,0 +1,118 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 {SnackbarContentComponent} from '@alfresco/adf-core';
import {MatIcon, MatIconModule} from '@angular/material/icon';
import {MAT_SNACK_BAR_DATA, MatSnackBarModule, MatSnackBarRef} from '@angular/material/snack-bar';
import {MatButtonModule} from '@angular/material/button';
import {By} from '@angular/platform-browser';
import {TranslateModule} from '@ngx-translate/core';
describe('SnackbarContentComponent', () => {
let component: SnackbarContentComponent;
let fixture: ComponentFixture<SnackbarContentComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SnackbarContentComponent],
imports: [
MatIconModule,
MatSnackBarModule,
MatButtonModule,
TranslateModule.forRoot()
],
providers: [{
provide: MatSnackBarRef,
useValue: {
dismissWithAction() {
}
}
}, {
provide: MAT_SNACK_BAR_DATA,
useValue: {}
}]
})
.compileComponents();
fixture = TestBed.createComponent(SnackbarContentComponent);
component = fixture.componentInstance;
});
it('should display message if message in data is set', () => {
component.data = {
message: 'Some message'
};
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-snackbar-message-content').innerText).toBe(component.data.message);
});
it('should not display message if message in data is not set', () => {
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-snackbar-message-content').innerText).toBe('');
});
it('should call snackBarRef.dismissWithAction() when button is clicked', () => {
component.data = {
message: '',
showAction: true,
actionLabel: 'Some action'
};
spyOn(component.snackBarRef, 'dismissWithAction');
fixture.detectChanges();
fixture.nativeElement.querySelector('.adf-snackbar-message-content-action-label').click();
expect(component.snackBarRef.dismissWithAction).toHaveBeenCalled();
});
it('should display actionLabel if actionLabel in data is set', () => {
component.data = {
message: '',
showAction: true,
actionLabel: 'Some action action'
};
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-snackbar-message-content-action-label').innerText).toBe(component.data.actionLabel);
});
it('should not display actionLabel if actionLabel in data is not set', () => {
component.data = {
message: '',
showAction: true
};
fixture.detectChanges();
expect(fixture.nativeElement.querySelector('.adf-snackbar-message-content-action-label')).toBeNull();
});
it('should render icon if actionIcon in data is set', () => {
component.data = {
message: '',
showAction: true,
actionIcon: 'close'
};
fixture.detectChanges();
expect(fixture.debugElement.query(By.directive(MatIcon))).toBeDefined();
});
it('should not render icon if actionIcon in data is not set', () => {
component.data = {
message: '',
showAction: true
};
fixture.detectChanges();
expect(fixture.debugElement.query(By.directive(MatIcon))).toBeNull();
});
});

View File

@ -0,0 +1,43 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 {Component, Inject} from '@angular/core';
import {MAT_SNACK_BAR_DATA, MatSnackBarRef} from '@angular/material/snack-bar';
import {SnackBarData} from './snack-bar-data';
@Component({
selector: 'adf-snackbar-content',
templateUrl: './snackbar-content.component.html',
styleUrls: ['./snackbar-content.component.scss'],
host: {
class: 'mat-simple-snackbar'
}
})
export class SnackbarContentComponent {
constructor(
public snackBarRef: MatSnackBarRef<SnackbarContentComponent>,
@Inject(MAT_SNACK_BAR_DATA) public data: SnackBarData
) {
if (!data) {
this.data = {message: ''};
}
}
onIconClicked(): void {
this.data.callActionOnIconClick ? this.snackBarRef.dismissWithAction() : this.snackBarRef.dismiss();
}
}

View File

@ -0,0 +1,41 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 { NgModule } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { CommonModule } from '@angular/common';
import { SnackbarContentComponent } from './snackbar-content.component';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatButtonModule } from '@angular/material/button';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
imports: [
CommonModule,
MatIconModule,
MatSnackBarModule,
MatButtonModule,
TranslateModule
],
declarations: [
SnackbarContentComponent
],
exports: [
SnackbarContentComponent
]
})
export class SnackbarContentModule {}

View File

@ -45,6 +45,7 @@ export * from './lib/notifications/index';
export * from './lib/search-text/index'; export * from './lib/search-text/index';
export * from './lib/blank-page/index'; export * from './lib/blank-page/index';
export * from './lib/rich-text-editor/index'; export * from './lib/rich-text-editor/index';
export * from './lib/snackbar-content/index';
export * from './lib/utils/index'; export * from './lib/utils/index';
export * from './lib/interface/index'; export * from './lib/interface/index';

View File

@ -21,7 +21,7 @@ import { BrowserActions } from '../utils/browser-actions';
export class SnackbarPage { export class SnackbarPage {
notificationSnackBar = $$('simple-snack-bar span').first(); notificationSnackBar = $$('.adf-snackbar-message-content').first();
snackBarAction = $('simple-snack-bar button span'); snackBarAction = $('simple-snack-bar button span');
snackBarContainerCss = $$('.mat-snack-bar-container'); snackBarContainerCss = $$('.mat-snack-bar-container');