]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: carbonize-osd-flags-modal 66776/head
authorSagar Gopale <sagar.gopale@ibm.com>
Fri, 2 Jan 2026 05:47:53 +0000 (11:17 +0530)
committerSagar Gopale <sagar.gopale@ibm.com>
Fri, 27 Mar 2026 05:57:48 +0000 (11:27 +0530)
Fixes: https://tracker.ceph.com/issues/74298
Signed-off-by: Sagar Gopale <sagar.gopale@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-flags-indiv-modal/osd-flags-indiv-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-flags-indiv-modal/osd-flags-indiv-modal.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-flags-indiv-modal/osd-flags-indiv-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/styles/ceph-custom/_spacings.scss

index a91be0ea580f3051badf873093eb2b11c444eead..d9fb2321f28473dceb9d44af36f30bf2efe22cfe 100644 (file)
@@ -1,48 +1,67 @@
-<cd-modal [modalRef]="activeModal">
-  <ng-container class="modal-title"
-                i18n>Individual OSD Flags</ng-container>
+<cds-modal size="sm"
+           [open]="open"
+           [hasScrollingContent]="false"
+           (overlaySelected)="closeModal()">
+  <cds-modal-header (closeSelect)="closeModal()">
+    <h3 cdsModalHeaderHeading
+        modal-primary-focus
+        i18n>Individual OSD flags</h3>
+  </cds-modal-header>
 
-  <ng-container class="modal-content">
+  <div cdsModalContent>
     <form name="osdFlagsForm"
-          #formDir="ngForm"
           [formGroup]="osdFlagsForm"
           novalidate>
-      <div class="modal-body osd-modal">
-        <div class="custom-control custom-checkbox"
-             *ngFor="let flag of flags; let last = last">
-          <input class="custom-control-input"
-                 type="checkbox"
-                 [checked]="flag.value"
-                 [indeterminate]="flag.indeterminate"
-                 (change)="changeValue(flag)"
-                 [name]="flag.code"
-                 [id]="flag.code">
-          <label class="custom-control-label"
-                 [for]="flag.code"
-                 ng-class="['tc_' + key]">
+      <div>
+        @for (flag of flags; track flag.code; let last = $last) {
+        <div>
+          <cds-checkbox
+            [checked]="flag.value"
+            [indeterminate]="flag.indeterminate"
+            (checkedChange)="changeValue(flag)"
+            [name]="flag.code"
+            [id]="flag.code"
+          >
             <strong>{{ flag.name }}</strong>
-            <cds-tag class="tag-hdd ms-2"
-                     [ngbTooltip]="clusterWideTooltip"
-                     *ngIf="flag.clusterWide"
+            @if (flag.clusterWide) {
+            <cds-tag class="cds-ml-3"
+                     [cdsTooltip]="clusterWideTooltip"
+                     trigger="hover"
                      i18n>Cluster-wide</cds-tag>
-            <br>
-            <span class="form-text text-muted">{{ flag.description }}</span>
-          </label>
-          <hr class="m-1"
-              *ngIf="!last">
-        </div>
-      </div>
+            }
 
-      <div class="modal-footer">
-        <button type="button"
-                class="btn btn-light"
-                (click)="resetSelection()"
-                i18n>Restore previous selection</button>
-        <cd-form-button-panel (submitActionEvent)="submitAction()"
-                              [form]="osdFlagsForm"
-                              [showSubmit]="permissions.osd.update"
-                              [submitText]="actionLabels.UPDATE"></cd-form-button-panel>
+            <div class="cds-mt-4">
+              <cd-help-text>{{ flag.description }}</cd-help-text>
+            </div>
+          </cds-checkbox>
+          @if (!last) {
+          <hr class="cds-m-1" />
+          }
+        </div>
+        }
       </div>
     </form>
-  </ng-container>
-</cd-modal>
+  </div>
+
+  <cds-modal-footer>
+    <button [cdsButton]="'ghost'"
+            (click)="closeModal()"
+            type="button"
+            i18n>
+      Cancel
+    </button>
+    <button [cdsButton]="'secondary'"
+            (click)="resetSelection()"
+            type="button"
+            i18n>
+      Restore previous selection
+    </button>
+    @if (permissions.osd.update) {
+    <cd-submit-button
+      (submitAction)="submitAction()"
+      [form]="osdFlagsForm"
+      [submitText]="actionLabels.UPDATE"
+    >{{ actionLabels.UPDATE }}</cd-submit-button>
+    }
+  </cds-modal-footer>
+</cds-modal>
index 93c9e9adcbbf0484aeed02dfb40905a5eb7bf6e1..78096e6bdbdc40df69c336336788686df2c27bd6 100644 (file)
@@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { ReactiveFormsModule } from '@angular/forms';
 import { RouterTestingModule } from '@angular/router/testing';
 
-import { NgbActiveModal, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
+import { ModalService, TooltipModule } from 'carbon-components-angular';
 import { ToastrModule } from 'ngx-toastr';
 import { of as observableOf } from 'rxjs';
 
@@ -27,11 +27,11 @@ describe('OsdFlagsIndivModalComponent', () => {
       ReactiveFormsModule,
       SharedModule,
       ToastrModule.forRoot(),
-      NgbTooltipModule,
+      TooltipModule,
       RouterTestingModule
     ],
     declarations: [OsdFlagsIndivModalComponent],
-    providers: [NgbActiveModal]
+    providers: [ModalService]
   });
 
   beforeEach(() => {
@@ -137,7 +137,6 @@ describe('OsdFlagsIndivModalComponent', () => {
     describe('submitAction', () => {
       let notificationType: NotificationType;
       let notificationService: NotificationService;
-      let bsModalRef: NgbActiveModal;
       let flags: object;
 
       beforeEach(() => {
@@ -145,8 +144,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         spyOn(notificationService, 'show').and.callFake((type) => {
           notificationType = type;
         });
-        bsModalRef = TestBed.inject(NgbActiveModal);
-        spyOn(bsModalRef, 'close').and.callThrough();
+        spyOn(component, 'closeModal');
         flags = {
           nodown: false,
           noin: false,
@@ -165,7 +163,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         req.flush({ flags, ids: [0] });
         expect(req.request.body).toEqual({ flags, ids: [0] });
         expect(notificationType).toBe(NotificationType.success);
-        expect(component.activeModal.close).toHaveBeenCalledTimes(1);
+        expect(component.closeModal).toHaveBeenCalledTimes(1);
       });
 
       it('should submit multiple flags', () => {
@@ -180,7 +178,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         req.flush({ flags, ids: [0] });
         expect(req.request.body).toEqual({ flags, ids: [0] });
         expect(notificationType).toBe(NotificationType.success);
-        expect(component.activeModal.close).toHaveBeenCalledTimes(1);
+        expect(component.closeModal).toHaveBeenCalledTimes(1);
       });
 
       it('should hide modal if request fails', () => {
@@ -189,7 +187,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         const req = httpTesting.expectOne('api/osd/flags/individual');
         req.flush([], { status: 500, statusText: 'failure' });
         expect(notificationService.show).toHaveBeenCalledTimes(0);
-        expect(component.activeModal.close).toHaveBeenCalledTimes(1);
+        expect(component.closeModal).toHaveBeenCalledTimes(1);
       });
     });
   });
@@ -269,7 +267,6 @@ describe('OsdFlagsIndivModalComponent', () => {
     describe('submitAction', () => {
       let notificationType: NotificationType;
       let notificationService: NotificationService;
-      let bsModalRef: NgbActiveModal;
       let flags: object;
 
       beforeEach(() => {
@@ -277,8 +274,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         spyOn(notificationService, 'show').and.callFake((type) => {
           notificationType = type;
         });
-        bsModalRef = TestBed.inject(NgbActiveModal);
-        spyOn(bsModalRef, 'close').and.callThrough();
+        spyOn(component, 'closeModal');
         flags = {
           nodown: false,
           noin: false,
@@ -299,7 +295,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         req.flush({ flags, ids: submittedIds });
         expect(req.request.body).toEqual({ flags, ids: submittedIds });
         expect(notificationType).toBe(NotificationType.success);
-        expect(component.activeModal.close).toHaveBeenCalledTimes(1);
+        expect(component.closeModal).toHaveBeenCalledTimes(1);
       });
 
       it('should submit multiple flags for multiple OSDs', () => {
@@ -316,7 +312,7 @@ describe('OsdFlagsIndivModalComponent', () => {
         req.flush({ flags, ids: submittedIds });
         expect(req.request.body).toEqual({ flags, ids: submittedIds });
         expect(notificationType).toBe(NotificationType.success);
-        expect(component.activeModal.close).toHaveBeenCalledTimes(1);
+        expect(component.closeModal).toHaveBeenCalledTimes(1);
       });
     });
   });
index 63e1bc8389695c9627a13ca94d613e9621c021b6..819a5c614412d4b533fd7adaf8762b77dc077afb 100644 (file)
@@ -1,7 +1,7 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, OnInit, Optional } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
 
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { CdForm } from '~/app/shared/forms/cd-form';
 import _ from 'lodash';
 
 import { OsdService } from '~/app/shared/api/osd.service';
@@ -18,9 +18,8 @@ import { NotificationService } from '~/app/shared/services/notification.service'
   styleUrls: ['./osd-flags-indiv-modal.component.scss'],
   standalone: false
 })
-export class OsdFlagsIndivModalComponent implements OnInit {
+export class OsdFlagsIndivModalComponent extends CdForm implements OnInit {
   permissions: Permissions;
-  selected: object[];
   initialSelection: Flag[] = [];
   osdFlagsForm = new UntypedFormGroup({});
   flags: Flag[] = [
@@ -60,13 +59,15 @@ export class OsdFlagsIndivModalComponent implements OnInit {
   clusterWideTooltip: string = $localize`The flag has been enabled for the entire cluster.`;
 
   constructor(
-    public activeModal: NgbActiveModal,
+    @Optional() @Inject('selected') public selected: object[] = [],
     public actionLabels: ActionLabelsI18n,
     private authStorageService: AuthStorageService,
     private osdService: OsdService,
     private notificationService: NotificationService
   ) {
+    super();
     this.permissions = this.authStorageService.getPermissions();
+    this.selected = this.selected || [];
   }
 
   ngOnInit() {
@@ -125,10 +126,10 @@ export class OsdFlagsIndivModalComponent implements OnInit {
     this.osdService.updateIndividualFlags(activeFlags, selectedIds).subscribe(
       () => {
         this.notificationService.show(NotificationType.success, $localize`Updated OSD Flags`);
-        this.activeModal.close();
+        this.closeModal();
       },
       () => {
-        this.activeModal.close();
+        this.closeModal();
       }
     );
   }
index b16e7fea10d8137bda84f5b8c7898cb758496325..2ff19e44fe9fadd905ecc8a19f48dd8fb6d40586 100644 (file)
@@ -502,7 +502,7 @@ export class OsdListComponent extends ListWithDetails implements OnInit {
     const initialState = {
       selected: this.getSelectedOsds()
     };
-    this.bsModalRef = this.modalService.show(OsdFlagsIndivModalComponent, initialState);
+    this.bsModalRef = this.cdsModalService.show(OsdFlagsIndivModalComponent, initialState);
   }
 
   showConfirmationModal(markAction: string, onSubmit: (id: number) => Observable<any>) {
index cb29ca8e494cea24b6d2cf4f1625a064d1076a5a..7d3d0ff98212b883ea90ee7929904c822e6daebd 100644 (file)
   margin: 0;
 }
 
+.cds-m-1 {
+  margin: layout.$spacing-01;
+}
+
 .cds-mb-0 {
   margin-bottom: 0;
 }
   margin-bottom: layout.$spacing-06;
 }
 
-.cds-mt-3 {
-  margin-top: layout.$spacing-03;
-}
-
 .cds-mt-1 {
   margin-top: layout.$spacing-01;
 }
 
+.cds-mt-2 {
+  margin-top: layout.$spacing-02;
+}
+
+.cds-mt-3 {
+  margin-top: layout.$spacing-03;
+}
+
 .cds-mt-4 {
   margin-top: layout.$spacing-04;
 }