diff options
author | Tomasz Golabek <tomasz.golabek@nokia.com> | 2019-04-09 12:39:08 +0200 |
---|---|---|
committer | Ofir Sonsino <ofir.sonsino@intl.att.com> | 2019-04-30 10:16:37 +0000 |
commit | ffd70b436f496416535984d6cfe71e10757ec254 (patch) | |
tree | 0f4754191cf4ef0db58070feb67b42acdf10a1d3 /catalog-ui/src/app/ng2 | |
parent | cd5da806dad48bfabd2de5ae56018483dab6106f (diff) |
Dynamic columns in GAB table
Possibility to add and remove new columns in GAB table.
Currently additional columns are not stored anywhere.
Change-Id: Idc204ef3da5be8f9509289461165e22d4c0068bf
Issue-ID: SDC-2229
Signed-off-by: Tomasz Golabek <tomasz.golabek@nokia.com>
Diffstat (limited to 'catalog-ui/src/app/ng2')
7 files changed, 434 insertions, 120 deletions
diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html new file mode 100644 index 0000000000..d5fa96a4a3 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.html @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (C) 2019 Nokia. All rights reserved. + ~ + ~ Licensed 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. + --> + +<div> + <form class="gab-column-provider-form" #generalForm="ngForm"> + <input type="text" name="name" (keyup)='checkNameDuplications($event)' + ng-maxlength="50" + [(ngModel)]="name" + class="gab-column-provider-input" + required> + <label placeholder="Column name" alt="Column name"></label> + + <input type="text" name="path" (keyup)='checkPathDuplications($event)' + [(ngModel)]="path" + class="gab-column-provider-input" + required> + <label placeholder="Json path" alt="Json path"></label> + + <button [disabled]="generalForm.invalid" class="tlv-btn blue" data-tests-id="Add" (click)="addColumn()">Add</button> + <button class="tlv-btn blue" data-tests-id="Close" (click)="cancelAddingNewColumn()">Cancel</button> + </form> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less new file mode 100644 index 0000000000..07241a8aff --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.less @@ -0,0 +1,92 @@ +.adaptive_placeholder(@height, @radius, @margin: 0em, @border: 1px) { + @borders: (@border * 2); + box-sizing: border-box; + width: 80%; + height: ~"calc(@{height} + @{borders})"; + margin: @margin; + padding: @margin; + border: @border solid #00bafa; + border-radius: @radius; + background: #fff; + resize: none; + outline: none; + &[required] { + &:focus { + border-color: #00bafa; + + label { + &[placeholder] { + &:before { + margin: 0; + color: #00bafa; + } + } + } + } + &:focus, + &:valid { + + label { + &[placeholder] { + &:before { + transform: translate(0, (-1em)) scale(.9, .9); + } + } + } + } + &:invalid { + + label { + &[placeholder] { + &[alt] { + &:before { + content: attr(alt); + } + } + } + } + } + + label { + &[placeholder] { + display: block; + pointer-events: none; + line-height: @margin * 1.25; + margin-top: ~"calc(-1em - @{borders})"; + margin-bottom: ~"calc((@{height} - @{margin}) + @{borders})"; + &:before { + content: attr(placeholder); + display: inline-block; + margin: 0 ~"calc(@{margin} + @{borders})"; + padding: 0 2px; + color: #00bafa; + font-size: small; + white-space: nowrap; + transition: .3s ease-in-out; + + background-image: linear-gradient(to bottom, #fff, #fff); + background-size: 100% 5px; + background-repeat: no-repeat; + background-position: center; + } + } + } + } +} + +.gab-column-provider-form { + margin: 5px +} + +.gab-column-provider-input { + @height: 2em; + &[type="text"] { + .adaptive_placeholder(@height, (@height / 2)); + } +} + +.gab-column-provider-input.ng-valid[required], .gab-column-provider-input.ng-valid.required { + border-left: 5px solid #42bb48; + border-right: 5px solid #42bb48; +} + +.gab-column-provider-input.ng-invalid:not(form) { + border-left: 5px solid #ff0511; + border-right: 5px solid #ff0511; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts new file mode 100644 index 0000000000..570f428b04 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser-column-provider.component.ts @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +import {Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation} from "@angular/core"; +import {PathsAndNamesDefinition} from "../../../../models/paths-and-names"; + +@Component({ + selector: 'gab-column-provider', + templateUrl: './generic-artifact-browser-column-provider.component.html', + styleUrls: ['./generic-artifact-browser-column-provider.component.less'], + encapsulation: ViewEncapsulation.None +}) +export class GenericArtifactBrowserColumnProviderComponent { + @Input() + pathsAndNames: PathsAndNamesDefinition[]; + + @Output() + onCancel = new EventEmitter(); + @Output() + onSave = new EventEmitter(); + + @ViewChild('generalForm') generalForm; + name: string; + path: string; + + constructor() {} + + checkNameDuplications(event) { + const tmp = event.target.value; + if (tmp && !this.columnsContainsName(tmp)) { + this.name = tmp; + this.generalForm.form.controls['name'].setErrors(null); + } else { + this.generalForm.form.controls['name'].setErrors({incorrect: true}); + } + } + + checkPathDuplications(event) { + const tmp = event.target.value; + if (tmp && !this.columnsContainsPath(tmp)) { + this.path = tmp; + this.generalForm.form.controls['path'].setErrors(null); + } else { + this.generalForm.form.controls['path'].setErrors({incorrect: true}); + } + } + + cancelAddingNewColumn() { + this.onCancel.emit(); + } + + saveNewColumn() { + this.onSave.emit(); + } + + addColumn() { + this.updateColumnFilter(this.name, this.path); + this.saveNewColumn(); + } + + private updateColumnFilter = (name: string, prop: string): void => { + this.pathsAndNames.push(new PathsAndNamesDefinition(prop, name)); + this.generalForm.form.controls['name'].setValue(""); + this.generalForm.form.controls['path'].setValue(""); + } + + private columnsContainsName = (name: string): boolean => { + const columnDefinitions = this.pathsAndNames.filter(column => column.friendlyName.toLowerCase() === name.toLowerCase()); + return columnDefinitions.length > 0; + } + + private columnsContainsPath = (path: string): boolean => { + const columnDefinitions = this.pathsAndNames.filter(column => column.path.toLowerCase() === path.toLowerCase()); + return columnDefinitions.length > 0; + } + +} diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html index 1abd68db50..ad1ebcb094 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.html @@ -15,36 +15,51 @@ --> <div> - <ngx-datatable - class="material" - *ngIf="ready" - [rows]="rows" - [columns]="columns" - [columnMode]="'force'" - [headerHeight]="50" - [scrollbarV]="true" - [scrollbarH]="true" - [footerHeight]="0" - [loadingIndicator]="isLoading" - [rowHeight]="200" - [reorderable]="false" - [selected]="selectedRows" - [selectionType]="'cell'" - > - <ngx-datatable-column [prop]="col.prop" *ngFor="let col of columns"> - <template let-column="column" ngx-datatable-header-template> - <span style="height:10px"> + <ngx-datatable + class="material" + *ngIf="ready" + [rows]="rows" + [columns]="columns" + [columnMode]="'force'" + [headerHeight]="125" + [scrollbarV]="true" + [scrollbarH]="true" + [footerHeight]="0" + [loadingIndicator]="isLoading" + [rowHeight]="200" + [reorderable]="false" + > + <ngx-datatable-column prop="{{col.prop}}" [minWidth]="100" *ngFor="let col of columns"> + <template let-column="column" height="100" ngx-datatable-header-template> + <span class="datatable-column-span"> <b>{{col.name}}</b> + <div *ngIf="canBeDeleted(col.name)" style="width: 45px !important; color: red; " + class="delete-icon" (click)="deleteColumn(col)">⛌</div> </span> - <br/> - <input - type='text' - class="datatable-input-filter" - placeholder='Filter column...' - (keyup)='updateColumnFilter($event, col.prop)' - /> - </template> - </ngx-datatable-column> + <br/> + <input + type='text' + class="datattable-input-filter" + placeholder='Filter column...' + (keyup)='updateColumnFilter($event, col.prop)' + /> + </template> + </ngx-datatable-column> - </ngx-datatable> + <ngx-datatable-column *ngIf="addNewColumn" class="datatable-white-body-cell" [minWidth]="220" [maxWidth]="220" [width]="220" > + <template ngx-datatable-header-template> + <gab-column-provider [pathsAndNames]="pathsandnames" (onCancel)="hideAddNewColumn()" (onSave)="refresh()"></gab-column-provider> + </template> + <template class="datatable-white-body-cell" ngx-datatable-cell-template> + </template> + </ngx-datatable-column> + + <ngx-datatable-column class="datatable-white-body-cell" [minWidth]="50" [maxWidth]="50" [width]="50" > + <template ngx-datatable-header-template> + <div data-tests-id="gab-add-btn" class="add-btn add-btn-div" (click)="showAddNewColumn()">Add</div> + </template> + <template class="datatable-white-body-cell" ngx-datatable-cell-template> + </template> + </ngx-datatable-column> + </ngx-datatable> </div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less index 57da199ef2..b237a123b3 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.less @@ -18,7 +18,9 @@ border-bottom: 6px solid white; .selectable(); - + :last-of-type { + background-color: white; + } span { display: inline-block; line-height: 15pt; @@ -29,6 +31,31 @@ } } +.datatable-column-span { + height: 10px; +} + +.add-btn-div { + width: 45px; +} + +.datattable-input-filter{ + height: 23px; + margin: 2px auto; + border: none; + border-bottom: solid 3px #009fdb; + border-radius: 15px; + width: 75% !important; + padding: 0 15px 0 15px !important; + color: #009fdb; + outline: none; +} + +.datattable-input-filter::placeholder { + color: #009fdb; + font-style: italic; +} + .datatable-row-group { padding-bottom: 6px; padding-top: 6px; @@ -45,8 +72,10 @@ } } -.datatable-header span.datatable-header-cell-label { - .break-all-words(); +.datatable-header { + span.datatable-header-cell-label { + .break-all-words(); + } } .datatable-input-filter{ @@ -68,6 +97,9 @@ font-weight: bold; font-size: 14px; text-align: center; + div { + display: inline-block; + } } .break-all-words { diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts index 695d782a15..7e704e1927 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component.ts @@ -25,49 +25,85 @@ import {PathsAndNamesDefinition} from "../../../../models/paths-and-names"; const COLUMN_PREFIX: string = 'col'; @Component({ - selector: 'gab', - templateUrl: './generic-artifact-browser.component.html', - styleUrls:['./generic-artifact-browser.component.less'], - encapsulation: ViewEncapsulation.None + selector: 'gab', + templateUrl: './generic-artifact-browser.component.html', + styleUrls: ['./generic-artifact-browser.component.less'], + encapsulation: ViewEncapsulation.None }) export class GenericArtifactBrowserComponent { - @Input() - pathsandnames: PathsAndNamesDefinition[]; - @Input() - artifactid: string; - @Input() - resourceid: string; - - columns: ColumnDefinition[]; - rows: any[]; - originRows: any[]; - selectedRows: any[]; - isLoading: boolean; - ready: boolean; - columnsFilters: Map<string, string>; - - constructor(private gabService: GabService) { - } + @Input() + pathsandnames: PathsAndNamesDefinition[]; + @Input() + artifactid: string; + @Input() + resourceid: string; + + columns: ColumnDefinition[]; + originColumns: ColumnDefinition[]; + rows: any[]; + originRows: any[]; + isLoading: boolean; + ready: boolean; + columnsFilters: Map<string, string>; + addNewColumn: boolean; + + constructor(private gabService: GabService) { + } - ngOnInit() { - this.ready = false; - this.isLoading = true; - this.columns = []; - this.columnsFilters = new Map<string,string>(); - let paths: string[] = this.pathsandnames.map(item => item.path); - this.gabService.getArtifact(this.artifactid, this.resourceid, paths) - .subscribe( - response => { - let typedServerResponse:IServerResponse = <IServerResponse>response.json(); - this.normalizeDataForNgxDatatable(typedServerResponse.data); - }, - err => console.log(err), - () => { - this.ready = true; - this.isLoading = false; - } - ); - } + ngOnInit() { + this.ready = false; + this.isLoading = true; + this.columns = []; + this.loadArtifacts(); + } + + loadArtifacts() { + this.addNewColumn = false; + this.columnsFilters = new Map<string, string>(); + let paths: string[] = this.pathsandnames.map(item => item.path); + this.gabService.getArtifact(this.artifactid, this.resourceid, paths) + .subscribe( + response => { + let typedServerResponse: IServerResponse = response.json() as IServerResponse; + this.normalizeDataForNgxDatatable(typedServerResponse.data); + }, + () => { + this.ready = false; + this.isLoading = false; + }, + () => { + this.ready = true; + this.isLoading = false; + } + ); + } + + refresh() { + this.loadArtifacts(); + } + + canBeDeleted(name: string){ + return this.originColumns.filter(function(column){ + return column.name === name; + }).length === 0; + } + + deleteColumn(col: ColumnDefinition) { + this.pathsandnames = this.pathsandnames.filter(function(pathandname){ + return pathandname.friendlyName != col.name; + }); + this.columns = this.columns.filter(function(column){ + return column != col; + }) + } + + hideAddNewColumn() { + this.addNewColumn = false; + } + + showAddNewColumn() { + this.addNewColumn = true; + } updateColumnFilter(event, column) { const val = event.target.value.toLowerCase(); @@ -83,68 +119,73 @@ export class GenericArtifactBrowserComponent { } private updateSingleColumnFilter(value, column, rows) { - return rows.filter(function(obj) { + return rows.filter(function (obj) { const row = obj[column]; return row !== undefined && row.toLowerCase().indexOf(value) !== -1 || !value; }); } - private normalizeDataForNgxDatatable(data: [{ [key: string]: string }]) { - let result: NormalizationResult = this.getNormalizationResult(data, this.pathsandnames); - this.rows = result.rows; - this.originRows = result.rows; - this.columns = result.columns; + private normalizeDataForNgxDatatable(data: [{ [key: string]: string }]) { + let result: NormalizationResult = this.getNormalizationResult(data, this.pathsandnames); + this.rows = result.rows; + this.originRows = result.rows; + this.columns = result.columns; + if (!this.originColumns){ + this.originColumns = [...result.columns]; } + } - private getNormalizationResult(data: [{ [key: string]: string }], - pathsAndNames: PathsAndNamesDefinition[]): NormalizationResult { - //Prepare column names and column data property names - let mappingsPathToProp = new Map<string,string>(); - let columnsDefinitions = this.normalizeColumns(pathsAndNames, mappingsPathToProp); + private getNormalizationResult(data: [{ [key: string]: string }], + pathsAndNames: PathsAndNamesDefinition[]): NormalizationResult { + //Prepare column names and column data property names + let mappingsPathToProp = new Map<string, string>(); + let columnsDefinitions = this.normalizeColumns(pathsAndNames, mappingsPathToProp); - //Convert rows from { "string": "string" } to { prop : "string" } format - //This is required by NgxDatatable component - let arrayOfRows = this.normalizeRows(data, mappingsPathToProp); + //Convert rows from { "string": "string" } to { prop : "string" } format + //This is required by NgxDatatable component + let arrayOfRows = this.normalizeRows(data, mappingsPathToProp); - return new NormalizationResult(arrayOfRows, columnsDefinitions); - } + return new NormalizationResult(arrayOfRows, columnsDefinitions); + } - private normalizeColumns(pathsAndNames: PathsAndNamesDefinition[], mappingsPathToProp: Map<string,string>) { - let columnsDefinitions: ColumnDefinition[] = []; - let index: number = 1; - - pathsAndNames.forEach(function (col) { - let columnDataPropertyName: string = COLUMN_PREFIX + index; - mappingsPathToProp.set(col.path, columnDataPropertyName); - let cell: ColumnDefinition = new ColumnDefinition(col.friendlyName, columnDataPropertyName); - columnsDefinitions.push(cell); - index += 1; - }); - return columnsDefinitions; - } + private normalizeColumns(pathsAndNames: PathsAndNamesDefinition[], mappingsPathToProp: Map<string, string>) { + let columnsDefinitions: ColumnDefinition[] = []; + let index: number = 1; - private normalizeRows(data: [{ [key: string]: string }], mappingsPathToProp: Map<string,string>) { - let arrayOfRows = []; - data.forEach(function (col) { - let row = {}; - for (let key in col) { - if (col.hasOwnProperty(key)) { - let columnNameAsProp = mappingsPathToProp.get(key); - row[columnNameAsProp] = col[key]; - } - } - arrayOfRows.push(row); - }); - - return arrayOfRows; - } + pathsAndNames.forEach(function (col) { + let columnDataPropertyName: string = COLUMN_PREFIX + index; + mappingsPathToProp.set(col.path, columnDataPropertyName); + let cell: ColumnDefinition = new ColumnDefinition(col.friendlyName, columnDataPropertyName); + columnsDefinitions.push(cell); + index += 1; + }); + return columnsDefinitions; + } + + private normalizeRows(data: [{ [key: string]: string }], mappingsPathToProp: Map<string, string>) { + let arrayOfRows = []; + data.forEach(function (col) { + let row = {}; + for (let key in col) { + if (col.hasOwnProperty(key)) { + let columnNameAsProp = mappingsPathToProp.get(key); + row[columnNameAsProp] = col[key]; + } + } + arrayOfRows.push(row); + }); + + return arrayOfRows; + } } class NormalizationResult { - constructor(public rows: any[], public columns: ColumnDefinition[]) {} + constructor(public rows: any[], public columns: ColumnDefinition[]) { + } } export class ColumnDefinition { - constructor(public name: string, public prop: string) {} + constructor(public name: string, public prop: string) { + } } diff --git a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts index afb20472f1..e3ccf3027b 100644 --- a/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts +++ b/catalog-ui/src/app/ng2/components/logic/generic-artifact-browser/generic-artifact-browser.module.ts @@ -22,17 +22,22 @@ import {GenericArtifactBrowserComponent} from "./generic-artifact-browser.compon import {NgxDatatableModule} from "@swimlane/ngx-datatable"; import {GabService} from "../../../services/gab.service"; import {BrowserModule} from "@angular/platform-browser"; +import {GenericArtifactBrowserColumnProviderComponent} from "./generic-artifact-browser-column-provider.component"; +import {FormsModule} from "@angular/forms"; @NgModule({ declarations: [ - GenericArtifactBrowserComponent + GenericArtifactBrowserComponent, + GenericArtifactBrowserColumnProviderComponent ], imports: [ BrowserModule, + FormsModule, NgxDatatableModule ], entryComponents: [ //need to add anything that will be dynamically created - GenericArtifactBrowserComponent + GenericArtifactBrowserComponent, + GenericArtifactBrowserColumnProviderComponent ], exports: [], providers: [GabService] |