]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: add support for inline-tip notification
authorNaman Munet <naman.munet@ibm.com>
Fri, 27 Jun 2025 08:30:42 +0000 (14:00 +0530)
committerNaman Munet <naman.munet@ibm.com>
Mon, 21 Jul 2025 06:55:58 +0000 (12:25 +0530)
https://tracker.ceph.com/issues/71870

Signed-off-by: Naman Munet <naman.munet@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/enum/icons.enum.ts
src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss

index 868a09b7af182d9921d3871601f4ebe255357d8b..b22124cebfdc7434cef11467f3be314de555ef1f 100644 (file)
@@ -36,7 +36,8 @@ import {
   SelectModule,
   ComboBoxModule,
   ProgressIndicatorModule,
-  PanelModule
+  PanelModule,
+  LayoutModule
 } from 'carbon-components-angular';
 import EditIcon from '@carbon/icons/es/edit/20';
 import CodeIcon from '@carbon/icons/es/code/16';
@@ -83,13 +84,16 @@ import { FormAdvancedFieldsetComponent } from './form-advanced-fieldset/form-adv
 import { UpgradableComponent } from './upgradable/upgradable.component';
 import { ProgressComponent } from './progress/progress.component';
 import { SidePanelComponent } from './side-panel/side-panel.component';
+import { ChartsModule } from '@carbon/charts-angular';
+import { InlineMessageComponent } from './inline-message/inline-message.component';
+import { IconComponent } from './icon/icon.component';
 
 // Icons
 import InfoIcon from '@carbon/icons/es/information/16';
 import CopyIcon from '@carbon/icons/es/copy/32';
 import downloadIcon from '@carbon/icons/es/download/16';
-import { ChartsModule } from '@carbon/charts-angular';
-import { IconComponent } from './icon/icon.component';
+import IdeaIcon from '@carbon/icons/es/idea/20';
+import CloseIcon from '@carbon/icons/es/close/16';
 
 @NgModule({
   imports: [
@@ -130,7 +134,8 @@ import { IconComponent } from './icon/icon.component';
     ProgressIndicatorModule,
     BaseChartDirective,
     PanelModule,
-    ChartsModule
+    ChartsModule,
+    LayoutModule
   ],
   declarations: [
     SparklineComponent,
@@ -174,7 +179,8 @@ import { IconComponent } from './icon/icon.component';
     UpgradableComponent,
     ProgressComponent,
     SidePanelComponent,
-    IconComponent
+    IconComponent,
+    InlineMessageComponent
   ],
   providers: [provideCharts(withDefaultRegisterables())],
   exports: [
@@ -215,11 +221,20 @@ import { IconComponent } from './icon/icon.component';
     UpgradableComponent,
     ProgressComponent,
     SidePanelComponent,
-    IconComponent
+    IconComponent,
+    InlineMessageComponent
   ]
 })
 export class ComponentsModule {
   constructor(private iconService: IconService) {
-    this.iconService.registerAll([InfoIcon, CopyIcon, EditIcon, CodeIcon, downloadIcon]);
+    this.iconService.registerAll([
+      InfoIcon,
+      CopyIcon,
+      EditIcon,
+      CodeIcon,
+      downloadIcon,
+      IdeaIcon,
+      CloseIcon
+    ]);
   }
 }
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.html
new file mode 100644 (file)
index 0000000..d6fba56
--- /dev/null
@@ -0,0 +1,46 @@
+@if (!isDismissed) {
+  <div class="inline-tip-container"
+       [cdsStack]="'horizontal'"
+       [gap]="4">
+    <div class="close-btn-position">
+      <cds-icon-button class="close-btn inline-tip-bg-color"
+                       type="button"
+                       kind="ghost"
+                       size="sm"
+                       description="Close"
+                       i18n-description
+                       (click)="close()">
+        <svg [size]="icons.size16"
+             [cdsIcon]="icons.destroy"></svg>
+      </cds-icon-button>
+    </div>
+
+    <div class="inline-tip-body"
+         [cdsStack]="'horizontal'"
+         [gap]="4">
+      <svg [cdsIcon]="icons.idea"
+           [size]="icons.size20"></svg>
+
+      <div [cdsStack]="'vertical'"
+           [gap]="1"
+           class="inline-tip-body-content">
+      @if (title) {
+        <h1 class="cds--type-heading-compact-01">{{ title }}</h1>
+      }
+
+        <div class="cds--type-body-compact-01"
+             [class.cds--text-truncate--end]="isTruncated">
+          <ng-content></ng-content>
+        </div>
+        @if (collapsible) {
+        <button class="inline-tip-bg-color btn-toggle"
+                cdsButton="ghost"
+                size="md"
+                (click)="toggleContent()">
+          <span i18n>{{ isTruncated ? 'Read more' : 'Read less' }}</span>
+        </button>
+      }
+      </div>
+    </div>
+  </div>
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.scss
new file mode 100644 (file)
index 0000000..7afc990
--- /dev/null
@@ -0,0 +1,42 @@
+@use '@carbon/colors';
+@use '@carbon/themes';
+@use './src/styles/vendor/variables' as vv;
+@use '@carbon/layout';
+
+.inline-tip-container {
+  position: relative;
+  background: linear-gradient(90deg, colors.$blue-90, colors.$purple-70);
+  color: vv.$white;
+  padding: layout.$spacing-05;
+
+  .inline-tip-body-content {
+    width: 80%;
+
+    .btn-toggle {
+      margin-top: layout.$spacing-05;
+      color: themes.$link-inverse-hover;
+
+      &:hover {
+        color: themes.$link-inverse-active;
+      }
+    }
+  }
+}
+
+.close-btn-position {
+  position: absolute;
+  top: 0;
+  right: 0;
+
+  .close-btn {
+    padding: 0;
+
+    svg {
+      fill: themes.$icon-on-color;
+    }
+  }
+}
+
+.inline-tip-bg-color:hover {
+  background-color: #7f3ae7;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.spec.ts
new file mode 100644 (file)
index 0000000..a12590e
--- /dev/null
@@ -0,0 +1,59 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { InlineMessageComponent } from './inline-message.component';
+import { ButtonModule, IconModule, LayoutModule } from 'carbon-components-angular';
+
+describe('InlineMessageComponent', () => {
+  let component: InlineMessageComponent;
+  let fixture: ComponentFixture<InlineMessageComponent>;
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [InlineMessageComponent],
+      imports: [LayoutModule, IconModule, ButtonModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(InlineMessageComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should have default values for inputs', () => {
+    expect(component.collapsible).toBe(false);
+    expect(component.title).toBe('');
+    expect(component.onClose).toBeDefined();
+  });
+
+  it('should set collapsible input', () => {
+    component.collapsible = true;
+    expect(component.collapsible).toBe(true);
+  });
+
+  it('should set title input', () => {
+    component.title = 'Test Title';
+    expect(component.title).toBe('Test Title');
+  });
+
+  it('should call onClose and set isDismissed to true when Close is called', () => {
+    const onCloseSpy = jasmine.createSpy('onClose');
+    component.onClose = onCloseSpy;
+    component.isDismissed = false;
+    component.close();
+    expect(component.isDismissed).toBe(true);
+    expect(onCloseSpy).toHaveBeenCalled();
+  });
+
+  it('should toggle isTruncated when toggleContent is called', () => {
+    component.isTruncated = false;
+    component.toggleContent();
+    expect(component.isTruncated).toBe(true);
+    component.toggleContent();
+    expect(component.isTruncated).toBe(false);
+  });
+
+  it('should have icons property set to Icons enum', () => {
+    expect(component.icons).toBeDefined();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/inline-message/inline-message.component.ts
new file mode 100644 (file)
index 0000000..aa15a77
--- /dev/null
@@ -0,0 +1,36 @@
+import { Component, Input } from '@angular/core';
+import { Icons } from '../../enum/icons.enum';
+
+@Component({
+  selector: 'cd-inline-message',
+  templateUrl: './inline-message.component.html',
+  styleUrl: './inline-message.component.scss'
+})
+export class InlineMessageComponent {
+  // collapsible when true will show read more/read less button
+  @Input()
+  collapsible = false;
+  // title to show in the message
+
+  @Input()
+  title = '';
+
+  // callback function to execute onclose
+  @Input()
+  onClose?: () => void = () => {};
+
+  isTruncated = false;
+  icons = Icons;
+  isDismissed = false;
+
+  close() {
+    this.isDismissed = true;
+    if (this.onClose) {
+      this.onClose();
+    }
+  }
+
+  toggleContent() {
+    this.isTruncated = !this.isTruncated;
+  }
+}
index 8c86a302f18bbfaada35ca79d31780230727513c..f1895743c7945869538513c420e985b00bcb1996 100644 (file)
@@ -83,6 +83,7 @@ export enum Icons {
   launch = 'launch',
   parentChild = 'parent-child',
   dataTable = 'data-table',
+  idea = 'idea',
   /* Icons for special effect */
   size16 = '16',
   size20 = '20',
index 7bc6e6c0079027ebc6c5da4169ab581f948e31ef..b1ba3d9ec47889384de1e208a67d4c2b1affb13c 100644 (file)
@@ -31,6 +31,7 @@
 );
 @use '@carbon/styles';
 @use '@carbon/type';
+@use '@carbon/styles/scss/utilities/text-truncate' as textTruncate;
 
 /******************************************
 Custom theme
@@ -40,7 +41,6 @@ Custom theme
 /******************************************
 Datatable
 ******************************************/
-
 :root {
   @include type.type-classes();
 }
@@ -212,3 +212,7 @@ Tabs
 .cds-mb-4 {
   margin-bottom: layout.$spacing-02;
 }
+
+.cds--text-truncate--end {
+  @include textTruncate.text-truncate-end;
+}