From: Stephan Müller Date: Thu, 19 Sep 2019 14:04:34 +0000 (+0200) Subject: mgr/dashboard: Searchable objects for table X-Git-Tag: v14.2.10~187^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a9b0d5bd3e7d8e61dbffa436ddf378cda7926645;p=ceph.git mgr/dashboard: Searchable objects for table The table can now search through objects, by default it won't search through them, but it won't fail like before. The RBD list page is now capable of searching through objects, which only exist on an RBD that was cloned from a snapshot. Fixes: https://tracker.ceph.com/issues/42480 Signed-off-by: Stephan Müller (cherry picked from commit 31ea95946497afa6a46a6322ef48dea9729134d4) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html index af73500de78..ccd95925353 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html @@ -7,6 +7,7 @@ columnMode="flex" [columns]="columns" identifier="id" + [searchableObjects]="true" forceIdentifier="true" selectionType="single" (updateSelection)="updateSelection($event)"> diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts index 780192b0b9e..44299f00ace 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts @@ -77,14 +77,6 @@ describe('TableComponent', () => { it('should have an int in setLimit parsing a string', () => { expect(component.limit).toBe(10); expect(component.limit).toEqual(jasmine.any(Number)); - - const e = { target: { value: '1' } }; - component.setLimit(e); - expect(component.userConfig.limit).toBe(1); - expect(component.userConfig.limit).toEqual(jasmine.any(Number)); - e.target.value = '-20'; - component.setLimit(e); - expect(component.userConfig.limit).toBe(1); }); it('should prevent propagation of mouseenter event', (done) => { @@ -111,85 +103,122 @@ describe('TableComponent', () => { expect(component.sorts[0].prop).toBe('a'); expect(component.sorts).toEqual(component.createSortingDefinition('a')); }); + }); - describe('test search', () => { - const doSearch = (search: string, expectedLength: number, firstObject?: object) => { - component.search = search; - component.updateFilter(); - expect(component.rows.length).toBe(expectedLength); - if (firstObject) { - expect(component.rows[0]).toEqual(firstObject); + describe('test search', () => { + const doSearch = (search: string, expectedLength: number, firstObject?: object) => { + component.search = search; + component.updateFilter(); + expect(component.rows.length).toBe(expectedLength); + if (firstObject) { + expect(component.rows[0]).toEqual(firstObject); + } + }; + + describe('searchableObjects', () => { + const testObject = { + obj: { + min: 8, + max: 123 } }; - it('should search for 13', () => { - doSearch('13', 9, { a: 7, b: 49, c: [-7, 'score13'], d: false }); - expect(component.rows[1].a).toBe(13); - expect(component.rows[8].a).toBe(87); + beforeEach(() => { + component.data = [testObject]; + component.columns = [{ prop: 'obj', name: 'Object' }]; }); - it('should search for true', () => { - doSearch('true', 50, { a: 0, b: 0, c: [-0, 'score6'], d: true }); - expect(component.rows[0].d).toBe(true); - expect(component.rows[1].d).toBe(true); + it('should not search through objects as default case', () => { + expect(component.searchableObjects).toBe(false); + doSearch('8', 0); }); - it('should search for false', () => { - doSearch('false', 50, { a: 1, b: 1, c: [-1, 'score7'], d: false }); - expect(component.rows[0].d).toBe(false); - expect(component.rows[1].d).toBe(false); + it('should search through objects if searchableObjects is set to true', () => { + component.searchableObjects = true; + doSearch('28', 0); + doSearch('8', 1, testObject); + doSearch('123', 1, testObject); + doSearch('max', 1, testObject); }); + }); - it('should test search manipulation', () => { - let searchTerms = []; - spyOn(component, 'subSearch').and.callFake((_d, search) => { - expect(search).toEqual(searchTerms); - }); - const searchTest = (s: string, st: string[]) => { - component.search = s; - searchTerms = st; - component.updateFilter(); - }; - 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 find a particular number', () => { + doSearch('5', 37, { a: 5, b: 25, c: [-5, 'score11'], d: false }); + // { a: 3, b: 9, c: [ -3, 'score9' ], d: false }, + // { a: 7, b: 49, c: [ -7, 'score13' ], d: false }, + // { a: 9, b: 81, c: [ -9, 'score15' ], d: false }, - it('should search for multiple values', () => { - doSearch('7 5 3', 5, { a: 57, b: 3249, c: [-7, 'score15'], d: false }); - }); + doSearch('9', 50, { a: 3, b: 9, c: [-3, 'score9'], d: false }); + }); - it('should search with column filter', () => { - doSearch('power:1369', 1, { a: 37, b: 1369, c: [-7, 'score11'], d: false }); - doSearch('ndex:7 ofa:5 poker:3', 3, { a: 71, b: 5041, c: [-1, 'score13'], d: false }); - }); + it('should search for 13', () => { + doSearch('13', 9, { a: 7, b: 49, c: [-7, 'score13'], d: false }); + expect(component.rows[1].a).toBe(13); + expect(component.rows[8].a).toBe(87); + }); - it('should search with through array', () => { - doSearch('array:score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); - }); + it('should search for true', () => { + doSearch('true', 50, { a: 0, b: 0, c: [-0, 'score6'], d: true }); + expect(component.rows[0].d).toBe(true); + expect(component.rows[1].d).toBe(true); + }); - it('should search with spaces', () => { - doSearch(`'poker array':score21`, 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); - doSearch('"poker array":score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); - doSearch('poker+array:score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); - }); + it('should search for false', () => { + doSearch('false', 50, { a: 1, b: 1, c: [-1, 'score7'], d: false }); + expect(component.rows[0].d).toBe(false); + expect(component.rows[1].d).toBe(false); + }); - it('should search if column name is incomplete', () => { - doSearch(`'poker array'`, 0); - doSearch('pok', 0); - doSearch('pok:', 100); + it('should test search manipulation', () => { + let searchTerms = []; + spyOn(component, 'subSearch').and.callFake((_d, search) => { + expect(search).toEqual(searchTerms); }); - - it('should restore full table after search', () => { - expect(component.rows.length).toBe(100); - component.search = '13'; + const searchTest = (s: string, st: string[]) => { + component.search = s; + searchTerms = st; component.updateFilter(); - expect(component.rows.length).toBe(9); - component.updateFilter(true); - expect(component.rows.length).toBe(100); - }); + }; + 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'], d: false }); + }); + + it('should search with column filter', () => { + doSearch('power:1369', 1, { a: 37, b: 1369, c: [-7, 'score11'], d: false }); + doSearch('ndex:7 ofa:5 poker:3', 3, { a: 71, b: 5041, c: [-1, 'score13'], d: false }); + }); + + it('should search with through array', () => { + doSearch('array:score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); + }); + + it('should search with spaces', () => { + doSearch(`'poker array':score21`, 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); + doSearch('"poker array":score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); + doSearch('poker+array:score21', 6, { a: 15, b: 225, c: [-5, 'score21'], d: false }); + }); + + it('should search if column name is incomplete', () => { + doSearch(`'poker array'`, 0); + doSearch('pok', 0); + doSearch('pok:', 100); + }); + + it('should restore full table after search', () => { + component.updateFilter(); + expect(component.rows.length).toBe(100); + component.search = '13'; + component.updateFilter(); + expect(component.rows.length).toBe(9); + component.updateFilter(true); + expect(component.rows.length).toBe(100); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts index 0fc351b610a..fa29c91094a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts @@ -105,6 +105,10 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O @Input() autoSave = true; + // Enable this in order to search through the JSON of any used object. + @Input() + searchableObjects = false; + // Only needed to set if the classAddingTpl is used @Input() customCss?: { [css: string]: number | string | ((any) => boolean) }; @@ -537,6 +541,15 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O } else if (_.isNumber(cellValue) || _.isBoolean(cellValue)) { cellValue = cellValue.toString(); } + + if (_.isObjectLike(cellValue)) { + if (this.searchableObjects) { + cellValue = JSON.stringify(cellValue); + } else { + return false; + } + } + return cellValue.toLowerCase().indexOf(searchTerm) !== -1; }).length > 0 );