]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Improve table search
authorStephan Müller <smueller@suse.com>
Thu, 22 Feb 2018 11:47:59 +0000 (12:47 +0100)
committerStephan Müller <smueller@suse.com>
Fri, 6 Apr 2018 14:46:39 +0000 (16:46 +0200)
You can now search for multiple words at a time if you separate them by
comma or space. The other improvement is that you can specify which
column should be searched by a search term. For example if you wand to
filter for a row with the ID 3 you type "id:3". The column name is case
insensitive and is separated from the search word by a colon.

Signed-off-by: Stephan Müller <smueller@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts

index 60ec7d013279b52e515d3814167005050ab66dc8..8340dc6adf1fec65059dd254e982fd60039183b4 100644 (file)
@@ -11,18 +11,26 @@ describe('TableComponent', () => {
   let component: TableComponent;
   let fixture: ComponentFixture<TableComponent>;
   const columns: TableColumn[] = [];
+
   const createFakeData = (n) => {
     const data = [];
     for (let i = 0; i < n; i++) {
       data.push({
         a: i,
         b: i * i,
-        c: -(i % 10)
+        c: [-(i % 10), 'score' + (i % 16 + 6) ]
       });
     }
     return data;
   };
 
+  const doSearch = (search: string, expectedLength: number, firstObject: object) => {
+    component.search = search;
+    component.updateFilter(true);
+    expect(component.rows.length).toBe(expectedLength);
+    expect(component.rows[0]).toEqual(firstObject);
+  };
+
   beforeEach(
     async(() => {
       TestBed.configureTestingModule({
@@ -41,9 +49,9 @@ describe('TableComponent', () => {
     component.data = createFakeData(100);
     component.useData();
     component.columns = [
-      {prop: 'a'},
-      {prop: 'b'},
-      {prop: 'c'}
+      {prop: 'a', name: 'Index'},
+      {prop: 'b', name: 'Power ofA'},
+      {prop: 'c', name: 'Poker array'}
     ];
   });
 
@@ -70,20 +78,29 @@ describe('TableComponent', () => {
   });
 
   it('should search for 13', () => {
-    component.search = '13';
-    expect(component.rows.length).toBe(100);
-    component.updateFilter(true);
-    expect(component.rows[0].a).toBe(13);
-    expect(component.rows[1].b).toBe(1369);
-    expect(component.rows[2].b).toBe(3136);
-    expect(component.rows.length).toBe(3);
+    doSearch('13', 9, {a: 7, b: 49, c: [ -7, 'score13'] });
+    expect(component.rows[1].a).toBe(13);
+    expect(component.rows[8].a).toBe(87);
+  });
+
+  it('should search for multiple values', () => {
+    doSearch('7 5 3', 5, {a: 57, b: 3249, c: [ -7, 'score15']});
+  });
+
+  it('should search with column filter', () => {
+    doSearch('power:1369', 1, {a: 37, b: 1369, c: [ -7, 'score11']});
+    doSearch('ndex:7 ofa:5 poker:3', 3, {a: 71, b: 5041, c: [-1, 'score13']});
+  });
+
+  it('should search with through array', () => {
+    doSearch('array:score21', 6, {a: 15, b: 225, c: [-5, 'score21']});
   });
 
   it('should restore full table after search', () => {
-    component.search = '13';
     expect(component.rows.length).toBe(100);
+    component.search = '13';
     component.updateFilter(true);
-    expect(component.rows.length).toBe(3);
+    expect(component.rows.length).toBe(9);
     component.updateFilter();
     expect(component.rows.length).toBe(100);
   });
index 9f04e91c71ca748e96fda8003ed54f9243b3aa87..efda07a9180718ffefa128318e8802003f838e10 100644 (file)
@@ -22,6 +22,7 @@ import * as _ from 'lodash';
 import 'rxjs/add/observable/timer';
 import { Observable } from 'rxjs/Observable';
 
+import { CellTemplate } from '../../enum/cell-template.enum';
 import { CdTableColumn } from '../../models/cd-table-column';
 import { CdTableSelection } from '../../models/cd-table-selection';
 
@@ -250,27 +251,50 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O
     ];
   }
 
-  updateFilter(event?) {
+  updateFilter(event?: any) {
     if (!event) {
       this.search = '';
     }
-    const val = this.search.toLowerCase();
-    const columns = this.columns;
+    const columns = this.columns.filter(c => c.cellTransformation !== CellTemplate.sparkline);
     // update the rows
-    this.rows = this.data.filter((d) => {
-      return (
-        columns.filter(c => {
-          return (
-            (_.isString(d[c.prop]) || _.isNumber(d[c.prop])) &&
-            (d[c.prop] + '').toLowerCase().indexOf(val) !== -1
-          );
-        }).length > 0
-      );
-    });
+    this.rows = this.subSearch(this.data, this.search.toLowerCase().split(/[, ]/), columns);
     // Whenever the filter changes, always go back to the first page
     this.table.offset = 0;
   }
 
+  subSearch (data: any[], currentSearch: string[], columns: CdTableColumn[]) {
+    let tempColumns: CdTableColumn[];
+    if (currentSearch.length === 0 || data.length === 0) {
+      return data;
+    }
+    const searchWords: string[] = currentSearch.pop().split(':');
+    if (searchWords.length === 2) {
+      tempColumns = [...columns];
+      columns = columns.filter((c) => c.name.toLowerCase().indexOf(searchWords[0]) !== -1);
+    }
+    const searchWord: string = _.last(searchWords);
+    if (searchWord.length > 0) {
+      data = data.filter(d => {
+        return columns.filter(c => {
+          let cellValue: any = _.get(d, c.prop);
+          if (_.isUndefined(cellValue)) {
+            return;
+          }
+          if (_.isArray(cellValue)) {
+            cellValue = cellValue.join('');
+          } else if (_.isNumber(cellValue)) {
+            cellValue = cellValue.toString();
+          }
+          return cellValue.toLowerCase().indexOf(searchWord) !== -1;
+        }).length > 0;
+      });
+    }
+    if (_.isArray(tempColumns)) {
+      columns = tempColumns;
+    }
+    return this.subSearch(data, currentSearch, columns);
+  }
+
   getRowClass() {
     // Return the function used to populate a row's CSS classes.
     return () => {