<cd-navigation>
<div class="container-fluid h-100"
[ngClass]="{'dashboard': (router.url == '/dashboard' || router.url == '/dashboard_3' || router.url == '/multi-cluster/overview'), 'rgw-dashboard': (router.url == '/rgw/overview')}">
- <cd-context></cd-context>
+ <!-- ************************ -->
+ <!-- ALERTS BANNER -->
+ <!-- ************************ -->
+ <div class="cd-alert-container"
+ [ngClass]="{'ms-4 me-4': (router.url == '/dashboard' || router.url == '/dashboard_3' || router.url == '/multi-cluster/overview'), 'm-3': (router.url == '/rgw/overview')}">
+ <cd-pwd-expiration-notification></cd-pwd-expiration-notification>
+ <cd-telemetry-notification></cd-telemetry-notification>
+ <cd-motd></cd-motd>
+ </div>
+ <cd-context></cd-context>
<cd-breadcrumbs></cd-breadcrumbs>
<router-outlet></router-outlet>
</div>
.rgw-dashboard {
background-color: vv.$body-bg-alt;
}
+
+.cd-alert-container {
+ display: flex;
+ flex-direction: column;
+}
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('showTopNotification', () => {
+ const notification1 = 'notificationName1';
+ const notification2 = 'notificationName2';
+
+ beforeEach(() => {
+ component.notifications = [];
+ });
+
+ it('should show notification', () => {
+ component.showTopNotification(notification1, true);
+ expect(component.notifications.includes(notification1)).toBeTruthy();
+ expect(component.notifications.length).toBe(1);
+ });
+
+ it('should not add a second notification if it is already shown', () => {
+ component.showTopNotification(notification1, true);
+ component.showTopNotification(notification1, true);
+ expect(component.notifications.includes(notification1)).toBeTruthy();
+ expect(component.notifications.length).toBe(1);
+ });
+
+ it('should add a second notification if the first one is different', () => {
+ component.showTopNotification(notification1, true);
+ component.showTopNotification(notification2, true);
+ expect(component.notifications.includes(notification1)).toBeTruthy();
+ expect(component.notifications.includes(notification2)).toBeTruthy();
+ expect(component.notifications.length).toBe(2);
+ });
+
+ it('should hide an active notification', () => {
+ component.showTopNotification(notification1, true);
+ expect(component.notifications.includes(notification1)).toBeTruthy();
+ expect(component.notifications.length).toBe(1);
+ component.showTopNotification(notification1, false);
+ expect(component.notifications.length).toBe(0);
+ });
+
+ it('should not fail if it tries to hide an inactive notification', () => {
+ expect(() => component.showTopNotification(notification1, false)).not.toThrow();
+ expect(component.notifications.length).toBe(0);
+ });
+
+ it('should keep other notifications if it hides one', () => {
+ component.showTopNotification(notification1, true);
+ component.showTopNotification(notification2, true);
+ expect(component.notifications.length).toBe(2);
+ component.showTopNotification(notification2, false);
+ expect(component.notifications.length).toBe(1);
+ expect(component.notifications.includes(notification1)).toBeTruthy();
+ });
+ });
});
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { FaviconService } from '~/app/shared/services/favicon.service';
import { SummaryService } from '~/app/shared/services/summary.service';
import { TaskManagerService } from '~/app/shared/services/task-manager.service';
+import { TelemetryNotificationService } from '../../../shared/services/telemetry-notification.service';
+import { MotdNotificationService } from '~/app/shared/services/motd-notification.service';
+import _ from 'lodash';
@Component({
selector: 'cd-workbench-layout',
providers: [FaviconService]
})
export class WorkbenchLayoutComponent implements OnInit, OnDestroy {
+ notifications: string[] = [];
private subs = new Subscription();
permissions: Permissions;
+ @HostBinding('class') get class(): string {
+ return 'top-notification-' + this.notifications.length;
+ }
constructor(
public router: Router,
private taskManagerService: TaskManagerService,
private multiClusterService: MultiClusterService,
private faviconService: FaviconService,
- private authStorageService: AuthStorageService
+ private authStorageService: AuthStorageService,
+ private telemetryNotificationService: TelemetryNotificationService,
+ private motdNotificationService: MotdNotificationService
) {
this.permissions = this.authStorageService.getPermissions();
}
}
this.subs.add(this.summaryService.startPolling());
this.subs.add(this.taskManagerService.init(this.summaryService));
+
+ this.subs.add(
+ this.authStorageService.isPwdDisplayed$.subscribe((isDisplayed) => {
+ this.showTopNotification('isPwdDisplayed', isDisplayed);
+ })
+ );
+ this.subs.add(
+ this.telemetryNotificationService.update.subscribe((visible: boolean) => {
+ this.showTopNotification('telemetryNotificationEnabled', visible);
+ })
+ );
+ this.subs.add(
+ this.motdNotificationService.motd$.subscribe((motd: any) => {
+ this.showTopNotification('motdNotificationEnabled', _.isPlainObject(motd));
+ })
+ );
this.faviconService.init();
}
+ showTopNotification(name: string, isDisplayed: boolean) {
+ if (isDisplayed) {
+ if (!this.notifications.includes(name)) {
+ this.notifications.push(name);
+ }
+ } else {
+ const index = this.notifications.indexOf(name);
+ if (index >= 0) {
+ this.notifications.splice(index, 1);
+ }
+ }
+ }
ngOnDestroy() {
this.subs.unsubscribe();
<div class="cd-navbar-main">
<!-- ************************ -->
-<!-- NOTIFICATIONS/ALERTS -->
+<!-- NOTIFICATIONS -->
<!-- ************************ -->
-<cd-pwd-expiration-notification></cd-pwd-expiration-notification>
-<cd-telemetry-notification></cd-telemetry-notification>
-<cd-motd></cd-motd>
<cd-notifications-sidebar></cd-notifications-sidebar>
<!-- ************************ -->
<!-- HEADER -->
});
}
});
-
- describe('showTopNotification', () => {
- const notification1 = 'notificationName1';
- const notification2 = 'notificationName2';
-
- beforeEach(() => {
- component.notifications = [];
- });
-
- it('should show notification', () => {
- component.showTopNotification(notification1, true);
- expect(component.notifications.includes(notification1)).toBeTruthy();
- expect(component.notifications.length).toBe(1);
- });
-
- it('should not add a second notification if it is already shown', () => {
- component.showTopNotification(notification1, true);
- component.showTopNotification(notification1, true);
- expect(component.notifications.includes(notification1)).toBeTruthy();
- expect(component.notifications.length).toBe(1);
- });
-
- it('should add a second notification if the first one is different', () => {
- component.showTopNotification(notification1, true);
- component.showTopNotification(notification2, true);
- expect(component.notifications.includes(notification1)).toBeTruthy();
- expect(component.notifications.includes(notification2)).toBeTruthy();
- expect(component.notifications.length).toBe(2);
- });
-
- it('should hide an active notification', () => {
- component.showTopNotification(notification1, true);
- expect(component.notifications.includes(notification1)).toBeTruthy();
- expect(component.notifications.length).toBe(1);
- component.showTopNotification(notification1, false);
- expect(component.notifications.length).toBe(0);
- });
-
- it('should not fail if it tries to hide an inactive notification', () => {
- expect(() => component.showTopNotification(notification1, false)).not.toThrow();
- expect(component.notifications.length).toBe(0);
- });
-
- it('should keep other notifications if it hides one', () => {
- component.showTopNotification(notification1, true);
- component.showTopNotification(notification2, true);
- expect(component.notifications.length).toBe(2);
- component.showTopNotification(notification2, false);
- expect(component.notifications.length).toBe(1);
- expect(component.notifications.includes(notification1)).toBeTruthy();
- });
- });
});
-import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import * as _ from 'lodash';
FeatureTogglesMap$,
FeatureTogglesService
} from '~/app/shared/services/feature-toggles.service';
-import { MotdNotificationService } from '~/app/shared/services/motd-notification.service';
import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
import { SummaryService } from '~/app/shared/services/summary.service';
-import { TelemetryNotificationService } from '~/app/shared/services/telemetry-notification.service';
@Component({
selector: 'cd-navigation',
styleUrls: ['./navigation.component.scss']
})
export class NavigationComponent implements OnInit, OnDestroy {
- notifications: string[] = [];
clusterDetails: any[] = [];
- @HostBinding('class') get class(): string {
- return 'top-notification-' + this.notifications.length;
- }
permissions: Permissions;
enabledFeature$: FeatureTogglesMap$;
private router: Router,
private summaryService: SummaryService,
private featureToggles: FeatureTogglesService,
- private telemetryNotificationService: TelemetryNotificationService,
public prometheusAlertService: PrometheusAlertService,
- private motdNotificationService: MotdNotificationService,
private cookieService: CookiesService,
private settingsService: SettingsService
) {
this.summaryData = summary;
})
);
- /*
- Note: If you're going to add more top notifications please do not forget to increase
- the number of generated css-classes in section topNotification settings in the scss
- file.
- */
- this.subs.add(
- this.authStorageService.isPwdDisplayed$.subscribe((isDisplayed) => {
- this.showTopNotification('isPwdDisplayed', isDisplayed);
- })
- );
- this.subs.add(
- this.telemetryNotificationService.update.subscribe((visible: boolean) => {
- this.showTopNotification('telemetryNotificationEnabled', visible);
- })
- );
- this.subs.add(
- this.motdNotificationService.motd$.subscribe((motd: any) => {
- this.showTopNotification('motdNotificationEnabled', _.isPlainObject(motd));
- })
- );
this.subs.add(
this.multiClusterService.subscribeClusterTokenStatus((resp: object) => {
this.clusterTokenStatus = resp;
this.rightSidebarOpen = !this.rightSidebarOpen;
}
- showTopNotification(name: string, isDisplayed: boolean) {
- if (isDisplayed) {
- if (!this.notifications.includes(name)) {
- this.notifications.push(name);
- }
- } else {
- const index = this.notifications.indexOf(name);
- if (index >= 0) {
- this.notifications.splice(index, 1);
- }
- }
- }
-
onClusterSelection(value: object) {
this.multiClusterService.setCluster(value).subscribe(
(resp: any) => {
-<ngb-alert type="{{ bootstrapClass }}"
- [dismissible]="dismissible"
- (closed)="onClose()"
- [ngClass]="spacingClass">
- <table>
- <ng-container *ngIf="size === 'normal'; else slim">
- <tr>
- <td *ngIf="showIcon"
- rowspan="2"
- class="alert-panel-icon">
- <i [ngClass]="[icons.large3x]"
- class="alert-{{ bootstrapClass }} {{ typeIcon }}"
- aria-hidden="true"></i>
- </td>
- <td *ngIf="showTitle"
- class="alert-panel-title">{{ title }}</td>
- </tr>
- <tr>
- <td class="alert-panel-text">
- <ng-container *ngTemplateOutlet="content"></ng-container>
- </td>
- </tr>
- </ng-container>
- <ng-template #slim>
- <tr>
- <td *ngIf="showIcon"
- class="alert-panel-icon">
- <i class="alert-{{ bootstrapClass }} {{ typeIcon }}"
- aria-hidden="true"></i>
- </td>
- <td *ngIf="showTitle"
- class="alert-panel-title">{{ title }}</td>
- <td class="alert-panel-text">
- <ng-container *ngTemplateOutlet="content"></ng-container>
- </td>
- </tr>
- </ng-template>
- </table>
-</ngb-alert>
+<cds-actionable-notification
+ class="mb-1"
+ [notificationObj]="notificationContent"
+ *ngIf="size === 'slim'; else normal">
+</cds-actionable-notification>
+
+<ng-template #normal>
+ <ngb-alert type="{{ bootstrapClass }}"
+ [dismissible]="dismissible"
+ (closed)="onClose()"
+ [ngClass]="spacingClass">
+ <table>
+ <ng-container *ngIf="size === 'normal'">
+ <tr>
+ <td *ngIf="showIcon"
+ rowspan="2"
+ class="alert-panel-icon">
+ <i [ngClass]="[icons.large3x]"
+ class="alert-{{ bootstrapClass }} {{ typeIcon }}"
+ aria-hidden="true"></i>
+ </td>
+ <td *ngIf="showTitle"
+ class="alert-panel-title">{{ title }}</td>
+ </tr>
+ <tr>
+ <td class="alert-panel-text">
+ <ng-container *ngTemplateOutlet="content"></ng-container>
+ </td>
+ </tr>
+ </ng-container>
+ </table>
+ </ngb-alert>
+</ng-template>
<ng-template #content>
<ng-content></ng-content>
</ng-template>
+
+<ng-template #closeTpl>
+ <button cdsActionableButton
+ cdsButton="ghost"
+ size="md"
+ title="Close"
+ (click)="onClose()"
+ *ngIf="dismissible">
+ <svg class="cds--btn__icon"
+ cdsIcon="close"
+ size="16"></svg>
+ </button>
+</ng-template>
.alert {
margin-bottom: 0;
}
+
+cds-actionable-notification {
+ max-width: 100%;
+}
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import {
+ Component,
+ EventEmitter,
+ Input,
+ OnInit,
+ Output,
+ TemplateRef,
+ ViewChild
+} from '@angular/core';
+import { NotificationContent, NotificationType } from 'carbon-components-angular';
import { Icons } from '~/app/shared/enum/icons.enum';
styleUrls: ['./alert-panel.component.scss']
})
export class AlertPanelComponent implements OnInit {
+ @ViewChild('content', { static: true })
+ alertContent: TemplateRef<any>;
+ @ViewChild('closeTpl', { static: true })
+ closeTpl: TemplateRef<any>;
+
@Input()
title = '';
@Input()
icons = Icons;
+ notificationContent: NotificationContent;
+
ngOnInit() {
+ const type: NotificationType = this.type === 'danger' ? 'error' : this.type;
+ this.notificationContent = {
+ type: type,
+ template: this.alertContent,
+ actionsTemplate: this.closeTpl,
+ showClose: false,
+ title: this.title
+ };
+
switch (this.type) {
case 'warning':
this.title = this.title || $localize`Warning`;
import { ClickOutsideModule } from 'ng-click-outside';
import { NgChartsModule } from 'ng2-charts';
import { SimplebarAngularModule } from 'simplebar-angular';
-import { UIShellModule, ButtonModule, NotificationModule } from 'carbon-components-angular';
+import {
+ UIShellModule,
+ ButtonModule,
+ NotificationModule,
+ IconModule
+} from 'carbon-components-angular';
import { MotdComponent } from '~/app/shared/components/motd/motd.component';
import { DirectivesModule } from '../directives/directives.module';
NgbTimepickerModule,
UIShellModule,
ButtonModule,
- NotificationModule
+ NotificationModule,
+ IconModule
],
declarations: [
SparklineComponent,
<cd-alert-panel *ngIf="displayNotification"
- class="no-margin-bottom telemetry-notification"
+ class="no-margin-bottom"
[showTitle]="false"
size="slim"
[type]="notificationSeverity"
color: darken(vv.$primary, 10);
font-weight: bold;
}
-
-.telemetry-notification {
- bottom: 25px;
- position: absolute;
- right: 50%;
- transform: translateX(50%);
- z-index: 99999;
-}