]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Fixing control flow on submit button for pool creation page 69218/head
authorDevika Babrekar <devika.babrekar@ibm.com>
Thu, 11 Jun 2026 06:47:49 +0000 (12:17 +0530)
committerDevika Babrekar <devika.babrekar@ibm.com>
Thu, 16 Jul 2026 06:04:31 +0000 (11:34 +0530)
Fixes: https://tracker.ceph.com/issues/77030
Signed-off-by: Devika Babrekar <devika.babrekar@ibm.com>
Conflicts:
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html
- updated conditions to render ui

src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts

index a38fb0583bbbd91ce60e04a87c3e1af5038b1873..84ecef6b155b6bbe6b72284e1cfd80e0b33b4c73 100644 (file)
@@ -41,6 +41,7 @@ import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service';
 import { MonitorService } from '~/app/shared/api/monitor.service';
 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
 import { Permissions } from '~/app/shared/models/permissions';
+import { FROM_STORAGE_CLASS } from './../../rgw/models/rgw-storage-class.model';
 
 interface FormFieldDescription {
   externalFieldName: string;
@@ -106,6 +107,8 @@ export class PoolFormComponent extends CdForm implements OnInit {
   isApplicationsSelected = true;
   msrCrush: boolean = false;
   isStretchMode: boolean = false;
+  private fromStorageClass: boolean = false;
+  private previousPath: string = '';
 
   readonly DEFAULT_REPLICATED_MIN_SIZE = 1;
   readonly DEFAULT_REPLICATED_MAX_SIZE = 3;
@@ -136,6 +139,9 @@ export class PoolFormComponent extends CdForm implements OnInit {
     this.resource = $localize`pool`;
     this.authenticate();
     this.createForm();
+    const nav = this.router.getCurrentNavigation();
+    this.fromStorageClass = nav?.extras?.state?.['from'] === FROM_STORAGE_CLASS;
+    this.previousPath = nav?.extras?.state?.['returnUrl'] || '/pool';
   }
 
   authenticate() {
@@ -1090,10 +1096,18 @@ export class PoolFormComponent extends CdForm implements OnInit {
           }
           this.form.setErrors({ cdSubmitButton: true });
         },
-        complete: () => this.router.navigate(['/pool'])
+        complete: () => this.navigateAfterPoolForm()
       });
   }
 
+  navigateAfterPoolForm(): void {
+    if (this.fromStorageClass) {
+      this.router.navigate([this.previousPath]);
+    } else {
+      this.router.navigate(['/pool']);
+    }
+  }
+
   appSelection(events: SelectOption[]) {
     this.data.applications.selected = events.map((e: SelectOption) => e.name);
     this.form.get('name').updateValueAndValidity({ emitEvent: false, onlySelf: true });
index de216e293db7db8afdfef1d1492f388445711a87..86e3910935968cbebef4b5af67cdccb6ec74179c 100644 (file)
@@ -350,3 +350,5 @@ export const AclHelperText: AclMaps = {
 export const POOL = {
   PATH: '/pool/create'
 };
+
+export const FROM_STORAGE_CLASS = 'from-storage-class';
index b2df4636e8a8f67c080e41d03afe6d07ec4c210b..49dec8d6c738aacba6b2669b5e930da4190f7c1e 100644 (file)
             spacingClass="mb-2"
             (action)="navigateCreatePool()"
           >
-            <span i18n>To create a new pool click <a [routerLink]="[POOL.PATH]">here</a></span>
+            <span i18n
+              >To create a new pool click
+              <a
+                href="#"
+                (click)="navigateCreatePool()"
+              >
+                here
+              </a>
+            </span>
           </cd-alert-panel>
           <cds-select
             label="Pool"
index 919a19a398e34550630b7e636f3da8dd1975c29d..58a3b638a2db8ecf82589443b82256b2b1b4c04a 100644 (file)
@@ -1,7 +1,8 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { Router } from '@angular/router';
 import { SharedModule } from '~/app/shared/shared.module';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { RouterTestingModule } from '@angular/router/testing';
 import { ReactiveFormsModule } from '@angular/forms';
 
@@ -15,10 +16,15 @@ import {
 import { CoreModule } from '~/app/core/core.module';
 import { RgwStorageClassFormComponent } from './rgw-storage-class-form.component';
 import { TIER_TYPE_DISPLAY } from '../models/rgw-storage-class.model';
+import { PoolFormComponent } from '../../pool/pool-form/pool-form.component';
+import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
+import { PoolService } from '~/app/shared/api/pool.service';
+import { of } from 'rxjs';
 
 describe('RgwStorageClassFormComponent', () => {
   let component: RgwStorageClassFormComponent;
   let fixture: ComponentFixture<RgwStorageClassFormComponent>;
+  let router: Router;
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
@@ -38,8 +44,14 @@ describe('RgwStorageClassFormComponent', () => {
       declarations: [RgwStorageClassFormComponent]
     }).compileComponents();
 
+    spyOn(TestBed.inject(RgwZonegroupService), 'getAllZonegroupsInfo').and.returnValue(
+      of({ zonegroups: [], default_zonegroup: '' })
+    );
+    spyOn(TestBed.inject(PoolService), 'getList').and.returnValue(of([]));
+
     fixture = TestBed.createComponent(RgwStorageClassFormComponent);
     component = fixture.componentInstance;
+    router = TestBed.inject(Router);
     fixture.detectChanges();
   });
 
@@ -195,4 +207,47 @@ describe('RgwStorageClassFormComponent', () => {
       expect(component).toBeTruthy();
     });
   });
+
+  describe('pool form navigation from storage class form', () => {
+    const storageClassReturnUrl = '/rgw/storage-class/create';
+
+    const createPoolFormNavigationContext = (
+      fromStorageClass: boolean,
+      previousPath: string,
+      poolRouter: Router
+    ) => {
+      const poolForm = Object.create(PoolFormComponent.prototype) as PoolFormComponent;
+      (poolForm as any).fromStorageClass = fromStorageClass;
+      (poolForm as any).previousPath = previousPath;
+      (poolForm as any).router = poolRouter;
+      return poolForm;
+    };
+
+    it('should return to storage class form after pool creation when opened from storage class', () => {
+      const navigateSpy = spyOn(router, 'navigate');
+      const poolForm = createPoolFormNavigationContext(true, storageClassReturnUrl, router);
+
+      poolForm.navigateAfterPoolForm();
+
+      expect(navigateSpy).toHaveBeenCalledWith([storageClassReturnUrl]);
+    });
+
+    it('should return to pool list after pool creation when not opened from storage class', () => {
+      const navigateSpy = spyOn(router, 'navigate');
+      const poolForm = createPoolFormNavigationContext(false, '/pool', router);
+
+      poolForm.navigateAfterPoolForm();
+
+      expect(navigateSpy).toHaveBeenCalledWith(['/pool']);
+    });
+
+    it('should return to pool list when pool creation is cancelled outside storage class form', () => {
+      const navigateSpy = spyOn(router, 'navigate');
+      const poolForm = createPoolFormNavigationContext(false, '/pool', router);
+
+      poolForm.navigateAfterPoolForm();
+
+      expect(navigateSpy).toHaveBeenCalledWith(['/pool']);
+    });
+  });
 });
index acb5eebd71dd183ea557b998f1d342c24faf470d..9ee9d724cfedfe83052d0e8e1f0e89796977beba 100644 (file)
@@ -64,7 +64,8 @@ import {
   AclType,
   ZoneRequest,
   AllZonesResponse,
-  POOL
+  POOL,
+  FROM_STORAGE_CLASS
 } from '../models/rgw-storage-class.model';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
 import { NotificationService } from '~/app/shared/services/notification.service';
@@ -120,6 +121,7 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
   rgwPools: Pool[];
   zones: any[];
   POOL = POOL;
+  FROM_STORAGE_CLASS = FROM_STORAGE_CLASS;
 
   constructor(
     public actionLabels: ActionLabelsI18n,
@@ -633,6 +635,12 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
     this.router.navigate([`rgw/storage-class`]);
   }
 
+  navigateCreatePool(): void {
+    this.router.navigate([POOL.PATH], {
+      state: { from: FROM_STORAGE_CLASS, returnUrl: this.router.url }
+    });
+  }
+
   getTierTargetByStorageClass(placementTargetInfo: PlacementTarget, storageClass: string) {
     const tierTarget = placementTargetInfo?.tier_targets?.find(
       (target: TierTarget) => target.val.storage_class === storageClass