]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: introduce side panel as a reusable component
authorNizamudeen A <nia@redhat.com>
Wed, 11 Jun 2025 07:59:58 +0000 (13:29 +0530)
committerAfreen Misbah <afreen@ibm.com>
Mon, 16 Mar 2026 07:20:15 +0000 (12:50 +0530)
Fixes: https://tracker.ceph.com/issues/71653
Signed-off-by: Nizamudeen A <nia@redhat.com>
(cherry picked from commit 36a2cce433e0a53c0851c1e04cad8f997baafae2)

 Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts

src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss
src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss

index d5337ed523328632a9ea6b634a460f03e88198cf..2b9695764edde9c07ea74ffdad3c29ba5e2700ec 100644 (file)
@@ -88,6 +88,8 @@ import { UpgradableComponent } from './upgradable/upgradable.component';
 import { ProgressComponent } from './progress/progress.component';
 import { TearsheetComponent } from './tearsheet/tearsheet.component';
 import { TearsheetStepComponent } from './tearsheet-step/tearsheet-step.component';
+import { SidePanelComponent } from './side-panel/side-panel.component';
+
 // Icons
 import InfoIcon from '@carbon/icons/es/information/16';
 import CopyIcon from '@carbon/icons/es/copy/32';
@@ -185,6 +187,7 @@ import { ChartsModule } from '@carbon/charts-angular';
     IconComponent,
     TearsheetComponent,
     TearsheetStepComponent,
+    SidePanelComponent
   ],
   providers: [provideCharts(withDefaultRegisterables())],
   exports: [
@@ -227,6 +230,7 @@ import { ChartsModule } from '@carbon/charts-angular';
     IconComponent,
     TearsheetComponent,
     TearsheetStepComponent,
+    SidePanelComponent
   ]
 })
 export class ComponentsModule {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.html
new file mode 100644 (file)
index 0000000..889607e
--- /dev/null
@@ -0,0 +1,26 @@
+<div class="overlay"
+     (click)="close()"
+     *ngIf="expanded && overlay"></div>
+
+<cds-panel [expanded]="expanded"
+           [attr.panel-size]="size">
+  <cds-icon-button
+    kind="ghost"
+    class="float-end"
+    title="Close"
+    (click)="close()">
+    <svg
+      class="cds--btn__icon"
+      cdsIcon="close"></svg>
+  </cds-icon-button>
+
+  <div
+    class="panel-header spacing-03"
+    *ngIf="headerText">
+      {{ headerText }}
+  </div>
+
+  <div class="spacing-03">
+    <ng-content select=".panel-content"></ng-content>
+  </div>
+</cds-panel>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.scss
new file mode 100644 (file)
index 0000000..3b4fc1c
--- /dev/null
@@ -0,0 +1,30 @@
+@use './src/styles/vendor/variables' as vv;
+
+:host ::ng-deep cds-panel {
+  &[panel-size='sm'] .cds--header-panel--expanded {
+    inline-size: 20rem;
+  }
+
+  &[panel-size='md'] .cds--header-panel--expanded {
+    inline-size: 30rem;
+  }
+
+  &[panel-size='lg'] .cds--header-panel--expanded {
+    inline-size: 40rem;
+  }
+
+  .cds--header-panel--expanded {
+    background-color: vv.$white;
+    max-inline-size: 100%;
+  }
+}
+
+.overlay {
+  background-color: vv.$side-panel-overlay-bg;
+  bottom: 0;
+  left: 0;
+  position: fixed;
+  right: 0;
+  top: 0;
+  z-index: 1000;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.spec.ts
new file mode 100644 (file)
index 0000000..1f7d97f
--- /dev/null
@@ -0,0 +1,61 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SidePanelComponent } from './side-panel.component';
+import { configureTestBed } from '~/testing/unit-test-helper';
+
+describe('SidePanelComponent', () => {
+  let component: SidePanelComponent;
+  let fixture: ComponentFixture<SidePanelComponent>;
+
+  configureTestBed({
+    declarations: [SidePanelComponent]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(SidePanelComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should show overlay only when expanded is true and slideOver is false', () => {
+    component.expanded = true;
+    component.overlay = true;
+    fixture.detectChanges();
+
+    const overlay = fixture.nativeElement.querySelector('.overlay');
+    expect(overlay).toBeTruthy();
+  });
+
+  it('should call close when overlay is clicked', () => {
+    spyOn(component, 'close');
+    component.expanded = true;
+    component.overlay = true;
+    fixture.detectChanges();
+
+    const overlay = fixture.nativeElement.querySelector('.overlay');
+    overlay.click();
+    expect(component.close).toHaveBeenCalled();
+  });
+
+  it('should call close when close button is clicked', () => {
+    spyOn(component, 'close');
+    component.expanded = true;
+    fixture.detectChanges();
+
+    const button = fixture.nativeElement.querySelector('cds-icon-button');
+    button.click();
+    expect(component.close).toHaveBeenCalled();
+  });
+
+  it('should show header text when headerText is provided', () => {
+    component.headerText = 'Test header text';
+    fixture.detectChanges();
+
+    const header = fixture.nativeElement.querySelector('.panel-header');
+    expect(header.textContent).toContain('Test header text');
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.ts
new file mode 100644 (file)
index 0000000..7b5a4ef
--- /dev/null
@@ -0,0 +1,19 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+  selector: 'cd-side-panel',
+  templateUrl: './side-panel.component.html',
+  styleUrl: './side-panel.component.scss'
+})
+export class SidePanelComponent {
+  @Input() expanded = false;
+  @Input() headerText = '';
+  @Input() overlay = true;
+  @Input() size: 'sm' | 'md' | 'lg' = 'lg';
+
+  @Output() closed = new EventEmitter<void>();
+
+  close() {
+    this.closed.emit();
+  }
+}
index 94aeabf11ae54ce9c4e6bedcaa699c5646d081bc..10d96792f424d35783145e8b32f05eab871037f1 100644 (file)
@@ -143,7 +143,8 @@ Overflow menu
 /******************************************
 Forms
 ******************************************/
-.form-header {
+.form-header,
+.panel-header {
   @include type.type-style('heading-04');
   margin-bottom: 40px;
 }
index ed319534085fdf9e845cfae3604ad1172e8b208f..cf5e2797603ef30a9da5b2da22237dfe417f9a85 100644 (file)
@@ -1,4 +1,5 @@
 // Color system
+@use '@carbon/colors';
 
 $white: #fff !default;
 $gray-100: #f8f9fa !default;
@@ -140,6 +141,9 @@ $tooltip-color: $white !default;
 $tooltip-bg: $body-color !default;
 $tooltip-opacity: 1 !default;
 
+// Side panel
+$side-panel-overlay-bg: rgba(colors.$gray-100, 50%) !default;
+
 // Misc
 
 $screen-sm-min: 576px !default;