From: Nizamudeen A Date: Sun, 16 Jun 2024 12:46:52 +0000 (+0530) Subject: mgr/dashboard: fix alert broken for multiple alerts X-Git-Tag: v20.0.0~1700^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F58069%2Fhead;p=ceph.git mgr/dashboard: fix alert broken for multiple alerts After carbon the alert panel was broken when there are multiple alerts present (telemetry, motd, password expiration). Applying carbon banner to the existing alert banner Fixes: https://tracker.ceph.com/issues/66512 Signed-off-by: Nizamudeen A --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.html index 2b3c82bfe20b..958ba64129a6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.html @@ -2,7 +2,16 @@
- + + + +
+ + + +
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.scss index e44a6d0afb37..321f684da291 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.scss @@ -16,3 +16,8 @@ .rgw-dashboard { background-color: vv.$body-bg-alt; } + +.cd-alert-container { + display: flex; + flex-direction: column; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.spec.ts index faf8c9cdf940..22451d8206a0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.spec.ts @@ -32,4 +32,56 @@ describe('WorkbenchLayoutComponent', () => { 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(); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.ts index 054ebf8bba11..230e6e7ae445 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/layouts/workbench-layout/workbench-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -9,6 +9,9 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; 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', @@ -17,8 +20,12 @@ import { TaskManagerService } from '~/app/shared/services/task-manager.service'; 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, @@ -26,7 +33,9 @@ export class WorkbenchLayoutComponent implements OnInit, OnDestroy { 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(); } @@ -38,8 +47,36 @@ export class WorkbenchLayoutComponent implements OnInit, OnDestroy { } 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(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html index 8a2c69840380..a4fb9ab24166 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html @@ -1,10 +1,7 @@
- + - - - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.spec.ts index 92d9a28878a0..c674cfdcf5c0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.spec.ts @@ -214,56 +214,4 @@ describe('NavigationComponent', () => { }); } }); - - 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(); - }); - }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts index d0b5fcf81069..fefe1d8dab5b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import * as _ from 'lodash'; @@ -14,10 +14,8 @@ import { 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', @@ -25,11 +23,7 @@ import { TelemetryNotificationService } from '~/app/shared/services/telemetry-no 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$; @@ -54,9 +48,7 @@ export class NavigationComponent implements OnInit, OnDestroy { private router: Router, private summaryService: SummaryService, private featureToggles: FeatureTogglesService, - private telemetryNotificationService: TelemetryNotificationService, public prometheusAlertService: PrometheusAlertService, - private motdNotificationService: MotdNotificationService, private cookieService: CookiesService, private settingsService: SettingsService ) { @@ -91,26 +83,6 @@ export class NavigationComponent implements OnInit, OnDestroy { 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; @@ -165,19 +137,6 @@ export class NavigationComponent implements OnInit, OnDestroy { 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) => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.html index 30f8b530a59c..58761eead54b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.html @@ -1,43 +1,50 @@ - - - - - - - - - - - - - - - - - - -
- - {{ title }}
- -
- - {{ title }} - -
-
+ + + + + + + + + + + + + + + +
+ + {{ title }}
+ +
+
+
+ + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.scss index 6b89d6d3e38a..98541e9bfda8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.scss @@ -10,3 +10,7 @@ .alert { margin-bottom: 0; } + +cds-actionable-notification { + max-width: 100%; +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.ts index cc2024baa233..3402eea57427 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/alert-panel/alert-panel.component.ts @@ -1,4 +1,13 @@ -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'; @@ -8,6 +17,11 @@ import { Icons } from '~/app/shared/enum/icons.enum'; styleUrls: ['./alert-panel.component.scss'] }) export class AlertPanelComponent implements OnInit { + @ViewChild('content', { static: true }) + alertContent: TemplateRef; + @ViewChild('closeTpl', { static: true }) + closeTpl: TemplateRef; + @Input() title = ''; @Input() @@ -36,7 +50,18 @@ export class AlertPanelComponent implements OnInit { 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`; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts index 21d333cc0f90..e6ccde0a3629 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts @@ -15,7 +15,12 @@ import { 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'; @@ -80,7 +85,8 @@ import { UpgradableComponent } from './upgradable/upgradable.component'; NgbTimepickerModule, UIShellModule, ButtonModule, - NotificationModule + NotificationModule, + IconModule ], declarations: [ SparklineComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/telemetry-notification/telemetry-notification.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/telemetry-notification/telemetry-notification.component.html index 98f473e3bbf7..9af7958370a7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/telemetry-notification/telemetry-notification.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/telemetry-notification/telemetry-notification.component.html @@ -1,5 +1,5 @@