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.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=36a2cce433e0a53c0851c1e04cad8f997baafae2;p=ceph.git mgr/dashboard: introduce side panel as a reusable component Fixes: https://tracker.ceph.com/issues/71653 Signed-off-by: Nizamudeen A --- 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 a46d9dc151672..fbd9d265d6b43 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 @@ -35,7 +35,8 @@ import { DropdownModule, SelectModule, ComboBoxModule, - ProgressIndicatorModule + ProgressIndicatorModule, + PanelModule } from 'carbon-components-angular'; import { MotdComponent } from '~/app/shared/components/motd/motd.component'; @@ -80,6 +81,7 @@ import { HelpTextComponent } from './help-text/help-text.component'; import { FormAdvancedFieldsetComponent } from './form-advanced-fieldset/form-advanced-fieldset.component'; import { UpgradableComponent } from './upgradable/upgradable.component'; import { ProgressComponent } from './progress/progress.component'; +import { SidePanelComponent } from './side-panel/side-panel.component'; // Icons import InfoIcon from '@carbon/icons/es/information/16'; @@ -122,7 +124,8 @@ import CopyIcon from '@carbon/icons/es/copy/32'; SelectModule, ComboBoxModule, ProgressIndicatorModule, - BaseChartDirective + BaseChartDirective, + PanelModule ], declarations: [ SparklineComponent, @@ -164,7 +167,8 @@ import CopyIcon from '@carbon/icons/es/copy/32'; HelpTextComponent, FormAdvancedFieldsetComponent, UpgradableComponent, - ProgressComponent + ProgressComponent, + SidePanelComponent ], providers: [provideCharts(withDefaultRegisterables())], exports: [ @@ -203,7 +207,8 @@ import CopyIcon from '@carbon/icons/es/copy/32'; HelpTextComponent, FormAdvancedFieldsetComponent, UpgradableComponent, - ProgressComponent + ProgressComponent, + 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 0000000000000..889607e5e3615 --- /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 0000000000000..3b4fc1c302c54 --- /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 0000000000000..1f7d97f3211dc --- /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 0000000000000..7b5a4efe09669 --- /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 61ad991178a7a..8440f0598e171 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles/_carbon-defaults.scss @@ -87,7 +87,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 ed319534085fd..cf5e2797603ef 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;