]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add productive card component 66914/head
authorAfreen Misbah <afreen@ibm.com>
Tue, 13 Jan 2026 20:47:40 +0000 (02:17 +0530)
committerAfreen Misbah <afreen@ibm.com>
Tue, 20 Jan 2026 19:33:09 +0000 (01:03 +0530)
- add generic productive card component
- based on carbon design system
- there are two versions of card - with shadow(tinted affect) and without.
- applies gray10 theme which is decided by new designs.

Fixes https://tracker.ceph.com/issues/74450

Signed-off-by: Afreen Misbah <afreen@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.ts [new file with mode: 0644]

index bc3bc26a68290cb5da96bb1e81df8978f2798dbb..2e74622998de3cd3e64b465c18d24ffa28501f01 100644 (file)
@@ -1,15 +1,10 @@
 <div class="container-fluid p-4">
   <div class="row d-flex flex-row ps-3">
-
     <!-- First Grid to hold Details and Inventory Card-->
-    <div class="col-sm-3 d-flex flex-column ps-2">
-
-      <!-- Details Card-->
-      <cd-card cardTitle="Details"
-               i18n-title
-               class="details"
-               aria-label="Details card">
-        <dl class="ms-4 me-4">
+    <div class="col-sm-3 d-flex flex-column ps-2 details-card">
+      <cd-productive-card title="Details"
+                          i18n-title>
+        <dl>
           <dt>Cluster ID</dt>
           <dd>{{ detailsCardData.fsid }}</dd>
           <dt>Orchestrator</dt>
@@ -23,7 +18,7 @@
           <dd>
             <a routerLink="/api-docs"
                target="_blank">
-               {{ origin }}/api-docs
+              {{ origin }}/api-docs
               <i class="fa fa-external-link"></i>
             </a>
           </dd>
@@ -57,7 +52,7 @@
             </span>
           </ng-container>
         </dl>
-      </cd-card>
+      </cd-productive-card>
 
       <!-- Inventory Card-->
       <cd-card cardTitle="Inventory"
index 0f2702b5ccc869ebf2fa1cb430380c2713a1a9fe..5fc3d4bb1175ca4a79310cb9ab741037abc10fc0 100644 (file)
     max-width: 83vw;
   }
 }
+
+.details-card {
+  dl,
+  dd:last-of-type {
+    margin-bottom: 0;
+  }
+}
index 0bb9a79501cb2a95dcb76696e2092c65a835f208..14300de21db071fcc587344ef566139e78eb233b 100644 (file)
@@ -217,7 +217,7 @@ describe('Dashbord Component', () => {
   it('should render all cards', () => {
     fixture.detectChanges();
     const dashboardCards = fixture.debugElement.nativeElement.querySelectorAll('cd-card');
-    expect(dashboardCards.length).toBe(5);
+    expect(dashboardCards.length).toBe(4);
   });
 
   it('should get corresponding data into detailsCardData', () => {
index 0e400e019941b70f54746b0e3ec82ad957bcb227..32588db56c12fd689c6cda3c57fe991f5c75e021 100644 (file)
@@ -12,8 +12,14 @@ import { CephSharedModule } from '../shared/ceph-shared.module';
 import { FeedbackComponent } from '../shared/feedback/feedback.component';
 import { DashboardComponent } from './dashboard/dashboard.component';
 import { HealthPieComponent } from './health-pie/health-pie.component';
-import { InputModule, ModalModule, SelectModule, ToggletipModule } from 'carbon-components-angular';
 import { OverviewComponent } from '../overview/overview.component';
+import {
+  InputModule,
+  ModalModule,
+  SelectModule,
+  ThemeModule,
+  ToggletipModule
+} from 'carbon-components-angular';
 
 @NgModule({
   imports: [
@@ -30,7 +36,8 @@ import { OverviewComponent } from '../overview/overview.component';
     ModalModule,
     InputModule,
     SelectModule,
-    OverviewComponent
+    OverviewComponent,
+    ThemeModule
   ],
   exports: [OverviewComponent],
   declarations: [DashboardComponent, HealthPieComponent, FeedbackComponent],
index 12d70e741f9696ccd75ebff8d4f36dec27454437..947c80aece04432011a3c82f275a029bee6d3798 100644 (file)
@@ -42,7 +42,8 @@ import {
   PopoverModule,
   InlineLoadingModule,
   TagModule,
-  LinkModule
+  LinkModule,
+  LayerModule
 } from 'carbon-components-angular';
 import EditIcon from '@carbon/icons/es/edit/20';
 import CodeIcon from '@carbon/icons/es/code/16';
@@ -102,6 +103,7 @@ import downloadIcon from '@carbon/icons/es/download/16';
 import IdeaIcon from '@carbon/icons/es/idea/20';
 import CloseIcon from '@carbon/icons/es/close/16';
 import { TearsheetStepComponent } from './tearsheet-step/tearsheet-step.component';
+import { ProductiveCardComponent } from './productive-card/productive-card.component';
 
 @NgModule({
   imports: [
@@ -148,7 +150,8 @@ import { TearsheetStepComponent } from './tearsheet-step/tearsheet-step.componen
     PopoverModule,
     InlineLoadingModule,
     TagModule,
-    LinkModule
+    LinkModule,
+    LayerModule
   ],
   declarations: [
     SparklineComponent,
@@ -196,7 +199,8 @@ import { TearsheetStepComponent } from './tearsheet-step/tearsheet-step.componen
     DetailsCardComponent,
     ToastComponent,
     TearsheetComponent,
-    TearsheetStepComponent
+    TearsheetStepComponent,
+    ProductiveCardComponent
   ],
   providers: [provideCharts(withDefaultRegisterables())],
   exports: [
@@ -241,7 +245,8 @@ import { TearsheetStepComponent } from './tearsheet-step/tearsheet-step.componen
     DetailsCardComponent,
     ToastComponent,
     TearsheetComponent,
-    TearsheetStepComponent
+    TearsheetStepComponent,
+    ProductiveCardComponent
   ]
 })
 export class ComponentsModule {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.html
new file mode 100644 (file)
index 0000000..67a4cbf
--- /dev/null
@@ -0,0 +1,31 @@
+<cds-tile class="productive-card"
+          [ngClass]="{'productive-card--shadow': applyShadow}"
+          [cdsLayer]="0">
+  <header
+    cdsGrid
+    [useCssGrid]="true"
+    [narrow]="true"
+    class="productive-card-header">
+    <h2 cdsCol
+        [columnNumbers]="{md: headerActionTemplate ? 12 : 16, lg: headerActionTemplate ? 12 : 16}"
+        class="cds--type-heading-compact-02 productive-card-header-title">
+      {{title}}
+    </h2>
+  @if(headerActionTemplate) {
+  <div cdsCol
+       [columnNumbers]="{md: 4, lg: 4}"
+       class="productive-card-header-actions">
+    <ng-container *ngTemplateOutlet="headerActionTemplate"></ng-container>
+  </div>
+  }
+  </header>
+  <section class="productive-card-section cds--type-body-compact-01"
+           [ngClass]="{'productive-card-section--footer': footerTemplate}">
+    <ng-content></ng-content>
+  </section>
+  @if(footerTemplate) {
+  <footer class="productive-card-footer">
+    <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
+  </footer>
+  }
+</cds-tile>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.scss
new file mode 100644 (file)
index 0000000..cbfd140
--- /dev/null
@@ -0,0 +1,48 @@
+@use '@carbon/colors';
+
+.productive-card {
+  margin: 0;
+  padding: 0;
+
+  &-header {
+    margin: 0;
+    padding: var(--cds-spacing-05);
+
+    &-title {
+      margin: 0;
+    }
+
+    &-actions {
+      margin: 0;
+    }
+  }
+
+  &-section {
+    margin: 0;
+    padding: var(--cds-spacing-05);
+    padding-top: 0;
+
+    &--footer {
+      padding-bottom: 0;
+    }
+  }
+
+  &-footer {
+    height: var(--cds-spacing-08);
+    padding: var(--cds-spacing-05);
+  }
+
+  &--shadow {
+    background-color: var(--cds-layer);
+    position: relative;
+    overflow: hidden; // ensures proper edge fade
+    background-image:
+      // cyan left edge
+      radial-gradient(120% 60% at -15% 100%, rgba(colors.$cyan-60, 0.13) 0%, transparent 65%),
+      // cyan right edge
+      radial-gradient(120% 60% at 115% 100%, rgba(colors.$cyan-60, 0.1) 0%, transparent 65%),
+      // pink center
+      radial-gradient(120% 60% at 50% 100%, rgba(colors.$magenta-60, 0.11) 0%, transparent 70%);
+    box-shadow: var(--cds-ai-drop-shadow), inset 0 0 0 1px var(--cds-ai-inner-shadow);
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.spec.ts
new file mode 100644 (file)
index 0000000..0fe568b
--- /dev/null
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProductiveCardComponent } from './productive-card.component';
+import { GridModule, LayerModule, TilesModule } from 'carbon-components-angular';
+
+describe('ProductiveCardComponent', () => {
+  let component: ProductiveCardComponent;
+  let fixture: ComponentFixture<ProductiveCardComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ProductiveCardComponent],
+      imports: [GridModule, LayerModule, TilesModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(ProductiveCardComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/productive-card/productive-card.component.ts
new file mode 100644 (file)
index 0000000..7047a1a
--- /dev/null
@@ -0,0 +1,38 @@
+import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
+
+/**
+ * A generic productive card component.
+ *
+ * @example
+ * <cd-productive-card title="Card Title"
+ *                     [applyShadow]="true">
+ *   <ng-template #headerAction>...</ng-template>
+ *   <ng-template #footer>...</ng-template>
+ *   <p>My card body content</p>
+ * </cd-productive-card>
+ */
+@Component({
+  selector: 'cd-productive-card',
+  standalone: false,
+  templateUrl: './productive-card.component.html',
+  styleUrl: './productive-card.component.scss'
+})
+export class ProductiveCardComponent {
+  /* Card Title */
+  @Input() title!: string;
+
+  /* Optional: Applies a tinted-colored background to card */
+  @Input() applyShadow: boolean = false;
+
+  /* Optional: Header action template, appears alongwith title in top-right corner */
+  @ContentChild('headerAction', {
+    read: TemplateRef
+  })
+  headerActionTemplate?: TemplateRef<any>;
+
+  /* Optional: Footer template , otherwise no footer will be used for card.*/
+  @ContentChild('footer', {
+    read: TemplateRef
+  })
+  footerTemplate?: TemplateRef<any>;
+}