mirror of
https://github.com/ceph/ceph
synced 2025-02-21 01:47:25 +00:00
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:
commit
2bf2013b01
@ -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);
|
||||
});
|
||||
|
@ -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 () => {
|
||||
|
Loading…
Reference in New Issue
Block a user