mirror of https://github.com/apache/nifi.git
NIFI-13136: Allowing users to unset optional property values (#8734)
* NIFI-13136: - Allowing users to unset optional property values. - Only selecting value and applying focus if it is not read only. * NIFI-13136: - Addressing review feedback. - Adding styles to disabled editor input. - Fixing show hint/autocomplete in production build. This closes #8734
This commit is contained in:
parent
37937ffa15
commit
914e2b1057
|
@ -28,7 +28,6 @@ import { MatSelectModule } from '@angular/material/select';
|
|||
import { Observable } from 'rxjs';
|
||||
import { ParameterContextEntity, SelectOption } from '../../../../../../../state/shared';
|
||||
import { Client } from '../../../../../../../service/client.service';
|
||||
import { PropertyTable } from '../../../../../../../ui/common/property-table/property-table.component';
|
||||
import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nifi-spinner.directive';
|
||||
import { NifiTooltipDirective } from '../../../../../../../ui/common/tooltips/nifi-tooltip.directive';
|
||||
import { TextTip } from '../../../../../../../ui/common/tooltips/text-tip/text-tip.component';
|
||||
|
@ -51,7 +50,6 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
|
|||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
AsyncPipe,
|
||||
PropertyTable,
|
||||
NifiSpinnerDirective,
|
||||
NifiTooltipDirective,
|
||||
FormsModule,
|
||||
|
|
|
@ -27,6 +27,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { initialState } from '../../../../../../../state/error/error.reducer';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditProcessor', () => {
|
||||
let component: EditProcessor;
|
||||
let fixture: ComponentFixture<EditProcessor>;
|
||||
|
|
|
@ -27,6 +27,8 @@ import { initialState } from '../../../state/parameter-context-listing/parameter
|
|||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
import { ParameterContextEntity } from '../../../../../state/shared';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditParameterContext', () => {
|
||||
let component: EditParameterContext;
|
||||
let fixture: ComponentFixture<EditParameterContext>;
|
||||
|
|
|
@ -26,6 +26,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { initialState } from '../../../../../state/error/error.reducer';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditFlowAnalysisRule', () => {
|
||||
let component: EditFlowAnalysisRule;
|
||||
let fixture: ComponentFixture<EditFlowAnalysisRule>;
|
||||
|
|
|
@ -26,6 +26,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { initialState } from '../../../../../state/error/error.reducer';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditRegistryClient', () => {
|
||||
let component: EditRegistryClient;
|
||||
let fixture: ComponentFixture<EditRegistryClient>;
|
||||
|
|
|
@ -26,6 +26,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { initialState } from '../../../../../state/error/error.reducer';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditReportingTask', () => {
|
||||
let component: EditReportingTask;
|
||||
let fixture: ComponentFixture<EditReportingTask>;
|
||||
|
|
|
@ -28,7 +28,6 @@ import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
|||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { PropertyTable } from '../../property-table/property-table.component';
|
||||
import { ControllerServiceApi } from '../controller-service-api/controller-service-api.component';
|
||||
import { ControllerServiceReferences } from '../controller-service-references/controller-service-references.component';
|
||||
import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
|
||||
|
@ -59,7 +58,6 @@ import {
|
|||
MatTabsModule,
|
||||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
PropertyTable,
|
||||
ControllerServiceApi,
|
||||
ControllerServiceReferences,
|
||||
AsyncPipe,
|
||||
|
|
|
@ -26,6 +26,8 @@ import { provideMockStore } from '@ngrx/store/testing';
|
|||
import { initialState } from '../../../../state/error/error.reducer';
|
||||
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('EditControllerService', () => {
|
||||
let component: EditControllerService;
|
||||
let fixture: ComponentFixture<EditControllerService>;
|
||||
|
|
|
@ -30,7 +30,6 @@ import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
|||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { PropertyTable } from '../../property-table/property-table.component';
|
||||
import { ControllerServiceApi } from '../controller-service-api/controller-service-api.component';
|
||||
import { ControllerServiceReferences } from '../controller-service-references/controller-service-references.component';
|
||||
import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
|
||||
|
@ -67,7 +66,6 @@ import {
|
|||
MatTabsModule,
|
||||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
PropertyTable,
|
||||
ControllerServiceApi,
|
||||
ControllerServiceReferences,
|
||||
AsyncPipe,
|
||||
|
|
|
@ -61,17 +61,14 @@
|
|||
@include mat.button-density(-1);
|
||||
|
||||
.nf-editor {
|
||||
border-color: var(--mdc-outlined-text-field-label-text-color);
|
||||
|
||||
&.blank {
|
||||
border-color: var(--mdc-outlined-text-field-disabled-label-text-color);
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
background-color: if($is-dark, $nifi-theme-surface-palette-darker, $nifi-theme-surface-palette-lighter);
|
||||
|
||||
&.blank {
|
||||
background: $material-theme-primary-palette-default;
|
||||
color: if(
|
||||
$is-dark,
|
||||
$material-theme-primary-palette-darker,
|
||||
$material-theme-primary-palette-lighter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-code {
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
import { ComponentRef, Injectable, Renderer2, ViewContainerRef } from '@angular/core';
|
||||
import * as CodeMirror from 'codemirror';
|
||||
import { Editor, Hint, Hints, StringStream } from 'codemirror';
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { ElFunction, Parameter } from '../../../../../../state/shared';
|
||||
import { ParameterTip } from '../../../../tooltips/parameter-tip/parameter-tip.component';
|
||||
import { ElService } from './el.service';
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
cdkDrag
|
||||
resizable
|
||||
(resized)="resized()">
|
||||
<form class="h-full" [formGroup]="nfEditorForm" cdkTrapFocus cdkTrapFocusAutoCapture>
|
||||
<form class="h-full" [formGroup]="nfEditorForm" cdkTrapFocus [cdkTrapFocusAutoCapture]="!readonly">
|
||||
<div class="flex flex-col gap-y-3 h-full">
|
||||
<div class="flex justify-end">
|
||||
<div
|
||||
|
@ -47,10 +47,9 @@
|
|||
</ng-template>
|
||||
</div>
|
||||
<div class="flex flex-col gap-y-0.5 flex-1">
|
||||
<div class="nf-editor flex-1" #nfEditorContainer>
|
||||
<div class="nf-editor flex-1" [class.blank]="blank">
|
||||
<ngx-codemirror
|
||||
[options]="getOptions()"
|
||||
[autoFocus]="true"
|
||||
formControlName="value"
|
||||
(mousedown)="preventDrag($event)"
|
||||
(codeMirrorLoaded)="codeMirrorLoaded($event)"></ngx-codemirror>
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
.nf-editor {
|
||||
min-height: 100px;
|
||||
min-width: 210px;
|
||||
border: 1px solid;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
cursor: default;
|
||||
|
||||
.CodeMirror {
|
||||
|
@ -40,11 +41,6 @@
|
|||
font-family: monospace;
|
||||
cursor: default;
|
||||
line-height: normal;
|
||||
|
||||
&.blank {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
|
|
|
@ -21,6 +21,8 @@ import { NfEditor } from './nf-editor.component';
|
|||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { PropertyItem } from '../../property-table.component';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('NfEditor', () => {
|
||||
let component: NfEditor;
|
||||
let fixture: ComponentFixture<NfEditor>;
|
||||
|
|
|
@ -57,15 +57,16 @@ import { Resizable } from '../../../resizable/resizable.component';
|
|||
export class NfEditor implements OnDestroy {
|
||||
@Input() set item(item: PropertyItem) {
|
||||
this.nfEditorForm.get('value')?.setValue(item.value);
|
||||
|
||||
const isEmptyString: boolean = item.value == '';
|
||||
this.nfEditorForm.get('setEmptyString')?.setValue(isEmptyString);
|
||||
if (isEmptyString) {
|
||||
this.nfEditorForm.get('value')?.disable();
|
||||
if (item.descriptor.required) {
|
||||
this.nfEditorForm.get('value')?.addValidators(Validators.required);
|
||||
} else {
|
||||
this.nfEditorForm.get('value')?.enable();
|
||||
this.nfEditorForm.get('value')?.removeValidators(Validators.required);
|
||||
}
|
||||
|
||||
const isEmptyString: boolean = item.value === '';
|
||||
this.nfEditorForm.get('setEmptyString')?.setValue(isEmptyString);
|
||||
this.setEmptyStringChanged();
|
||||
|
||||
this.supportsEl = item.descriptor.supportsEl;
|
||||
this.sensitive = item.descriptor.sensitive;
|
||||
this.mode = this.supportsEl ? this.nfel.getLanguageId() : this.nfpr.getLanguageId();
|
||||
|
@ -83,7 +84,7 @@ export class NfEditor implements OnDestroy {
|
|||
@Input() width!: number;
|
||||
@Input() readonly: boolean = false;
|
||||
|
||||
@Output() ok: EventEmitter<string> = new EventEmitter<string>();
|
||||
@Output() ok: EventEmitter<string | null> = new EventEmitter<string | null>();
|
||||
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
protected readonly PropertyHintTip = PropertyHintTip;
|
||||
|
@ -95,6 +96,7 @@ export class NfEditor implements OnDestroy {
|
|||
sensitive = false;
|
||||
supportsEl = false;
|
||||
supportsParameters = false;
|
||||
blank = false;
|
||||
|
||||
mode!: string;
|
||||
_parameters!: Parameter[];
|
||||
|
@ -109,7 +111,7 @@ export class NfEditor implements OnDestroy {
|
|||
private nfpr: NfPr
|
||||
) {
|
||||
this.nfEditorForm = this.formBuilder.group({
|
||||
value: new FormControl('', Validators.required),
|
||||
value: new FormControl(''),
|
||||
setEmptyString: new FormControl(false)
|
||||
});
|
||||
}
|
||||
|
@ -117,7 +119,17 @@ export class NfEditor implements OnDestroy {
|
|||
codeMirrorLoaded(codeEditor: any): void {
|
||||
this.editor = codeEditor.codeMirror;
|
||||
this.editor.setSize('100%', '100%');
|
||||
this.editor.execCommand('selectAll');
|
||||
|
||||
if (!this.readonly) {
|
||||
this.editor.execCommand('selectAll');
|
||||
}
|
||||
|
||||
// disabling of the input through the form isn't supported until codemirror
|
||||
// has loaded so we must disable again if the value is an empty string
|
||||
if (this.nfEditorForm.get('setEmptyString')?.value) {
|
||||
this.nfEditorForm.get('value')?.disable();
|
||||
this.editor.setOption('readOnly', 'nocursor');
|
||||
}
|
||||
}
|
||||
|
||||
loadParameters(): void {
|
||||
|
@ -164,7 +176,9 @@ export class NfEditor implements OnDestroy {
|
|||
extraKeys: {
|
||||
'Ctrl-Space': 'autocomplete',
|
||||
Enter: () => {
|
||||
this.okClicked();
|
||||
if (this.nfEditorForm.dirty && this.nfEditorForm.valid) {
|
||||
this.okClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -188,6 +202,8 @@ export class NfEditor implements OnDestroy {
|
|||
setEmptyStringChanged(): void {
|
||||
const emptyStringChecked: AbstractControl | null = this.nfEditorForm.get('setEmptyString');
|
||||
if (emptyStringChecked) {
|
||||
this.blank = emptyStringChecked.value;
|
||||
|
||||
if (emptyStringChecked.value) {
|
||||
this.nfEditorForm.get('value')?.setValue('');
|
||||
this.nfEditorForm.get('value')?.disable();
|
||||
|
@ -207,8 +223,18 @@ export class NfEditor implements OnDestroy {
|
|||
|
||||
okClicked(): void {
|
||||
const valueControl: AbstractControl | null = this.nfEditorForm.get('value');
|
||||
if (valueControl) {
|
||||
this.ok.next(valueControl.value);
|
||||
const emptyStringChecked: AbstractControl | null = this.nfEditorForm.get('setEmptyString');
|
||||
if (valueControl && emptyStringChecked) {
|
||||
const value = valueControl.value;
|
||||
if (value === '') {
|
||||
if (emptyStringChecked.value) {
|
||||
this.ok.next('');
|
||||
} else {
|
||||
this.ok.next(null);
|
||||
}
|
||||
} else {
|
||||
this.ok.next(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
|
||||
import { PropertyTable } from './property-table.component';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
describe('PropertyTable', () => {
|
||||
let component: PropertyTable;
|
||||
let fixture: ComponentFixture<PropertyTable>;
|
||||
|
|
|
@ -554,7 +554,7 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
}
|
||||
}
|
||||
|
||||
savePropertyValue(item: PropertyItem, newValue: string): void {
|
||||
savePropertyValue(item: PropertyItem, newValue: string | null): void {
|
||||
if (item.value != newValue) {
|
||||
item.value = newValue;
|
||||
item.dirty = true;
|
||||
|
|
|
@ -19,6 +19,15 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
import 'codemirror/addon/edit/matchbrackets';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/comment-fold';
|
||||
import 'codemirror/addon/fold/foldcode';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/fold/markdown-fold';
|
||||
import 'codemirror/addon/fold/xml-fold';
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
|
|
Loading…
Reference in New Issue