mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Set Save button on top, enable it only when devfile changed (#7015)
* Set Save button on top, enable it only when devfile changed * Use snackbar to display parse errors * Do not alert devfile modified when user clicks Save * Update UI static files
This commit is contained in:
2
pkg/apiserver-impl/ui/index.html
generated
2
pkg/apiserver-impl/ui/index.html
generated
@@ -11,6 +11,6 @@
|
|||||||
<body class="mat-typography">
|
<body class="mat-typography">
|
||||||
<div id="loading">Loading, please wait...</div>
|
<div id="loading">Loading, please wait...</div>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.a562f09a5f00f55f.js" type="module"></script>
|
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.1f6f2714e5bbe400.js" type="module"></script>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
1
pkg/apiserver-impl/ui/main.1f6f2714e5bbe400.js
generated
Normal file
1
pkg/apiserver-impl/ui/main.1f6f2714e5bbe400.js
generated
Normal file
File diff suppressed because one or more lines are too long
1
pkg/apiserver-impl/ui/main.a562f09a5f00f55f.js
generated
1
pkg/apiserver-impl/ui/main.a562f09a5f00f55f.js
generated
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@ describe('devfile editor errors handling', () => {
|
|||||||
it('fails when YAML is not valid', () => {
|
it('fails when YAML is not valid', () => {
|
||||||
cy.init();
|
cy.init();
|
||||||
cy.setDevfile("wrong yaml content");
|
cy.setDevfile("wrong yaml content");
|
||||||
cy.getByDataCy("yaml-error").should('contain.text', 'error parsing devfile YAML');
|
cy.get(".cdk-overlay-container").should('contain.text', 'error parsing devfile YAML');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails when adding a container with an already used name', () => {
|
it('fails when adding a container with an already used name', () => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<span class="topright">Work in progress</span>
|
<span class="topright">Work in progress</span>
|
||||||
<a mat-icon-button href="https://github.com/feloy/devfile-builder" target="_blank"><mat-icon svgIcon="github"></mat-icon></a>
|
<a mat-icon-button href="https://github.com/feloy/devfile-builder" target="_blank"><mat-icon svgIcon="github"></mat-icon></a>
|
||||||
|
<button style="top: -8px" data-cy="yaml-send" matTooltip="Save Devfile to disk" mat-flat-button color="warn" disabled="{{!(state.modified|async)}}" (click)="onSave(input.value)">Save</button>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
@@ -13,13 +14,11 @@
|
|||||||
|
|
||||||
<mat-tab data-cy="tab-yaml" label="{{tabNames[0]}}">
|
<mat-tab data-cy="tab-yaml" label="{{tabNames[0]}}">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div *ngIf="errorMessage" data-cy="yaml-error" class="error-message">{{errorMessage}}</div>
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>Devfile YAML</mat-label>
|
<mat-label>Devfile YAML</mat-label>
|
||||||
<textarea data-cy="yaml-input" matInput #input id="input" rows="20" [value]="devfileYaml"></textarea>
|
<textarea data-cy="yaml-input" matInput #input id="input" rows="20" [value]="devfileYaml"></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button data-cy="yaml-send" matTooltip="Save Devfile to disk" mat-flat-button color="primary" (click)="onSave(input.value)">Save</button>
|
<button data-cy="yaml-save" matTooltip="Apply changes to other tabs" mat-flat-button color="primary" (click)="onApply(input.value)">Apply</button>
|
||||||
<button data-cy="yaml-save" matTooltip="Apply changes to other tabs" mat-flat-button color="normal" (click)="onApply(input.value)">Apply</button>
|
|
||||||
<button data-cy="yaml-clear" matTooltip="Clear Devfile content" mat-flat-button color="normal" (click)="clear()">Clear</button>
|
<button data-cy="yaml-clear" matTooltip="Clear Devfile content" mat-flat-button color="normal" (click)="clear()">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export class AppComponent implements OnInit {
|
|||||||
];
|
];
|
||||||
protected mermaidContent: string = "";
|
protected mermaidContent: string = "";
|
||||||
protected devfileYaml: string = "";
|
protected devfileYaml: string = "";
|
||||||
protected errorMessage: string = "";
|
|
||||||
private snackBarRef: MatSnackBarRef<ConfirmComponent> | null = null;
|
private snackBarRef: MatSnackBarRef<ConfirmComponent> | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -42,7 +41,7 @@ export class AppComponent implements OnInit {
|
|||||||
private wasmGo: DevstateService,
|
private wasmGo: DevstateService,
|
||||||
private odoApi: OdoapiService,
|
private odoApi: OdoapiService,
|
||||||
private mermaid: MermaidService,
|
private mermaid: MermaidService,
|
||||||
private state: StateService,
|
protected state: StateService,
|
||||||
private sse: SseService,
|
private sse: SseService,
|
||||||
private telemetry: TelemetryService,
|
private telemetry: TelemetryService,
|
||||||
private snackbar: MatSnackBar
|
private snackbar: MatSnackBar
|
||||||
@@ -63,7 +62,7 @@ export class AppComponent implements OnInit {
|
|||||||
devfile.subscribe({
|
devfile.subscribe({
|
||||||
next: (devfile) => {
|
next: (devfile) => {
|
||||||
if (devfile.content != undefined) {
|
if (devfile.content != undefined) {
|
||||||
this.propagateChange(devfile.content, false);
|
this.propagateChange(devfile.content, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -88,6 +87,10 @@ export class AppComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.sse.subscribeTo(['DevfileUpdated']).subscribe(event => {
|
this.sse.subscribeTo(['DevfileUpdated']).subscribe(event => {
|
||||||
|
const newDevfile: DevfileContent = JSON.parse(event.data);
|
||||||
|
if (!this.state.isUpdated(newDevfile.content)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.snackBarRef != null) {
|
if (this.snackBarRef != null) {
|
||||||
this.snackBarRef.afterDismissed().subscribe(() => {});
|
this.snackBarRef.afterDismissed().subscribe(() => {});
|
||||||
this.snackBarRef.dismiss();
|
this.snackBarRef.dismiss();
|
||||||
@@ -98,9 +101,8 @@ export class AppComponent implements OnInit {
|
|||||||
yesLabel: "Update"
|
yesLabel: "Update"
|
||||||
}});
|
}});
|
||||||
this.snackBarRef.onAction().subscribe(() => {
|
this.snackBarRef.onAction().subscribe(() => {
|
||||||
let newDevfile: DevfileContent = JSON.parse(event.data);
|
|
||||||
if (newDevfile.content != undefined) {
|
if (newDevfile.content != undefined) {
|
||||||
this.propagateChange(newDevfile.content, false);
|
this.propagateChange(newDevfile.content, false, true);
|
||||||
}
|
}
|
||||||
this.snackBarRef = null;
|
this.snackBarRef = null;
|
||||||
});
|
});
|
||||||
@@ -123,35 +125,34 @@ export class AppComponent implements OnInit {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
propagateChange(content: string, saveToApi: boolean){
|
propagateChange(content: string, saveToApi: boolean, fromApi: boolean){
|
||||||
const result = this.wasmGo.setDevfileContent(content);
|
const result = this.wasmGo.setDevfileContent(content);
|
||||||
result.subscribe({
|
result.subscribe({
|
||||||
next: (value) => {
|
next: (value) => {
|
||||||
this.errorMessage = '';
|
this.state.changeDevfileYaml(value, fromApi);
|
||||||
this.state.changeDevfileYaml(value);
|
|
||||||
if (saveToApi) {
|
if (saveToApi) {
|
||||||
this.odoApi.saveDevfile(value.content).subscribe({
|
this.odoApi.saveDevfile(value.content).subscribe({
|
||||||
next: () => {},
|
next: () => {},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
this.errorMessage = error.error.message;
|
this.snackbar.open(error.error.message, "ok");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
this.errorMessage = error.error.message;
|
this.snackbar.open(error.error.message, "ok");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave(content: string) {
|
onSave(content: string) {
|
||||||
this.telemetry.track("[ui] save devfile to disk");
|
this.telemetry.track("[ui] save devfile to disk");
|
||||||
this.propagateChange(content, true);
|
this.propagateChange(content, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
onApply(content: string) {
|
onApply(content: string) {
|
||||||
this.telemetry.track("[ui] change devfile from textarea");
|
this.telemetry.track("[ui] change devfile from textarea");
|
||||||
this.propagateChange(content, false);
|
this.propagateChange(content, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
@@ -159,7 +160,7 @@ export class AppComponent implements OnInit {
|
|||||||
this.telemetry.track("[ui] clear devfile");
|
this.telemetry.track("[ui] clear devfile");
|
||||||
this.wasmGo.clearDevfileContent().subscribe({
|
this.wasmGo.clearDevfileContent().subscribe({
|
||||||
next: (value) => {
|
next: (value) => {
|
||||||
this.propagateChange(value.content, false);
|
this.propagateChange(value.content, false, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,32 @@ import { DevfileContent } from '../api-gen';
|
|||||||
})
|
})
|
||||||
export class StateService {
|
export class StateService {
|
||||||
|
|
||||||
|
private savedDevfile: string = "";
|
||||||
|
|
||||||
private _state = new BehaviorSubject<DevfileContent | null>(null);
|
private _state = new BehaviorSubject<DevfileContent | null>(null);
|
||||||
public state = this._state.asObservable();
|
public state = this._state.asObservable();
|
||||||
|
|
||||||
changeDevfileYaml(newValue: DevfileContent) {
|
private _modified = new BehaviorSubject<boolean | null>(null);
|
||||||
|
public modified = this._modified.asObservable();
|
||||||
|
|
||||||
|
changeDevfileYaml(newValue: DevfileContent, fromDisk: boolean = false) {
|
||||||
this._state.next(newValue);
|
this._state.next(newValue);
|
||||||
|
|
||||||
|
if (fromDisk) {
|
||||||
|
this.savedDevfile = newValue.content;
|
||||||
|
}
|
||||||
|
if (this.savedDevfile == "") {
|
||||||
|
this.savedDevfile = newValue.content;
|
||||||
|
}
|
||||||
|
if (this.savedDevfile == newValue.content) {
|
||||||
|
this._modified.next(false);
|
||||||
|
} else {
|
||||||
|
this._modified.next(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isUpdated(devfile: string): boolean {
|
||||||
|
return devfile != this.savedDevfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDragAndDropEnabled(): boolean {
|
getDragAndDropEnabled(): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user