Merge pull request #20807 from Devp00l/improve-table-search

mgr/dashboard: Improve table search

Reviewed-by: Tiago Melo <tmelo@suse.com>
Reviewed-by: Ricardo Marques <rimarques@suse.com>
Reviewed-by: Ricardo Dias <rdias@suse.com>
This commit is contained in:
Lenz Grimmer 2018-04-09 17:43:46 +02:00 committed by GitHub
commit 2bf2013b01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 113 additions and 26 deletions

View 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,58 @@ 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 test search manipulation', () => {
let searchTerms = [];
spyOn(component, 'subSearch').and.callFake((d, search, c) => {
expect(search).toEqual(searchTerms);
});
const searchTest = (s: string, st: string[]) => {
component.search = s;
searchTerms = st;
component.updateFilter(true);
};
searchTest('a b c', [ 'a', 'b', 'c' ]);
searchTest('a+b c', [ 'a+b', 'c' ]);
searchTest('a,,,, b,,, c', [ 'a', 'b', 'c' ]);
searchTest('a,,,+++b,,, c', [ 'a+++b', 'c' ]);
searchTest('"a b c" "d e f", "g, h i"', [ 'a+b+c', 'd+e++f', 'g+h+i' ]);
});
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 search with spaces', () => {
doSearch('\'poker array\':score21', 6, {a: 15, b: 225, c: [-5, 'score21']});
doSearch('"poker array":score21', 6, {a: 15, b: 225, c: [-5, 'score21']});
doSearch('poker+array:score21', 6, {a: 15, b: 225, c: [-5, 'score21']});
});
it('should not search if column name is incomplete', () => {
doSearch('\'poker array\'', 100, {a: 0, b: 0, c: [-0, 'score6']});
doSearch('pok', 100, {a: 0, b: 0, c: [-0, 'score6']});
doSearch('pok:', 100, {a: 0, b: 0, c: [-0, 'score6']});
});
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);
});

View File

@ -23,6 +23,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';
@ -256,27 +257,67 @@ 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;
let search = this.search.toLowerCase().replace(/,/g, '');
const columns = this.columns.filter(c => c.cellTransformation !== CellTemplate.sparkline);
if (search.match(/['"][^'"]+['"]/)) {
search = search.replace(/['"][^'"]+['"]/g, (match: string) => {
return match.replace(/(['"])([^'"]+)(['"])/g, '$2').replace(/ /g, '+');
});
}
// 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, search.split(' ').filter(s => s.length > 0), columns);
// Whenever the filter changes, always go back to the first page
this.table.offset = 0;
}
subSearch (data: any[], currentSearch: string[], columns: CdTableColumn[]) {
if (currentSearch.length === 0 || data.length === 0) {
return data;
}
const searchTerms: string[] = currentSearch.pop().replace('+', ' ').split(':');
const columnsClone = [...columns];
const dataClone = [...data];
const filterColumns = (columnName: string) =>
columnsClone.filter((c) => c.name.toLowerCase().indexOf(columnName) !== -1);
if (searchTerms.length === 2) {
columns = filterColumns(searchTerms[0]);
}
const searchTerm: string = _.last(searchTerms);
data = this.basicDataSearch(searchTerm, data, columns);
// Checks if user searches for column but he is still typing
if (data.length === 0 && searchTerms.length === 1 && filterColumns(searchTerm).length > 0) {
data = dataClone;
}
return this.subSearch(data, currentSearch, columnsClone);
}
basicDataSearch(searchTerm: string, data: any[], columns: CdTableColumn[]) {
if (searchTerm.length === 0) {
return data;
}
return data.filter(d => {
return columns.filter(c => {
let cellValue: any = _.get(d, c.prop);
if (!_.isUndefined(c.pipe)) {
cellValue = c.pipe.transform(cellValue);
}
if (_.isUndefined(cellValue)) {
return;
}
if (_.isArray(cellValue)) {
cellValue = cellValue.join(' ');
} else if (_.isNumber(cellValue)) {
cellValue = cellValue.toString();
}
return cellValue.toLowerCase().indexOf(searchTerm) !== -1;
}).length > 0;
});
}
getRowClass() {
// Return the function used to populate a row's CSS classes.
return () => {