From: Devika Babrekar Date: Thu, 11 Jun 2026 06:47:49 +0000 (+0530) Subject: mgr/dashboard: Fixing control flow on submit button for pool creation page X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=22e6ac309ad6b65bf9338b89548037ba06d4d438;p=ceph.git mgr/dashboard: Fixing control flow on submit button for pool creation page Fixes: https://tracker.ceph.com/issues/77030 Signed-off-by: Devika Babrekar 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 --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts index a38fb0583bb..84ecef6b155 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts @@ -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 }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts index de216e293db..86e39109359 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts @@ -350,3 +350,5 @@ export const AclHelperText: AclMaps = { export const POOL = { PATH: '/pool/create' }; + +export const FROM_STORAGE_CLASS = 'from-storage-class'; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html index b2df4636e8a..49dec8d6c73 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html @@ -131,7 +131,15 @@ spacingClass="mb-2" (action)="navigateCreatePool()" > - To create a new pool click here + To create a new pool click + + here + + { let component: RgwStorageClassFormComponent; let fixture: ComponentFixture; + 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']); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts index acb5eebd71d..9ee9d724cfe 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts @@ -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