From: Nizamudeen A Date: Wed, 11 Jun 2025 07:59:58 +0000 (+0530) Subject: mgr/dashboard: introduce side panel as a reusable component X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=ee503b990ba99418ee6072e6b433212f9b8b1e6e;p=ceph.git mgr/dashboard: introduce side panel as a reusable component Fixes: https://tracker.ceph.com/issues/71653 Signed-off-by: Nizamudeen A (cherry picked from commit 36a2cce433e0a53c0851c1e04cad8f997baafae2) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts --- 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 d5337ed52332..2b9695764edd 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 @@ -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 index 000000000000..889607e5e361 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.html @@ -0,0 +1,26 @@ +
+ + + + + + +
+ {{ headerText }} +
+ +
+ +
+
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 index 000000000000..3b4fc1c302c5 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.scss @@ -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 index 000000000000..1f7d97f3211d --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.spec.ts @@ -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; + + 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 index 000000000000..7b5a4efe0966 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/side-panel/side-panel.component.ts @@ -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(); + + close() { + this.closed.emit(); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss b/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss index 94aeabf11ae5..10d96792f424 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss @@ -143,7 +143,8 @@ Overflow menu /****************************************** Forms ******************************************/ -.form-header { +.form-header, +.panel-header { @include type.type-style('heading-04'); margin-bottom: 40px; } diff --git a/src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss b/src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss index ed319534085f..cf5e2797603e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles/defaults/_bootstrap-defaults.scss @@ -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;