[NIFI-13197] - Clicking outside of a dialog no longer closes the dialog (#8811)

* [NIFI-13197] - Clicking outside of a dialog no longer closes the dialog. Also, escape will only close the dialog if the underlying form is not dirty.

* add license header

* allow ESC to close even if the for is dirty

This closes #8811
This commit is contained in:
Rob Fellows 2024-05-14 18:24:33 -04:00 committed by GitHub
parent 2d112871db
commit 63a12b06b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
86 changed files with 485 additions and 132 deletions

View File

@ -37,5 +37,10 @@
"targetName": "test"
}
}
]
],
"generators": {
"@nx/angular:component": {
"style": "scss"
}
}
}

View File

@ -19,17 +19,21 @@ import { MatDialogConfig } from '@angular/material/dialog';
export const SMALL_DIALOG: MatDialogConfig = {
maxWidth: '24rem',
minWidth: 320
minWidth: 320,
disableClose: true
};
export const MEDIUM_DIALOG: MatDialogConfig = {
maxWidth: 470,
minWidth: 470
minWidth: 470,
disableClose: true
};
export const LARGE_DIALOG: MatDialogConfig = {
maxWidth: 760,
minWidth: 760
minWidth: 760,
disableClose: true
};
export const XL_DIALOG: MatDialogConfig = {
maxWidth: 1024,
minWidth: 1024
minWidth: 1024,
disableClose: true
};

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddTenantToPolicyDialog } from './add-tenant-to-policy-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AddTenantToPolicyDialogRequest } from '../../../state/access-policy';
@ -69,7 +69,10 @@ describe('AddTenantToPolicyDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AddTenantToPolicyDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(AddTenantToPolicyDialog);
component = fixture.componentInstance;

View File

@ -31,6 +31,7 @@ import { TenantEntity, UserEntity, UserGroupEntity } from '../../../../../state/
import { AddTenantsToPolicyRequest, AddTenantToPolicyDialogRequest } from '../../../state/access-policy';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'add-tenant-to-policy-dialog',
@ -51,7 +52,7 @@ import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spin
templateUrl: './add-tenant-to-policy-dialog.component.html',
styleUrls: ['./add-tenant-to-policy-dialog.component.scss']
})
export class AddTenantToPolicyDialog {
export class AddTenantToPolicyDialog extends CloseOnEscapeDialog {
@Input() set users$(users$: Observable<UserEntity[]>) {
users$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((users: UserEntity[]) => {
const policy: AccessPolicy = this.request.accessPolicy.component;
@ -99,6 +100,7 @@ export class AddTenantToPolicyDialog {
@Inject(MAT_DIALOG_DATA) private request: AddTenantToPolicyDialogRequest,
private formBuilder: FormBuilder
) {
super();
this.addTenantsForm = this.formBuilder.group({
users: new FormControl([]),
userGroups: new FormControl([])
@ -148,4 +150,8 @@ export class AddTenantToPolicyDialog {
userGroups
});
}
override isDirty(): boolean {
return this.addTenantsForm.dirty;
}
}

View File

@ -19,6 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverridePolicyDialog } from './override-policy-dialog.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatDialogRef } from '@angular/material/dialog';
describe('OverridePolicyDialog', () => {
let component: OverridePolicyDialog;
@ -26,7 +27,8 @@ describe('OverridePolicyDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [OverridePolicyDialog, NoopAnimationsModule]
imports: [OverridePolicyDialog, NoopAnimationsModule],
providers: [{ provide: MatDialogRef, useValue: null }]
});
fixture = TestBed.createComponent(OverridePolicyDialog);
component = fixture.componentInstance;

View File

@ -20,6 +20,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatRadioModule } from '@angular/material/radio';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'override-policy-dialog',
@ -28,12 +29,13 @@ import { MatRadioModule } from '@angular/material/radio';
templateUrl: './override-policy-dialog.component.html',
styleUrls: ['./override-policy-dialog.component.scss']
})
export class OverridePolicyDialog {
export class OverridePolicyDialog extends CloseOnEscapeDialog {
@Output() copyInheritedPolicy: EventEmitter<boolean> = new EventEmitter<boolean>();
overridePolicyForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
super();
this.overridePolicyForm = this.formBuilder.group({
override: new FormControl('copy')
});

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ClusterNodeDetailDialog } from './cluster-node-detail-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ClusterNode } from '../../../state/cluster-listing';
describe('ClusterNodeDetailDialog', () => {
@ -64,7 +64,8 @@ describe('ClusterNodeDetailDialog', () => {
useValue: {
request: data
}
}
},
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();

View File

@ -19,6 +19,7 @@ import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MatButton } from '@angular/material/button';
import { ClusterNode } from '../../../state/cluster-listing';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'cluster-node-detail-dialog',
@ -27,10 +28,11 @@ import { ClusterNode } from '../../../state/cluster-listing';
templateUrl: './cluster-node-detail-dialog.component.html',
styleUrl: './cluster-node-detail-dialog.component.scss'
})
export class ClusterNodeDetailDialog {
export class ClusterNodeDetailDialog extends CloseOnEscapeDialog {
node: ClusterNode;
constructor(@Inject(MAT_DIALOG_DATA) public request: ClusterNode) {
super();
this.node = request;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActionDetails } from './action-details.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ActionEntity } from '../../../state/flow-configuration-history-listing';
describe('ActionDetails', () => {
@ -43,7 +43,10 @@ describe('ActionDetails', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ActionDetails],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(ActionDetails);
component = fixture.componentInstance;

View File

@ -30,6 +30,7 @@ import {
} from '../../../state/flow-configuration-history-listing';
import { PipesModule } from '../../../../../pipes/pipes.module';
import { MatButtonModule } from '@angular/material/button';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'action-details',
@ -38,8 +39,10 @@ import { MatButtonModule } from '@angular/material/button';
templateUrl: './action-details.component.html',
styleUrls: ['./action-details.component.scss']
})
export class ActionDetails {
constructor(@Inject(MAT_DIALOG_DATA) public actionEntity: ActionEntity) {}
export class ActionDetails extends CloseOnEscapeDialog {
constructor(@Inject(MAT_DIALOG_DATA) public actionEntity: ActionEntity) {
super();
}
isRemoteProcessGroup(action: Action): boolean {
return action.sourceType === 'RemoteProcessGroup';

View File

@ -22,6 +22,7 @@ import { provideMockStore } from '@ngrx/store/testing';
import { initialHistoryState } from '../../../state/flow-configuration-history-listing/flow-configuration-history-listing.reducer';
import { MatNativeDateModule } from '@angular/material/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatDialogRef } from '@angular/material/dialog';
describe('PurgeHistory', () => {
let component: PurgeHistory;
@ -30,7 +31,13 @@ describe('PurgeHistory', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [PurgeHistory, MatNativeDateModule, NoopAnimationsModule],
providers: [provideMockStore({ initialState: initialHistoryState })]
providers: [
provideMockStore({ initialState: initialHistoryState }),
{
provide: MatDialogRef,
useValue: null
}
]
});
fixture = TestBed.createComponent(PurgeHistory);
component = fixture.componentInstance;

View File

@ -29,6 +29,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { selectAbout } from '../../../../../state/about/about.selectors';
import { Store } from '@ngrx/store';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'purge-history',
@ -37,7 +38,7 @@ import { Store } from '@ngrx/store';
templateUrl: './purge-history.component.html',
styleUrls: ['./purge-history.component.scss']
})
export class PurgeHistory {
export class PurgeHistory extends CloseOnEscapeDialog {
private static readonly DEFAULT_PURGE_TIME: string = '00:00:00';
private static readonly TIME_REGEX = /^([0-1]\d|2[0-3]):([0-5]\d):([0-5]\d)$/;
purgeHistoryForm: FormGroup;
@ -50,6 +51,7 @@ export class PurgeHistory {
private nifiCommon: NiFiCommon,
private store: Store<FlowConfigurationHistoryListingState>
) {
super();
const now: Date = new Date();
const aMonthAgo: Date = new Date();
aMonthAgo.setMonth(now.getMonth() - 1);

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateConnection } from './create-connection.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
import { CreateConnectionDialogRequest } from '../../../../../state/flow';
@ -376,7 +376,8 @@ describe('CreateConnection', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateConnection);

View File

@ -58,6 +58,7 @@ import { BreadcrumbEntity } from '../../../../../state/shared';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-connection',
@ -92,7 +93,7 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
templateUrl: './create-connection.component.html',
styleUrls: ['./create-connection.component.scss']
})
export class CreateConnection {
export class CreateConnection extends CloseOnEscapeDialog {
@Input() set getChildOutputPorts(getChildOutputPorts: (groupId: string) => Observable<any>) {
if (this.source.componentType == ComponentType.ProcessGroup) {
this.childOutputPorts$ = getChildOutputPorts(this.source.id).pipe(
@ -144,6 +145,7 @@ export class CreateConnection {
private clusterConnectionService: ClusterConnectionService,
private client: Client
) {
super();
this.source = dialogRequest.request.source;
this.destination = dialogRequest.request.destination;
@ -304,4 +306,8 @@ export class CreateConnection {
})
);
}
override isDirty(): boolean {
return this.createConnectionForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditConnectionComponent } from './edit-connection.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { EditConnectionDialogRequest } from '../../../../../state/flow';
import { ComponentType } from '../../../../../../../state/shared';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
@ -130,7 +130,14 @@ describe('EditConnectionComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditConnectionComponent, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
});
store = TestBed.inject(MockStore);

View File

@ -56,6 +56,7 @@ import { SourceRemoteProcessGroup } from '../source/source-remote-process-group/
import { DestinationRemoteProcessGroup } from '../destination/destination-remote-process-group/destination-remote-process-group.component';
import { BreadcrumbEntity } from '../../../../../state/shared';
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-connection',
@ -90,7 +91,7 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
templateUrl: './edit-connection.component.html',
styleUrls: ['./edit-connection.component.scss']
})
export class EditConnectionComponent {
export class EditConnectionComponent extends CloseOnEscapeDialog {
@Input() set getChildOutputPorts(getChildOutputPorts: (groupId: string) => Observable<any>) {
if (this.sourceType == ComponentType.ProcessGroup) {
this.childOutputPorts$ = getChildOutputPorts(this.source.groupId);
@ -232,6 +233,7 @@ export class EditConnectionComponent {
private canvasUtils: CanvasUtils,
private client: Client
) {
super();
const connection: any = dialogRequest.entity.component;
this.connectionReadonly = !dialogRequest.entity.permissions.canWrite;
@ -425,4 +427,8 @@ export class EditConnectionComponent {
protected readonly loadBalanceStrategies = loadBalanceStrategies;
protected readonly loadBalanceCompressionStrategies = loadBalanceCompressionStrategies;
override isDirty(): boolean {
return this.editConnectionForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeVersionDialog } from './change-version-dialog';
import { ChangeVersionDialogRequest } from '../../../../../state/flow';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@ -86,7 +86,8 @@ describe('ChangeVersionDialog', () => {
value: 0
}
]
})
}),
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();

View File

@ -27,6 +27,7 @@ import { NiFiCommon } from '../../../../../../../service/nifi-common.service';
import { Store } from '@ngrx/store';
import { CanvasState } from '../../../../../state';
import { selectTimeOffset } from '../../../../../../../state/flow-configuration/flow-configuration.selectors';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'change-version-dialog',
@ -35,7 +36,7 @@ import { selectTimeOffset } from '../../../../../../../state/flow-configuration/
templateUrl: './change-version-dialog.html',
styleUrl: './change-version-dialog.scss'
})
export class ChangeVersionDialog {
export class ChangeVersionDialog extends CloseOnEscapeDialog {
displayedColumns: string[] = ['version', 'created', 'comments'];
dataSource: MatTableDataSource<VersionedFlowSnapshotMetadata> =
new MatTableDataSource<VersionedFlowSnapshotMetadata>();
@ -55,6 +56,7 @@ export class ChangeVersionDialog {
private nifiCommon: NiFiCommon,
private store: Store<CanvasState>
) {
super();
const flowVersions = dialogRequest.versions.map((entity) => entity.versionedFlowSnapshotMetadata);
const sortedFlowVersions = this.sortVersions(flowVersions, this.sort);
this.selectedFlowVersion = sortedFlowVersions[0];

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ImportFromRegistry } from './import-from-registry.component';
import { ImportFromRegistryDialogRequest } from '../../../../../state/flow';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
@ -121,7 +121,8 @@ describe('ImportFromRegistry', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(ImportFromRegistry);

View File

@ -54,6 +54,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Client } from '../../../../../../../service/client.service';
import { importFromRegistry } from '../../../../../state/flow/flow.actions';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'import-from-registry',
@ -82,7 +83,7 @@ import { ClusterConnectionService } from '../../../../../../../service/cluster-c
templateUrl: './import-from-registry.component.html',
styleUrls: ['./import-from-registry.component.scss']
})
export class ImportFromRegistry implements OnInit {
export class ImportFromRegistry extends CloseOnEscapeDialog implements OnInit {
@Input() getBranches: (registryId: string) => Observable<BranchEntity[]> = () => of([]);
@Input() getBuckets!: (registryId: string, branch?: string) => Observable<BucketEntity[]>;
@Input() getFlows!: (registryId: string, bucketId: string, branch?: string) => Observable<VersionedFlowEntity[]>;
@ -126,6 +127,7 @@ export class ImportFromRegistry implements OnInit {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.store
.select(selectTimeOffset)
.pipe(isDefinedAndNotNull(), takeUntilDestroyed())
@ -410,4 +412,8 @@ export class ImportFromRegistry implements OnInit {
);
}
}
override isDirty(): boolean {
return this.importFromRegistryForm.dirty;
}
}

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LocalChangesDialog } from './local-changes-dialog';
import { LocalChangesDialogRequest } from '../../../../../state/flow';
import { ComponentType } from '../../../../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('LocalChangesDialog', () => {
@ -87,7 +87,8 @@ describe('LocalChangesDialog', () => {
{
provide: MAT_DIALOG_DATA,
useValue: request
}
},
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();

View File

@ -29,6 +29,7 @@ import { MatInput } from '@angular/material/input';
import { MatOption } from '@angular/material/autocomplete';
import { ReactiveFormsModule } from '@angular/forms';
import { LocalChangesTable } from './local-changes-table/local-changes-table';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'local-changes-dialog',
@ -46,7 +47,7 @@ import { LocalChangesTable } from './local-changes-table/local-changes-table';
templateUrl: './local-changes-dialog.html',
styleUrl: './local-changes-dialog.scss'
})
export class LocalChangesDialog {
export class LocalChangesDialog extends CloseOnEscapeDialog {
mode: 'SHOW' | 'REVERT' = 'SHOW';
versionControlInformation: VersionControlInformationEntity;
localModifications: FlowComparisonEntity;
@ -58,6 +59,7 @@ export class LocalChangesDialog {
@Output() goToChange: EventEmitter<NavigateToComponentRequest> = new EventEmitter<NavigateToComponentRequest>();
constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: LocalChangesDialogRequest) {
super();
this.mode = dialogRequest.mode;
this.versionControlInformation = dialogRequest.versionControlInformation;
this.localModifications = dialogRequest.localModifications;

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SaveVersionDialog } from './save-version-dialog.component';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { SaveVersionDialogRequest } from '../../../../../state/flow';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
@ -113,7 +113,14 @@ describe('SaveVersionDialog', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SaveVersionDialog, MatDialogModule, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();
fixture = TestBed.createComponent(SaveVersionDialog);

View File

@ -37,6 +37,7 @@ import { TextTip } from '../../../../../../../ui/common/tooltips/text-tip/text-t
import { NifiTooltipDirective } from '../../../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { NgForOf, NgIf } from '@angular/common';
import { MatInput } from '@angular/material/input';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'save-version-dialog',
@ -63,7 +64,7 @@ import { MatInput } from '@angular/material/input';
templateUrl: './save-version-dialog.component.html',
styleUrl: './save-version-dialog.component.scss'
})
export class SaveVersionDialog implements OnInit {
export class SaveVersionDialog extends CloseOnEscapeDialog implements OnInit {
@Input() getBranches: (registryId: string) => Observable<BranchEntity[]> = () => of([]);
@Input() getBuckets: (registryId: string, branch?: string) => Observable<BucketEntity[]> = () => of([]);
@Input({ required: true }) saving!: Signal<boolean>;
@ -85,6 +86,7 @@ export class SaveVersionDialog implements OnInit {
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon
) {
super();
this.versionControlInformation = dialogRequest.versionControlInformation;
this.forceCommit = !!dialogRequest.forceCommit;
@ -234,4 +236,8 @@ export class SaveVersionDialog implements OnInit {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.saveVersionForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditProcessGroup } from './edit-process-group.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { provideMockStore } from '@ngrx/store/testing';
@ -118,7 +118,8 @@ describe('EditProcessGroup', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditProcessGroup);

View File

@ -35,6 +35,7 @@ import { ControllerServiceTable } from '../../../../../../../ui/common/controlle
import { EditComponentDialogRequest } from '../../../../../state/flow';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-process-group',
@ -58,7 +59,7 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
],
styleUrls: ['./edit-process-group.component.scss']
})
export class EditProcessGroup {
export class EditProcessGroup extends CloseOnEscapeDialog {
@Input() set parameterContexts(parameterContexts: ParameterContextEntity[]) {
parameterContexts.forEach((parameterContext) => {
if (parameterContext.permissions.canRead) {
@ -155,6 +156,7 @@ export class EditProcessGroup {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.readonly = !request.entity.permissions.canWrite;
this.parameterContextsOptions.push({
@ -220,4 +222,8 @@ export class EditProcessGroup {
this.editProcessGroup.next(payload);
}
override isDirty(): boolean {
return this.editProcessGroupForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditProcessor } from './edit-processor.component';
import { EditComponentDialogRequest } from '../../../../../state/flow';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { ComponentType } from '../../../../../../../state/shared';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
@ -745,7 +745,8 @@ describe('EditProcessor', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditProcessor);

View File

@ -49,6 +49,7 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
import { ConvertToParameterResponse } from '../../../../../service/parameter-helper.service';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-processor',
@ -73,7 +74,7 @@ import { ConvertToParameterResponse } from '../../../../../service/parameter-hel
],
styleUrls: ['./edit-processor.component.scss']
})
export class EditProcessor {
export class EditProcessor extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() parameterContext: ParameterContextEntity | undefined;
@ -156,6 +157,7 @@ export class EditProcessor {
private clusterConnectionService: ClusterConnectionService,
private nifiCommon: NiFiCommon
) {
super();
this.readonly =
!request.entity.permissions.canWrite || !this.canvasUtils.runnableSupportsModification(request.entity);
@ -342,4 +344,8 @@ export class EditProcessor {
payload
});
}
override isDirty(): boolean {
return this.editProcessorForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditRemoteProcessGroup } from './edit-remote-process-group.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing';
@ -72,7 +72,14 @@ describe('EditRemoteProcessGroup', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditRemoteProcessGroup, BrowserAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditRemoteProcessGroup);
component = fixture.componentInstance;

View File

@ -32,6 +32,7 @@ import { EditComponentDialogRequest } from '../../../../../state/flow';
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
import { NifiTooltipDirective } from '../../../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../../../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
standalone: true,
@ -52,7 +53,7 @@ import { NifiTooltipDirective } from '../../../../../../../ui/common/tooltips/ni
],
styleUrls: ['./edit-remote-process-group.component.scss']
})
export class EditRemoteProcessGroup {
export class EditRemoteProcessGroup extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() editRemoteProcessGroup: EventEmitter<any> = new EventEmitter<any>();
@ -67,6 +68,7 @@ export class EditRemoteProcessGroup {
private canvasUtils: CanvasUtils,
private client: Client
) {
super();
this.readonly =
!request.entity.permissions.canWrite ||
!this.canvasUtils.remoteProcessGroupSupportsModification(request.entity);
@ -103,4 +105,8 @@ export class EditRemoteProcessGroup {
this.editRemoteProcessGroup.next(payload);
}
override isDirty(): boolean {
return this.editRemoteProcessGroupForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditRemotePortComponent } from './edit-remote-port.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditComponentDialogRequest } from '../../../state/flow';
@ -59,7 +59,8 @@ describe('EditRemotePortComponent', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditRemotePortComponent);

View File

@ -34,6 +34,8 @@ import { configureRemotePort } from '../../../state/manage-remote-ports/manage-r
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
import { CanvasState } from '../../../state';
@Component({
standalone: true,
@ -51,7 +53,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
],
styleUrls: ['./edit-remote-port.component.scss']
})
export class EditRemotePortComponent {
export class EditRemotePortComponent extends CloseOnEscapeDialog {
saving$ = this.store.select(selectSaving);
editPortForm: FormGroup;
@ -64,6 +66,7 @@ export class EditRemotePortComponent {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
// set the port type name
if (ComponentType.InputPort == this.request.type) {
this.portTypeLabel = 'Input Port';
@ -111,4 +114,8 @@ export class EditRemotePortComponent {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editPortForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditParameterContext } from './edit-parameter-context.component';
import { EditParameterContextRequest } from '../../../state/parameter-context-listing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { of } from 'rxjs';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
@ -247,7 +247,8 @@ describe('EditParameterContext', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditParameterContext);

View File

@ -45,6 +45,7 @@ import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-parameter-context',
@ -72,7 +73,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
],
styleUrls: ['./edit-parameter-context.component.scss']
})
export class EditParameterContext {
export class EditParameterContext extends CloseOnEscapeDialog {
@Input() createNewParameter!: (existingParameters: string[]) => Observable<Parameter>;
@Input() editParameter!: (parameter: Parameter) => Observable<Parameter>;
@Input() updateRequest!: Observable<ParameterContextUpdateRequestEntity | null>;
@ -96,6 +97,7 @@ export class EditParameterContext {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
if (request.parameterContext) {
this.isNew = false;
this.readonly = !request.parameterContext.permissions.canWrite;
@ -187,4 +189,8 @@ export class EditParameterContext {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editParameterContextForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProvenanceSearchDialog } from './provenance-search-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatNativeDateModule } from '@angular/material/core';
import { ProvenanceSearchDialogRequest } from '../../../state/provenance-event-listing';
@ -74,7 +74,10 @@ describe('ProvenanceSearchDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProvenanceSearchDialog, NoopAnimationsModule, MatNativeDateModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(ProvenanceSearchDialog);
component = fixture.componentInstance;

View File

@ -34,6 +34,7 @@ import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.com
import { MatOption } from '@angular/material/autocomplete';
import { MatSelect } from '@angular/material/select';
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'provenance-search-dialog',
@ -53,7 +54,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
],
styleUrls: ['./provenance-search-dialog.component.scss']
})
export class ProvenanceSearchDialog {
export class ProvenanceSearchDialog extends CloseOnEscapeDialog {
@Input() timezone!: string;
@Output() submitSearchCriteria: EventEmitter<ProvenanceRequest> = new EventEmitter<ProvenanceRequest>();
@ -71,6 +72,7 @@ export class ProvenanceSearchDialog {
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon
) {
super();
const now = new Date();
this.clearTime(now);
@ -266,4 +268,8 @@ export class ProvenanceSearchDialog {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.provenanceOptionsForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FlowFileDialog } from './flowfile-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { FlowFileDialogRequest } from '../../../state/queue-listing';
@ -47,7 +47,10 @@ describe('FlowFileDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FlowFileDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(FlowFileDialog);
component = fixture.componentInstance;

View File

@ -26,6 +26,7 @@ import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatTabsModule } from '@angular/material/tabs';
import { FlowFileDialogRequest } from '../../../state/queue-listing';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'flowfile-dialog',
@ -48,7 +49,7 @@ import { NiFiCommon } from '../../../../../service/nifi-common.service';
KeyValuePipe
]
})
export class FlowFileDialog {
export class FlowFileDialog extends CloseOnEscapeDialog {
@Input() contentViewerAvailable!: boolean;
@Output() downloadContent: EventEmitter<void> = new EventEmitter<void>();
@ -57,7 +58,9 @@ export class FlowFileDialog {
constructor(
@Inject(MAT_DIALOG_DATA) public request: FlowFileDialogRequest,
private nifiCommon: NiFiCommon
) {}
) {
super();
}
formatDurationValue(duration: number): string {
if (duration === 0) {

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateFlowAnalysisRule } from './create-flow-analysis-rule.component';
import { CreateFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { initialState } from '../../../state/flow-analysis-rules/flow-analysis-rules.reducer';
@ -48,7 +48,14 @@ describe('CreateFlowAnalysisRule', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CreateFlowAnalysisRule, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateFlowAnalysisRule);
component = fixture.componentInstance;

View File

@ -25,6 +25,7 @@ import { Client } from '../../../../../service/client.service';
import { DocumentedType } from '../../../../../state/shared';
import { selectSaving } from '../../../state/flow-analysis-rules/flow-analysis-rules.selectors';
import { AsyncPipe } from '@angular/common';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-flow-analysis-rule',
@ -33,7 +34,7 @@ import { AsyncPipe } from '@angular/common';
templateUrl: './create-flow-analysis-rule.component.html',
styleUrls: ['./create-flow-analysis-rule.component.scss']
})
export class CreateFlowAnalysisRule {
export class CreateFlowAnalysisRule extends CloseOnEscapeDialog {
flowAnalysisRules: DocumentedType[];
saving$ = this.store.select(selectSaving);
@ -42,6 +43,7 @@ export class CreateFlowAnalysisRule {
private store: Store<FlowAnalysisRulesState>,
private client: Client
) {
super();
this.flowAnalysisRules = dialogRequest.flowAnalysisRuleTypes;
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditFlowAnalysisRule } from './edit-flow-analysis-rule.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules';
import { Component } from '@angular/core';
@ -115,7 +115,8 @@ describe('EditFlowAnalysisRule', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditFlowAnalysisRule);

View File

@ -46,6 +46,7 @@ import {
import { FlowAnalysisRuleTable } from '../flow-analysis-rule-table/flow-analysis-rule-table.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-flow-analysis-rule',
@ -69,7 +70,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
],
styleUrls: ['./edit-flow-analysis-rule.component.scss']
})
export class EditFlowAnalysisRule {
export class EditFlowAnalysisRule extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() saving$!: Observable<boolean>;
@ -95,6 +96,7 @@ export class EditFlowAnalysisRule {
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.readonly =
!request.flowAnalysisRule.permissions.canWrite || request.flowAnalysisRule.status.runStatus !== 'DISABLED';
@ -157,4 +159,8 @@ export class EditFlowAnalysisRule {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editFlowAnalysisRuleForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateParameterProvider } from './create-parameter-provider.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialParameterProvidersState } from '../../../state/parameter-providers/parameter-providers.reducer';
import { CreateParameterProviderDialogRequest } from '../../../state/parameter-providers';
@ -64,7 +64,8 @@ describe('CreateParameterProvider', () => {
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState: initialParameterProvidersState })
provideMockStore({ initialState: initialParameterProvidersState }),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateParameterProvider);

View File

@ -22,6 +22,7 @@ import { DocumentedType } from '../../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { CreateParameterProviderDialogRequest } from '../../../state/parameter-providers';
import { ExtensionCreation } from '../../../../../ui/common/extension-creation/extension-creation.component';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-parameter-provider',
@ -30,13 +31,14 @@ import { ExtensionCreation } from '../../../../../ui/common/extension-creation/e
templateUrl: './create-parameter-provider.component.html',
styleUrls: ['./create-parameter-provider.component.scss']
})
export class CreateParameterProvider {
export class CreateParameterProvider extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() createParameterProvider: EventEmitter<DocumentedType> = new EventEmitter<DocumentedType>();
parameterProviderTypes: DocumentedType[];
constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: CreateParameterProviderDialogRequest) {
super();
this.parameterProviderTypes = dialogRequest.parameterProviderTypes;
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditParameterProvider } from './edit-parameter-provider.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { EditParameterProviderRequest } from '../../../state/parameter-providers';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
@ -162,7 +162,8 @@ describe('EditParameterProvider', () => {
},
provideMockStore({
initialState: initialParameterProvidersState
})
}),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditParameterProvider);

View File

@ -46,6 +46,7 @@ import { CommonModule } from '@angular/common';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-parameter-provider',
@ -68,7 +69,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
templateUrl: './edit-parameter-provider.component.html',
styleUrls: ['./edit-parameter-provider.component.scss']
})
export class EditParameterProvider {
export class EditParameterProvider extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() goToService!: (serviceId: string) => void;
@ -88,6 +89,7 @@ export class EditParameterProvider {
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.readonly = !request.parameterProvider.permissions.canWrite;
const providerProperties = request.parameterProvider.component.properties;
@ -151,4 +153,8 @@ export class EditParameterProvider {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editParameterProviderForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FetchParameterProviderParameters } from './fetch-parameter-provider-parameters.component';
import { FetchParameterProviderDialogRequest } from '../../../state/parameter-providers';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialParameterProvidersState } from '../../../state/parameter-providers/parameter-providers.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@ -170,7 +170,8 @@ describe('FetchParameterProviderParameters', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(FetchParameterProviderParameters);

View File

@ -49,6 +49,7 @@ import { Store } from '@ngrx/store';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PipesModule } from '../../../../../pipes/pipes.module';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'fetch-parameter-provider-parameters',
@ -72,7 +73,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
templateUrl: './fetch-parameter-provider-parameters.component.html',
styleUrls: ['./fetch-parameter-provider-parameters.component.scss']
})
export class FetchParameterProviderParameters implements OnInit {
export class FetchParameterProviderParameters extends CloseOnEscapeDialog implements OnInit {
fetchParametersForm: FormGroup;
parameterProvider: ParameterProviderEntity;
selectedParameterGroup: ParameterGroupConfiguration | null = null;
@ -112,6 +113,7 @@ export class FetchParameterProviderParameters implements OnInit {
private store: Store<ParameterProvidersState>,
@Inject(MAT_DIALOG_DATA) public request: FetchParameterProviderDialogRequest
) {
super();
this.parameterProvider = request.parameterProvider;
this.fetchParametersForm = this.formBuilder.group({});
@ -600,4 +602,8 @@ export class FetchParameterProviderParameters implements OnInit {
parameterGroupConfigurations: groupConfigs
};
}
override isDirty(): boolean {
return this.fetchParametersForm.dirty;
}
}

View File

@ -17,7 +17,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CreateRegistryClient } from './create-registry-client.component';
import { CreateRegistryClientDialogRequest } from '../../../state/registry-clients';
@ -52,7 +52,8 @@ describe('CreateRegistryClient', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateRegistryClient);

View File

@ -32,6 +32,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-registry-client',
@ -50,7 +51,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
],
styleUrls: ['./create-registry-client.component.scss']
})
export class CreateRegistryClient {
export class CreateRegistryClient extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() createRegistryClient: EventEmitter<CreateRegistryClientRequest> =
new EventEmitter<CreateRegistryClientRequest>();
@ -66,6 +67,7 @@ export class CreateRegistryClient {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
let type: string | null = null;
if (request.registryClientTypes.length > 0) {
type = request.registryClientTypes[0].type;
@ -99,4 +101,8 @@ export class CreateRegistryClient {
this.createRegistryClient.next(request);
}
override isDirty(): boolean {
return this.createRegistryClientForm.dirty;
}
}

View File

@ -17,7 +17,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditRegistryClient } from './edit-registry-client.component';
import { EditRegistryClientDialogRequest } from '../../../state/registry-clients';
@ -128,7 +128,8 @@ describe('EditRegistryClient', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditRegistryClient);

View File

@ -40,6 +40,7 @@ import { MatTabsModule } from '@angular/material/tabs';
import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-registry-client',
@ -61,7 +62,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
],
styleUrls: ['./edit-registry-client.component.scss']
})
export class EditRegistryClient {
export class EditRegistryClient extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() goToService!: (serviceId: string) => void;
@ -80,6 +81,7 @@ export class EditRegistryClient {
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
super();
const serviceProperties: any = request.registryClient.component.properties;
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
const [property, value] = entry;
@ -132,4 +134,8 @@ export class EditRegistryClient {
postUpdateNavigation
});
}
override isDirty(): boolean {
return this.editRegistryClientForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateReportingTask } from './create-reporting-task.component';
import { CreateReportingTaskDialogRequest } from '../../../state/reporting-tasks';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/extension-types/extension-types.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@ -47,7 +47,14 @@ describe('CreateReportingTask', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CreateReportingTask, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateReportingTask);
component = fixture.componentInstance;

View File

@ -25,6 +25,7 @@ import { Client } from '../../../../../service/client.service';
import { DocumentedType } from '../../../../../state/shared';
import { selectSaving } from '../../../state/reporting-tasks/reporting-tasks.selectors';
import { AsyncPipe } from '@angular/common';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-reporting-task',
@ -33,7 +34,7 @@ import { AsyncPipe } from '@angular/common';
templateUrl: './create-reporting-task.component.html',
styleUrls: ['./create-reporting-task.component.scss']
})
export class CreateReportingTask {
export class CreateReportingTask extends CloseOnEscapeDialog {
reportingTasks: DocumentedType[];
saving$ = this.store.select(selectSaving);
@ -42,6 +43,7 @@ export class CreateReportingTask {
private store: Store<ReportingTasksState>,
private client: Client
) {
super();
this.reportingTasks = dialogRequest.reportingTaskTypes;
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditReportingTask } from './edit-reporting-task.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditReportingTaskDialogRequest } from '../../../state/reporting-tasks';
import { Component } from '@angular/core';
@ -408,7 +408,8 @@ describe('EditReportingTask', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditReportingTask);

View File

@ -48,6 +48,7 @@ import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-too
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-reporting-task',
@ -72,7 +73,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
],
styleUrls: ['./edit-reporting-task.component.scss']
})
export class EditReportingTask {
export class EditReportingTask extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() goToService!: (serviceId: string) => void;
@ -109,6 +110,7 @@ export class EditReportingTask {
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.readonly =
!request.reportingTask.permissions.canWrite ||
(request.reportingTask.status.runStatus !== 'STOPPED' &&
@ -215,4 +217,8 @@ export class EditReportingTask {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editReportingTaskForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ClusterSummaryDialog } from './cluster-summary-dialog.component';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialComponentClusterStatusState } from '../../../state/component-cluster-status/component-cluster-status.reducer';
import { ComponentClusterStatusRequest, ComponentClusterStatusState } from '../../../state/component-cluster-status';
@ -47,7 +47,8 @@ describe('ClusterSummaryDialog', () => {
canRead: true
}
} as ComponentClusterStatusState
})
}),
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();

View File

@ -47,6 +47,7 @@ import { PortClusterTable } from './port-cluster-table/port-cluster-table.compon
import { RemoteProcessGroupClusterTable } from './remote-process-group-cluster-table/remote-process-group-cluster-table.component';
import { ConnectionClusterTable } from './connection-cluster-table/connection-cluster-table.component';
import { ProcessGroupClusterTable } from './process-group-cluster-table/process-group-cluster-table.component';
import { CloseOnEscapeDialog } from '../../../../../ui/common/close-on-escape-dialog/close-on-escape-dialog.component';
interface Helper {
getName: () => string;
@ -74,7 +75,7 @@ interface Helper {
templateUrl: './cluster-summary-dialog.component.html',
styleUrl: './cluster-summary-dialog.component.scss'
})
export class ClusterSummaryDialog {
export class ClusterSummaryDialog extends CloseOnEscapeDialog {
private _componentType: ComponentType = ComponentType.Processor;
loading$: Observable<boolean> = this.store
.select(selectComponentClusterStatusLoadingStatus)
@ -97,6 +98,7 @@ export class ClusterSummaryDialog {
private store: Store<ComponentClusterStatusState>,
@Inject(MAT_DIALOG_DATA) private clusterStatusRequest: ComponentClusterStatusRequest
) {
super();
this.componentId = clusterStatusRequest.id;
this.componentType = clusterStatusRequest.componentType;

View File

@ -21,6 +21,7 @@ import { AboutDialog } from './about-dialog.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/component-state/component-state.reducer';
import { MatDialogRef } from '@angular/material/dialog';
describe('AboutDialog', () => {
let component: AboutDialog;
@ -29,7 +30,7 @@ describe('AboutDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AboutDialog, NoopAnimationsModule],
providers: [provideMockStore({ initialState })]
providers: [provideMockStore({ initialState }), { provide: MatDialogRef, useValue: null }]
});
fixture = TestBed.createComponent(AboutDialog);
component = fixture.componentInstance;

View File

@ -29,6 +29,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { AboutState } from '../../../state/about';
import { selectAbout } from '../../../state/about/about.selectors';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'about',
@ -49,8 +50,10 @@ import { selectAbout } from '../../../state/about/about.selectors';
],
styleUrls: ['./about-dialog.component.scss']
})
export class AboutDialog {
export class AboutDialog extends CloseOnEscapeDialog {
about$ = this.store.select(selectAbout);
constructor(private store: Store<AboutState>) {}
constructor(private store: Store<AboutState>) {
super();
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeComponentVersionDialog } from './change-component-version-dialog';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import { OpenChangeComponentVersionDialogRequest } from '../../../state/shared';
@ -72,7 +72,10 @@ describe('ChangeComponentVersionDialog', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ChangeComponentVersionDialog, MatDialogModule, NoopAnimationsModule, MatFormFieldModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
}).compileComponents();
fixture = TestBed.createComponent(ChangeComponentVersionDialog);

View File

@ -25,6 +25,7 @@ import { TextTip } from '../tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../tooltips/nifi-tooltip.directive';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { ControllerServiceApi } from '../controller-service/controller-service-api/controller-service-api.component';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'change-component-version-dialog',
@ -43,7 +44,7 @@ import { ControllerServiceApi } from '../controller-service/controller-service-a
templateUrl: './change-component-version-dialog.html',
styleUrl: './change-component-version-dialog.scss'
})
export class ChangeComponentVersionDialog {
export class ChangeComponentVersionDialog extends CloseOnEscapeDialog {
versions: DocumentedType[];
selected: DocumentedType | null = null;
changeComponentVersionForm: FormGroup;
@ -56,6 +57,7 @@ export class ChangeComponentVersionDialog {
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon
) {
super();
this.versions = dialogRequest.componentVersions;
this.currentBundle = dialogRequest.fetchRequest.bundle;
const idx = this.versions.findIndex(
@ -82,4 +84,8 @@ export class ChangeComponentVersionDialog {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.changeComponentVersionForm.dirty;
}
}

View File

@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { filter } from 'rxjs';
import { MatDialogRef } from '@angular/material/dialog';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'close-on-escape-dialog',
standalone: true,
imports: [CommonModule],
template: ''
})
export abstract class CloseOnEscapeDialog {
private dialogRef: MatDialogRef<CloseOnEscapeDialog> = inject(MatDialogRef);
protected constructor() {
if (this.dialogRef) {
this.dialogRef
.keydownEvents()
.pipe(
filter((event: KeyboardEvent) => event.key === 'Escape'),
takeUntilDestroyed()
)
.subscribe(() => {
this.dialogRef.close();
});
}
}
isDirty(): boolean {
return false;
}
}

View File

@ -21,6 +21,7 @@ import { ComponentStateDialog } from './component-state.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/component-state/component-state.reducer';
import { MatDialogRef } from '@angular/material/dialog';
describe('ComponentStateDialog', () => {
let component: ComponentStateDialog;
@ -29,7 +30,7 @@ describe('ComponentStateDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ComponentStateDialog, NoopAnimationsModule],
providers: [provideMockStore({ initialState })]
providers: [provideMockStore({ initialState }), { provide: MatDialogRef, useValue: null }]
});
fixture = TestBed.createComponent(ComponentStateDialog);
component = fixture.componentInstance;

View File

@ -41,6 +41,7 @@ import { MatInputModule } from '@angular/material/input';
import { selectClusterSummary } from '../../../state/cluster-summary/cluster-summary.selectors';
import { ErrorBanner } from '../error-banner/error-banner.component';
import { clearBannerErrors } from '../../../state/error/error.actions';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'component-state',
@ -61,7 +62,7 @@ import { clearBannerErrors } from '../../../state/error/error.actions';
],
styleUrls: ['./component-state.component.scss']
})
export class ComponentStateDialog implements AfterViewInit, OnDestroy {
export class ComponentStateDialog extends CloseOnEscapeDialog implements AfterViewInit, OnDestroy {
@Input() initialSortColumn: 'key' | 'value' = 'key';
@Input() initialSortDirection: 'asc' | 'desc' = 'asc';
@ -84,6 +85,7 @@ export class ComponentStateDialog implements AfterViewInit, OnDestroy {
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon
) {
super();
this.filterForm = this.formBuilder.group({ filterTerm: '' });
this.store

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateControllerService } from './create-controller-service.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/extension-types/extension-types.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@ -57,7 +57,14 @@ describe('CreateControllerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CreateControllerService, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{
provide: MAT_DIALOG_DATA,
useValue: data
},
provideMockStore({ initialState }),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(CreateControllerService);
component = fixture.componentInstance;

View File

@ -21,6 +21,7 @@ import { CreateControllerServiceDialogRequest, DocumentedType } from '../../../.
import { ExtensionCreation } from '../../extension-creation/extension-creation.component';
import { Observable } from 'rxjs';
import { AsyncPipe } from '@angular/common';
import { CloseOnEscapeDialog } from '../../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'create-controller-service',
@ -29,13 +30,14 @@ import { AsyncPipe } from '@angular/common';
templateUrl: './create-controller-service.component.html',
styleUrls: ['./create-controller-service.component.scss']
})
export class CreateControllerService {
export class CreateControllerService extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() createControllerService: EventEmitter<DocumentedType> = new EventEmitter<DocumentedType>();
controllerServiceTypes: DocumentedType[];
constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: CreateControllerServiceDialogRequest) {
super();
this.controllerServiceTypes = dialogRequest.controllerServiceTypes;
}

View File

@ -22,7 +22,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer';
import { ComponentType, SetEnableControllerServiceDialogRequest } from '../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
describe('EnableControllerService', () => {
let component: DisableControllerService;
@ -342,7 +342,14 @@ describe('EnableControllerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [DisableControllerService, NoopAnimationsModule],
providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
provideMockStore({ initialState }),
{
provide: MAT_DIALOG_DATA,
useValue: data
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(DisableControllerService);
component = fixture.componentInstance;

View File

@ -45,6 +45,7 @@ import {
selectControllerService,
selectControllerServiceSetEnableRequest
} from '../../../../state/contoller-service-state/controller-service-state.selectors';
import { CloseOnEscapeDialog } from '../../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'disable-controller-service',
@ -67,7 +68,7 @@ import {
],
styleUrls: ['./disable-controller-service.component.scss']
})
export class DisableControllerService implements OnDestroy {
export class DisableControllerService extends CloseOnEscapeDialog implements OnDestroy {
@Input() goToReferencingComponent!: (component: ControllerServiceReferencingComponent) => void;
protected readonly TextTip = TextTip;
@ -85,6 +86,7 @@ export class DisableControllerService implements OnDestroy {
@Inject(MAT_DIALOG_DATA) public request: SetEnableControllerServiceDialogRequest,
private store: Store<ControllerServiceState>
) {
super();
this.store.dispatch(
setControllerService({
request: {

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditControllerService } from './edit-controller-service.component';
import { EditControllerServiceDialogRequest } from '../../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
@ -567,7 +567,8 @@ describe('EditControllerService', () => {
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditControllerService);

View File

@ -47,6 +47,7 @@ import { ClusterConnectionService } from '../../../../service/cluster-connection
import { TextTip } from '../../tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
import { ConvertToParameterResponse } from '../../../../pages/flow-designer/service/parameter-helper.service';
import { CloseOnEscapeDialog } from '../../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-controller-service',
@ -71,7 +72,7 @@ import { ConvertToParameterResponse } from '../../../../pages/flow-designer/serv
],
styleUrls: ['./edit-controller-service.component.scss']
})
export class EditControllerService {
export class EditControllerService extends CloseOnEscapeDialog {
@Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable<Property>;
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
@Input() parameterContext: ParameterContextEntity | undefined;
@ -120,6 +121,7 @@ export class EditControllerService {
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
super();
this.readonly =
!request.controllerService.permissions.canWrite ||
request.controllerService.status.runStatus !== 'DISABLED';
@ -180,4 +182,8 @@ export class EditControllerService {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editControllerServiceForm.dirty;
}
}

View File

@ -21,7 +21,7 @@ import { EnableControllerService } from './enable-controller-service.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/contoller-service-state/controller-service-state.reducer';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { ComponentType, SetEnableControllerServiceDialogRequest } from '../../../../state/shared';
describe('EnableControllerService', () => {
@ -342,7 +342,14 @@ describe('EnableControllerService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EnableControllerService, NoopAnimationsModule, MatDialogModule],
providers: [provideMockStore({ initialState }), { provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
provideMockStore({ initialState }),
{
provide: MAT_DIALOG_DATA,
useValue: data
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EnableControllerService);
component = fixture.componentInstance;

View File

@ -52,6 +52,7 @@ import {
selectControllerService,
selectControllerServiceSetEnableRequest
} from '../../../../state/contoller-service-state/controller-service-state.selectors';
import { CloseOnEscapeDialog } from '../../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'enable-controller-service',
@ -75,7 +76,7 @@ import {
],
styleUrls: ['./enable-controller-service.component.scss']
})
export class EnableControllerService implements OnDestroy {
export class EnableControllerService extends CloseOnEscapeDialog implements OnDestroy {
@Input() goToReferencingComponent!: (component: ControllerServiceReferencingComponent) => void;
protected readonly TextTip = TextTip;
@ -97,6 +98,7 @@ export class EnableControllerService implements OnDestroy {
private store: Store<ControllerServiceState>,
private formBuilder: FormBuilder
) {
super();
// build the form
this.enableControllerServiceForm = this.formBuilder.group({
scope: new FormControl(controllerServiceActionScopes[0].value, Validators.required)
@ -144,4 +146,8 @@ export class EnableControllerService implements OnDestroy {
ngOnDestroy(): void {
this.store.dispatch(resetEnableControllerServiceState());
}
override isDirty(): boolean {
return this.enableControllerServiceForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditParameterDialog } from './edit-parameter-dialog.component';
import { EditParameterRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditParameterDialog', () => {
@ -52,7 +52,10 @@ describe('EditParameterDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditParameterDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditParameterDialog);
component = fixture.componentInstance;

View File

@ -39,6 +39,7 @@ import { AsyncPipe } from '@angular/common';
import { Observable } from 'rxjs';
import { TextTip } from '../tooltips/text-tip/text-tip.component';
import { NifiTooltipDirective } from '../tooltips/nifi-tooltip.directive';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-parameter-dialog',
@ -59,7 +60,7 @@ import { NifiTooltipDirective } from '../tooltips/nifi-tooltip.directive';
templateUrl: './edit-parameter-dialog.component.html',
styleUrls: ['./edit-parameter-dialog.component.scss']
})
export class EditParameterDialog {
export class EditParameterDialog extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() editParameter: EventEmitter<EditParameterResponse> = new EventEmitter<EditParameterResponse>();
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
@ -73,6 +74,7 @@ export class EditParameterDialog {
@Inject(MAT_DIALOG_DATA) public request: EditParameterRequest,
private formBuilder: FormBuilder
) {
super();
// get the optional parameter. when existingParameters are specified this parameter is used to
// seed the form for the new parameter. when existingParameters are not specified, this is the
// existing parameter that populates the form
@ -175,4 +177,8 @@ export class EditParameterDialog {
}
protected readonly TextTip = TextTip;
override isDirty(): boolean {
return this.editParameterForm.dirty;
}
}

View File

@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditTenantDialog } from './edit-tenant-dialog.component';
import { EditTenantRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
@ -799,7 +799,8 @@ describe('EditTenantDialog', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(EditTenantDialog);

View File

@ -41,6 +41,7 @@ import { MatListModule } from '@angular/material/list';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { ErrorBanner } from '../error-banner/error-banner.component';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'edit-tenant-dialog',
@ -62,7 +63,7 @@ import { ErrorBanner } from '../error-banner/error-banner.component';
templateUrl: './edit-tenant-dialog.component.html',
styleUrls: ['./edit-tenant-dialog.component.scss']
})
export class EditTenantDialog {
export class EditTenantDialog extends CloseOnEscapeDialog {
@Input() saving$!: Observable<boolean>;
@Output() editTenant: EventEmitter<EditTenantResponse> = new EventEmitter<EditTenantResponse>();
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
@ -85,6 +86,7 @@ export class EditTenantDialog {
private nifiCommon: NiFiCommon,
private client: Client
) {
super();
const user: UserEntity | undefined = request.user;
const userGroup: UserGroupEntity | undefined = request.userGroup;
@ -253,4 +255,8 @@ export class EditTenantDialog {
});
}
}
override isDirty(): boolean {
return this.editTenantForm.dirty;
}
}

View File

@ -20,7 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NewPropertyDialog } from './new-property-dialog.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NewPropertyDialogRequest } from '../../../state/shared';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('NewPropertyDialog', () => {
@ -35,7 +35,10 @@ describe('NewPropertyDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NewPropertyDialog, NoopAnimationsModule, FormsModule, ReactiveFormsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(NewPropertyDialog);
component = fixture.componentInstance;

View File

@ -33,6 +33,7 @@ import {
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatRadioModule } from '@angular/material/radio';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'new-property-dialog',
@ -49,7 +50,7 @@ import { MatRadioModule } from '@angular/material/radio';
templateUrl: './new-property-dialog.component.html',
styleUrls: ['./new-property-dialog.component.scss']
})
export class NewPropertyDialog {
export class NewPropertyDialog extends CloseOnEscapeDialog {
@Output() newProperty: EventEmitter<NewPropertyDialogResponse> = new EventEmitter<NewPropertyDialogResponse>();
newPropertyForm: FormGroup;
@ -59,6 +60,7 @@ export class NewPropertyDialog {
@Inject(MAT_DIALOG_DATA) public request: NewPropertyDialogRequest,
private formBuilder: FormBuilder
) {
super();
this.name = new FormControl('', [
Validators.required,
this.existingPropertyValidator(request.existingProperties)
@ -99,4 +101,8 @@ export class NewPropertyDialog {
sensitive: this.newPropertyForm.get('sensitive')?.value
});
}
override isDirty(): boolean {
return this.newPropertyForm.dirty;
}
}

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OkDialog } from './ok-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
describe('OkDialog', () => {
let component: OkDialog;
@ -34,7 +34,8 @@ describe('OkDialog', () => {
title: 'Title',
message: 'Message'
}
}
},
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(OkDialog);

View File

@ -19,6 +19,7 @@ import { Component, EventEmitter, Inject, Output } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { OkDialogRequest } from '../../../state/shared';
import { MatButtonModule } from '@angular/material/button';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'ok-dialog',
@ -27,10 +28,12 @@ import { MatButtonModule } from '@angular/material/button';
templateUrl: './ok-dialog.component.html',
styleUrls: ['./ok-dialog.component.scss']
})
export class OkDialog {
export class OkDialog extends CloseOnEscapeDialog {
@Output() ok: EventEmitter<void> = new EventEmitter<void>();
constructor(@Inject(MAT_DIALOG_DATA) public request: OkDialogRequest) {}
constructor(@Inject(MAT_DIALOG_DATA) public request: OkDialogRequest) {
super();
}
okClicked(): void {
this.ok.next();

View File

@ -177,8 +177,7 @@
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
[cdkConnectedOverlayOpen]="editorOpen"
(detach)="closeEditor()"
(backdropClick)="closeEditor()">
(detach)="closeEditor()">
@if (hasAllowableValues(editorItem)) {
<combo-editor
[item]="editorItem"

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProvenanceEventDialog } from './provenance-event-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('ProvenanceEventDialog', () => {
@ -72,7 +72,10 @@ describe('ProvenanceEventDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProvenanceEventDialog, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(ProvenanceEventDialog);
component = fixture.componentInstance;

View File

@ -26,6 +26,7 @@ import { MatDatepickerModule } from '@angular/material/datepicker';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { MatTabsModule } from '@angular/material/tabs';
import { Attribute, ProvenanceEventDialogRequest } from '../../../state/shared';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'provenance-event-dialog',
@ -46,7 +47,7 @@ import { Attribute, ProvenanceEventDialogRequest } from '../../../state/shared';
FormsModule
]
})
export class ProvenanceEventDialog {
export class ProvenanceEventDialog extends CloseOnEscapeDialog {
@Input() contentViewerAvailable!: boolean;
@Output() downloadContent: EventEmitter<string> = new EventEmitter<string>();
@ -58,7 +59,9 @@ export class ProvenanceEventDialog {
constructor(
@Inject(MAT_DIALOG_DATA) public request: ProvenanceEventDialogRequest,
private nifiCommon: NiFiCommon
) {}
) {
super();
}
formatDurationValue(duration: number): string {
if (duration === 0) {

View File

@ -18,7 +18,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { YesNoDialog } from './yes-no-dialog.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { YesNoDialogRequest } from '../../../state/shared';
import { By } from '@angular/platform-browser';
@ -34,7 +34,10 @@ describe('YesNoDialog', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [YesNoDialog],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{ provide: MatDialogRef, useValue: null }
]
});
fixture = TestBed.createComponent(YesNoDialog);
component = fixture.componentInstance;

View File

@ -19,6 +19,7 @@ import { Component, EventEmitter, Inject, Output } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { YesNoDialogRequest } from '../../../state/shared';
import { MatButtonModule } from '@angular/material/button';
import { CloseOnEscapeDialog } from '../close-on-escape-dialog/close-on-escape-dialog.component';
@Component({
selector: 'yes-no-dialog',
@ -27,11 +28,13 @@ import { MatButtonModule } from '@angular/material/button';
templateUrl: './yes-no-dialog.component.html',
styleUrls: ['./yes-no-dialog.component.scss']
})
export class YesNoDialog {
export class YesNoDialog extends CloseOnEscapeDialog {
@Output() yes: EventEmitter<void> = new EventEmitter<void>();
@Output() no: EventEmitter<void> = new EventEmitter<void>();
constructor(@Inject(MAT_DIALOG_DATA) public request: YesNoDialogRequest) {}
constructor(@Inject(MAT_DIALOG_DATA) public request: YesNoDialogRequest) {
super();
}
yesClicked(): void {
this.yes.next();