Skip to content

Commit

Permalink
[UI] Allow single item deletion from multi-value fields (#7084)
Browse files Browse the repository at this point in the history
* Support single endpoint deletion from multi-endpoints component

* Support single command deletion from multi-commands component

* Support single key-value item deletion from multi-key-value component

* Support single text item deletion from multi-text component

* Support single volume mount item deletion from multi-volume-mounts component

* Add Cypress test cases

* Git-ignore Cypress screenshots folder

* Generate static UI

* Update Delete icon and add tooltip to it

Co-authored-by: Philippe Martin <[email protected]>

* Move the "delete endpoint" button closer to the element it is attached to

Co-authored-by: Philippe Martin <[email protected]>

* Generate static UI

* Revert "Move the "delete endpoint" button closer to the element it is attached to"

This reverts commit 4bf895f.

* Move the "delete endpoint" buttons closer to the elements they are attached to

Co-authored-by: Philippe Martin <[email protected]>

* Generate static UI

---------

Co-authored-by: Philippe Martin <[email protected]>
  • Loading branch information
rm3l and feloy authored Sep 18, 2023
1 parent b20ceb6 commit 9724292
Show file tree
Hide file tree
Showing 23 changed files with 448 additions and 91 deletions.
2 changes: 1 addition & 1 deletion pkg/apiserver-impl/ui/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ testem.log
Thumbs.db

/cypress/videos
/cypress/screenshots

.odo
.odo
282 changes: 270 additions & 12 deletions ui/cypress/e2e/spec.cy.ts

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions ui/cypress/fixtures/input/with-exec-command.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ commands:
hotReloadCapable: false
workingDir: /projects
id: command1
- exec:
commandLine: echo command2
component: container1
hotReloadCapable: true
workingDir: /projects
id: command2
- exec:
commandLine: echo command3
component: container1
hotReloadCapable: true
workingDir: /projects
id: command3
components:
- container:
args:
Expand Down
12 changes: 12 additions & 0 deletions ui/cypress/fixtures/input/with-volume.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
schemaVersion: 2.2.0
metadata: {}
components:
- name: volume1
volume: {}
- name: volume2
volume:
size: 2Gi
- name: volume3
volume:
ephemeral: true
size: 3G
15 changes: 15 additions & 0 deletions ui/src/app/controls/endpoints/endpoints.component.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
.mid-width { width: 50%; }
.quart-width { width: 25%; }

mat-card{
display:flex;
flex-direction: row;
margin-bottom: 16px;
}

mat-card-content{
flex-grow: 1;
overflow: auto;
}

button.adjust-position {
right: 6px;
}
88 changes: 47 additions & 41 deletions ui/src/app/controls/endpoints/endpoints.component.html
Original file line number Diff line number Diff line change
@@ -1,43 +1,49 @@
<div *ngFor="let control of form.controls; index as i">
<ng-container [formGroup]="control">
<mat-form-field class="mid-width" appearance="outline">
<mat-label><span>Name</span></mat-label>
<input [attr.data-cy]="'endpoint-name-'+i" matInput formControlName="name">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label><span>Target Port</span></mat-label>
<input [attr.data-cy]="'endpoint-targetPort-'+i" type="number" matInput formControlName="targetPort">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label>Exposure</mat-label>
<mat-select [attr.data-cy]="'endpoint-exposure-'+i" formControlName="exposure">
<mat-option value="">(default, public)</mat-option>
<mat-option value="public">public</mat-option>
<mat-option value="internal">internal</mat-option>
<mat-option value="none">none</mat-option>
</mat-select>
</mat-form-field>
<div class="group">
<mat-card *ngFor="let control of form.controls; index as i">
<mat-card-content [formGroup]="control">
<mat-form-field class="mid-width" appearance="outline">
<mat-label><span>Name</span></mat-label>
<input [attr.data-cy]="'endpoint-name-'+i" matInput formControlName="name">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label><span>Target Port</span></mat-label>
<input [attr.data-cy]="'endpoint-targetPort-'+i" type="number" matInput formControlName="targetPort">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label>Exposure</mat-label>
<mat-select [attr.data-cy]="'endpoint-exposure-'+i" formControlName="exposure">
<mat-option value="">(default, public)</mat-option>
<mat-option value="public">public</mat-option>
<mat-option value="internal">internal</mat-option>
<mat-option value="none">none</mat-option>
</mat-select>
</mat-form-field>

<mat-form-field class="mid-width" appearance="outline">
<mat-label><span>Path</span></mat-label>
<input [attr.data-cy]="'endpoint-path-'+i" matInput formControlName="path">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label>Protocol</mat-label>
<mat-select [attr.data-cy]="'endpoint-protocol-'+i" formControlName="protocol">
<mat-option value="">(default, http)</mat-option>
<mat-option value="http">http</mat-option>
<mat-option value="https">https</mat-option>
<mat-option value="ws">ws</mat-option>
<mat-option value="wss">wss</mat-option>
<mat-option value="tcp">tcp</mat-option>
<mat-option value="udp">udp</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [attr.data-cy]="'endpoint-secure-'+i" formControlName="secure">Protocol Is Secure</mat-checkbox>
</ng-container>
<mat-form-field class="mid-width" appearance="outline">
<mat-label><span>Path</span></mat-label>
<input [attr.data-cy]="'endpoint-path-'+i" matInput formControlName="path">
</mat-form-field>
<mat-form-field class="quart-width" appearance="outline">
<mat-label>Protocol</mat-label>
<mat-select [attr.data-cy]="'endpoint-protocol-'+i" formControlName="protocol">
<mat-option value="">(default, http)</mat-option>
<mat-option value="http">http</mat-option>
<mat-option value="https">https</mat-option>
<mat-option value="ws">ws</mat-option>
<mat-option value="wss">wss</mat-option>
<mat-option value="tcp">tcp</mat-option>
<mat-option value="udp">udp</mat-option>
</mat-select>
</mat-form-field>
<mat-checkbox [attr.data-cy]="'endpoint-secure-'+i" formControlName="secure">Protocol Is Secure</mat-checkbox>
</mat-card-content>
<mat-card-actions>
<button [attr.data-cy]="'endpoint-minus-'+i" class="adjust-position" mat-icon-button matTooltip="Delete endpoint" (click)="removeEndpoint(i)">
<mat-icon class="tab-icon material-icons-outlined">delete_forever</mat-icon>
</button>
</mat-card-actions>
</mat-card>
<div>
<button data-cy="endpoints-add" mat-flat-button (click)="addEndpoint()">Add an Endpoint</button>
</div>
</div>
<button data-cy="endpoints-plus" *ngIf="form.value.length > 0" mat-icon-button (click)="addEndpoint()">
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
</button>
<button data-cy="endpoints-add" *ngIf="form.value.length == 0" mat-flat-button (click)="addEndpoint()">Add an Endpoint</button>
4 changes: 4 additions & 0 deletions ui/src/app/controls/endpoints/endpoints.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export class EndpointsComponent implements ControlValueAccessor, Validator {
}));
}

removeEndpoint(index: number) {
this.form.removeAt(index);
}

/* ControlValueAccessor implementation */
writeValue(value: Endpoint[]) {
value.forEach(ep => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
h3 { margin-bottom: 0; }
div.group { margin-bottom: 16px; }
div.group { margin-bottom: 16px; }

button.adjust-position {
right: 6px;
}
12 changes: 7 additions & 5 deletions ui/src/app/controls/multi-command/multi-command.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ <h3>{{title}}</h3>
<span *ngFor="let control of form.controls; index as i">
<mat-form-field appearance="fill">
<mat-label><span>Command</span></mat-label>
<mat-select [formControl]="control">
<mat-select [attr.data-cy]="'command-selector-'+i" [formControl]="control">
<mat-option *ngFor="let commandElement of commandList" [value]="commandElement">{{commandElement}}</mat-option>
</mat-select>
</mat-form-field>
<button [attr.data-cy]="'command-minus-'+i" class="adjust-position" mat-icon-button matTooltip="Delete command" (click)="removeCommand(i)">
<mat-icon class="tab-icon material-icons-outlined">delete_forever</mat-icon>
</button>
</span>
<button *ngIf="form.controls.length > 0" mat-icon-button (click)="addCommand('')">
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
</button>
<button *ngIf="form.controls.length == 0" mat-flat-button (click)="addCommand('')">{{addLabel}}</button>
<div>
<button [attr.data-cy]="'add-command'" mat-flat-button (click)="addCommand('')">{{addLabel}}</button>
</div>
</div>
4 changes: 4 additions & 0 deletions ui/src/app/controls/multi-command/multi-command.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export class MultiCommandComponent implements ControlValueAccessor, Validator {
this.form.push(this.newCommand(cmdName));
}

removeCommand(index: number) {
this.form.removeAt(index);
}

/* Validator implementation */
validate(control: AbstractControl): ValidationErrors | null {
if (!this.form.valid) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
div.group { margin-bottom: 16px; }
.mid-width { width: 50%; }
.kv-width { width: 45%; }

button.adjust-position {
right: 6px;
top: 6px;
}

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<div class="group">
<div *ngFor="let control of form.controls; index as i">
<ng-container [formGroup]="control">
<mat-form-field class="mid-width" appearance="outline">
<mat-form-field class="kv-width" appearance="outline">
<mat-label><span>Name</span></mat-label>
<input [attr.data-cy]="dataCyPrefix+'-name-'+i" matInput formControlName="name">
</mat-form-field>
<mat-form-field class="mid-width" appearance="outline">
<mat-form-field class="kv-width" appearance="outline">
<mat-label><span>Value</span></mat-label>
<input [attr.data-cy]="dataCyPrefix+'-value-'+i" matInput formControlName="value">
</mat-form-field>
<button [attr.data-cy]="dataCyPrefix+'-minus-'+i" class="adjust-position" mat-icon-button [matTooltip]="deleteLabel" (click)="removeEntry(i)">
<mat-icon class="tab-icon material-icons-outlined">delete_forever</mat-icon>
</button>
</ng-container>
</div>
<button [attr.data-cy]="dataCyPrefix+'-plus'" *ngIf="form.controls.length > 0" mat-icon-button (click)="addEntry('', '')">
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
</button>
<button [attr.data-cy]="dataCyPrefix+'-add'" *ngIf="form.controls.length == 0" mat-flat-button (click)="addEntry('', '')">{{addLabel}}</button>
<button [attr.data-cy]="dataCyPrefix+'-add'" mat-flat-button (click)="addEntry('', '')">{{addLabel}}</button>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class MultiKeyValueComponent implements ControlValueAccessor, Validator {

@Input() dataCyPrefix: string = "";
@Input() addLabel: string = "";
@Input() deleteLabel: string = "";

form = new FormArray<FormGroup>([]);

Expand Down Expand Up @@ -71,6 +72,10 @@ export class MultiKeyValueComponent implements ControlValueAccessor, Validator {
this.form.push(this.newKeyValueForm({name, value}));
}

removeEntry(index: number) {
this.form.removeAt(index);
}

/* Validator implementation */
validate(control: AbstractControl): ValidationErrors | null {
if (!this.form.valid) {
Expand Down
7 changes: 6 additions & 1 deletion ui/src/app/controls/multi-text/multi-text.component.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
h3 { margin-bottom: 0; }
div.group { margin-bottom: 16px; }
div.group { margin-bottom: 16px; }

button.adjust-position {
right: 6px;
top: 6px;
}
27 changes: 17 additions & 10 deletions ui/src/app/controls/multi-text/multi-text.component.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<h3 *ngIf="title">{{title}}</h3>
<div class="group">
<span *ngFor="let control of form.controls; index as i">
<mat-form-field class="inline" appearance="outline">
<mat-label><span>{{label}}</span></mat-label>
<input matInput [formControl]="control">
</mat-form-field>
</span>
<button *ngIf="form.controls.length > 0" mat-icon-button (click)="addText('')">
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
</button>
<button *ngIf="form.controls.length == 0" mat-flat-button (click)="addText('')">{{addLabel}}</button>
<mat-card *ngIf="form.controls.length > 0">
<mat-card-content>
<span *ngFor="let control of form.controls; index as i">
<mat-form-field class="inline" appearance="outline">
<mat-label><span>{{label}}</span></mat-label>
<input [attr.data-cy]="dataCyPrefix+'-text-'+i" matInput [formControl]="control">
</mat-form-field>
<button [attr.data-cy]="dataCyPrefix+'-minus-'+i" class="adjust-position" mat-icon-button [matTooltip]="deleteLabel" (click)="removeText(i)">
<mat-icon class="tab-icon material-icons-outlined">delete_forever</mat-icon>
</button>
</span>
</mat-card-content>
<mat-card-actions>
<button [attr.data-cy]="'add-text'" mat-flat-button (click)="addText('')">{{addLabel}}</button>
</mat-card-actions>
</mat-card>
<button *ngIf="form.controls.length == 0" [attr.data-cy]="'add-text'" mat-flat-button (click)="addText('')">{{addLabel}}</button>
</div>
6 changes: 6 additions & 0 deletions ui/src/app/controls/multi-text/multi-text.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import {
})
export class MultiTextComponent implements ControlValueAccessor, Validator {

@Input() dataCyPrefix: string = "";
@Input() label: string = "";
@Input() addLabel: string = "";
@Input() deleteLabel: string = "";
@Input() title: string = "";

onChange = (_: string[]) => {};
Expand Down Expand Up @@ -62,6 +64,10 @@ export class MultiTextComponent implements ControlValueAccessor, Validator {
this.form.push(this.newText(text));
}

removeText(index: number) {
this.form.removeAt(index);
}

/* Validator implementation */
validate(control: AbstractControl): ValidationErrors | null {
if (!this.form.valid) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
h3 { margin-bottom: 0; }
div.group { margin-bottom: 16px; }
div.group { margin-bottom: 16px; }

button.adjust-position {
right: 6px;
top: 6px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
<input formControlName="path" [attr.data-cy]="'volume-mount-path-'+i" matInput>
</mat-form-field>

<button [attr.data-cy]="'volume-mount-minus-'+i" class="adjust-position" mat-icon-button matTooltip="Delete Volume Mount" (click)="remove(i)">
<mat-icon class="tab-icon material-icons-outlined">delete_forever</mat-icon>
</button>

<app-volume
*ngIf="showNewVolume[i]"
(created)="onNewVolumeCreated(i, $event)"
></app-volume>
</ng-container>
</div>
<button data-cy="volume-mount-add" *ngIf="form.controls.length > 0" mat-icon-button (click)="add('', '')">
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
</button>
<button data-cy="volume-mount-add" *ngIf="form.controls.length == 0" mat-flat-button (click)="add('', '')">Add Volume Mount</button>
<button data-cy="volume-mount-add" mat-flat-button (click)="add('', '')">Add Volume Mount</button>
</div>

4 changes: 4 additions & 0 deletions ui/src/app/controls/volume-mounts/volume-mounts.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export class VolumeMountsComponent implements ControlValueAccessor, Validator {
this.form.push(this.newVolumeMount({name, path}));
}

remove(i: number) {
this.form.removeAt(i);
}

onNameChange(i: number, name: string) {
this.showNewVolume[i] = name == "!";
}
Expand Down
Loading

0 comments on commit 9724292

Please sign in to comment.