Added in panel support - details panels
Change-Id: I2803edd6fe12cb0d97a2d3c45a692ea701786dd2
diff --git a/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts b/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts
new file mode 100644
index 0000000..d7cf7b8
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/detailspanel.base.ts
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+import { FnService } from '../util/fn.service';
+import { LoadingService } from '../layer/loading.service';
+import { LogService } from '../../log.service';
+import { WebSocketService } from '../remote/websocket.service';
+
+import { PanelBaseImpl } from './panel.base';
+
+/**
+ * A generic model of the data returned from the *DetailsResponse
+ */
+interface DetailsResponse {
+ details: any;
+}
+
+/**
+ * Extends the PanelBase abstract class specifically for showing details
+ *
+ * This makes another call through WSS to the server for specific
+ * details to fill the panel with
+ *
+ * This replaces the detailspanel service in the old gui
+ */
+export abstract class DetailsPanelBaseImpl extends PanelBaseImpl {
+
+ private root: string;
+ private req: string;
+ private resp: string;
+ private handlers: string[] = [];
+ public detailsData: any = {};
+ public closed: boolean = false;
+
+ constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ protected wss: WebSocketService,
+ protected tag: string,
+ ) {
+ super(fs, ls, log, wss, {});
+ this.root = tag + 's';
+ this.req = tag + 'DetailsRequest';
+ this.resp = tag + 'DetailsResponse';
+ }
+
+ /**
+ * When the details panel is created set up a listener on
+ * Web Socket for details responses
+ */
+ init() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ [this.resp, (data) => this.detailsPanelResponseCb(data)]
+ ]));
+ this.handlers.push(this.resp);
+ }
+
+ /**
+ * When the details panel is destroyed this should be called to
+ * de-register from the WebSocket
+ */
+ destroy() {
+ this.wss.unbindHandlers(this.handlers);
+ }
+
+ /**
+ * A callback that executes when the details data that was requested
+ * on WebSocketService arrives.
+ */
+ detailsPanelResponseCb(data: DetailsResponse) {
+ this.detailsData = data['details'];
+ }
+
+ /**
+ * Details Panel Data Request - should be called whenever id changes
+ * If id is empty, no request is made
+ */
+ requestDetailsPanelData(id: string) {
+ if (id === '') {
+ return;
+ }
+ this.closed = false;
+ const query = {'id': id};
+
+ // Do not send if the Web Socket hasn't opened
+ if (this.wss.isConnected()) {
+ if (this.fs.debugOn('panel')) {
+ this.log.debug('Details panel data REQUEST:', this.req, query);
+ }
+ this.wss.sendEvent(this.req, query);
+ }
+ }
+
+ /**
+ * this should be called when the details panel close button is clicked
+ */
+ close(): void {
+ this.closed = true;
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/panel-theme.css b/web/gui2/src/main/webapp/app/fw/widget/panel-theme.css
new file mode 100644
index 0000000..6b984ab
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/panel-theme.css
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/*
+ ONOS GUI -- Panel Service (theme) -- CSS file
+ */
+
+.light .floatpanel {
+ background-color: white;
+ color: #3c3a3a;
+ border: 1px solid #c7c7c0;
+}
+
+.light .floatpanel hr {
+ border: 1px solid #c7c7c0;
+}
+
+.light .floatpanel .bottom tr:nth-child(odd) {
+ background-color: #f4f4f4;
+}
+
+.light .floatpanel .bottom tr:nth-child(even) {
+ background-color: #fbfbfb;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark .floatpanel {
+ background-color: #282528;
+ color: #888c8c;
+ border: 1px solid #364144;
+}
+
+.dark .floatpanel th {
+ background-color: #242424;
+}
+
+.dark .floatpanel h2 {
+ color: #dddddd;
+}
+
+.dark .floatpanel hr {
+ border: 1px solid #30303a;
+}
+
+.dark .floatpanel .bottom tr:nth-child(odd) {
+ background-color: #333333;
+}
+
+.dark .floatpanel .bottom tr:nth-child(even) {
+ background-color: #3a3a3a;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/panel.base.ts b/web/gui2/src/main/webapp/app/fw/widget/panel.base.ts
new file mode 100644
index 0000000..703cbf9
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/panel.base.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+import { FnService } from '../util/fn.service';
+import { LoadingService } from '../layer/loading.service';
+import { LogService } from '../../log.service';
+import { WebSocketService } from '../remote/websocket.service';
+
+
+const noop = (): any => undefined;
+
+/**
+ ********* Static functions *********
+ */
+function margin(p) {
+ return p.settings.margin;
+}
+
+function hideMargin(p) {
+ return p.settings.hideMargin;
+}
+
+function noPx(p, what) {
+ return Number(p.el.style(what).replace(/px$/, ''));
+}
+
+function widthVal(p) {
+ return noPx(p, 'width');
+}
+
+function heightVal(p) {
+ return noPx(p, 'height');
+}
+
+function pxShow(p) {
+ return margin(p) + 'px';
+}
+
+function pxHide(p) {
+ return (-hideMargin(p) - widthVal(p) - (noPx(p, 'padding') * 2)) + 'px';
+}
+
+
+/**
+ * Base model of panel view - implemented by Panel components
+ */
+export interface PanelBase {
+ showPanel(cb: any): void;
+ hidePanel(cb: any): void;
+ togglePanel(cb: any): void;
+ emptyPanel(): void;
+ appendPanel(what: any): void;
+ panelWidth(w: number): number;
+ panelHeight(h: number): number;
+ panelBBox(): string;
+ panelIsVisible(): boolean;
+ classed(cls: any, bool: boolean): boolean;
+ panelEl(): any;
+}
+
+/**
+ * ONOS GUI -- Widget -- Panel Base class
+ *
+ * Replacing the panel service in the old implementation
+ */
+export abstract class PanelBaseImpl implements PanelBase {
+
+ protected on: boolean;
+ protected el: any;
+
+ constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ protected wss: WebSocketService,
+ protected settings: any
+ ) {
+// this.log.debug('Panel base class constructed');
+ }
+
+ showPanel(cb) {
+ const endCb = this.fs.isF(cb) || noop;
+ this.on = true;
+ this.el.transition().duration(this.settings.xtnTime)
+ .each('end', endCb)
+ .style(this.settings.edge, pxShow(this))
+ .style('opacity', 1);
+ }
+
+ hidePanel(cb) {
+ const endCb = this.fs.isF(cb) || noop;
+ const endOpacity = this.settings.fade ? 0 : 1;
+ this.on = false;
+ this.el.transition().duration(this.settings.xtnTime)
+ .each('end', endCb)
+ .style(this.settings.edge, pxHide(this))
+ .style('opacity', endOpacity);
+ }
+
+ togglePanel(cb): boolean {
+ if (this.on) {
+ this.hidePanel(cb);
+ } else {
+ this.showPanel(cb);
+ }
+ return this.on;
+ }
+
+ emptyPanel(): string {
+ return this.el.text('');
+ }
+
+ appendPanel(what) {
+ return this.el.append(what);
+ }
+
+ panelWidth(w: number): number {
+ if (w === undefined) {
+ return widthVal(this);
+ }
+ this.el.style('width', w + 'px');
+ }
+
+ panelHeight(h: number): number {
+ if (h === undefined) {
+ return heightVal(this);
+ }
+ this.el.style('height', h + 'px');
+ }
+
+ panelBBox(): string {
+ return this.el.node().getBoundingClientRect();
+ }
+
+ panelIsVisible(): boolean {
+ return this.on;
+ }
+
+ classed(cls, bool): boolean {
+ return this.el.classed(cls, bool);
+ }
+
+ panelEl() {
+ return this.el;
+ }
+
+
+ /**
+ * A dummy implementation of the lionFn until the response is received and the LION
+ * bundle is received from the WebSocket
+ */
+ dummyLion(key: string): string {
+ return '%' + key + '%';
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/panel.css b/web/gui2/src/main/webapp/app/fw/widget/panel.css
new file mode 100644
index 0000000..34d127f
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/panel.css
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * 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.
+ */
+
+/*
+ ONOS GUI -- Panel Service (layout) -- CSS file
+ */
+
+.floatpanel {
+ position: absolute;
+ z-index: 100;
+ display: block;
+ top: 120px;
+ width: 500px;
+ right: -505px;
+ opacity: 100;
+
+ padding: 2px;
+ font-size: 10pt;
+}
+
+/* The following 4 are copied here from Theme until we sort out the
+ * theme service
+ */
+.floatpanel {
+ background-color: white;
+ color: #3c3a3a;
+ border: 1px solid #c7c7c0;
+}
+
+.floatpanel hr {
+ border: 1px solid #c7c7c0;
+}
+
+.floatpanel .bottom tr:nth-child(odd) {
+ background-color: #f4f4f4;
+}
+
+.floatpanel .bottom tr:nth-child(even) {
+ background-color: #fbfbfb;
+}
+
+.floatpanel.dialog {
+ top: 180px;
+}
+
+html[data-platform='iPad'] .floatpanel {
+ top: 80px;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts b/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
similarity index 60%
rename from web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
rename to web/gui2/src/main/webapp/app/fw/widget/table.base.ts
index 807a014..0093f72 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.base.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-present Open Networking Foundation
+ * Copyright 2018-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,39 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
import { FnService } from '../util/fn.service';
import { LoadingService } from '../layer/loading.service';
import { LogService } from '../../log.service';
import { WebSocketService } from '../remote/websocket.service';
+import { Observable, of } from 'rxjs';
const REFRESH_INTERVAL = 2000;
+const SEARCH_REGEX = '\\W';
/**
- * Base model of table view - implemented by Table components
+ * Model of table annotations within this table base class
*/
-export interface TableBase {
- annots: TableAnnots;
- autoRefresh: boolean;
- autoRefreshTip: string;
- changedData: any;
- payloadParams: any;
- selId: string;
- sortParams: any;
- tableData: any[];
- toggleRefresh(): void;
- selectCallback(event: any, selRow: any): void;
- parentSelCb(event: any, selRow: any): void;
- sortCallback(): void;
- responseCallback(): void;
-}
-
interface TableAnnots {
noRowsMsg: string;
}
/**
- * A model of data returned in a TableResponse
+ * A model of data returned from Web Socket in a TableResponse
*
* There is an interface extending from this one in the parent component
*/
@@ -56,23 +41,50 @@
}
/**
+ * A criteria for filtering the tableData
+ */
+export interface TableFilter {
+ queryStr: string;
+ queryBy: string;
+ sortBy: string;
+}
+
+/**
+ * Enumerated values for the sort dir
+ */
+export enum SortDir {
+ asc = 'asc', desc = 'desc'
+}
+
+/**
+ * A structure to format sort params for table
+ * This is sent to WebSocket as part of table request
+ */
+export interface SortParams {
+ firstCol: string;
+ firstDir: SortDir;
+ secondCol: string;
+ secondDir: SortDir;
+}
+
+/**
* ONOS GUI -- Widget -- Table Base class
*/
-export class TableBaseImpl implements TableBase {
+export abstract class TableBaseImpl {
// attributes from the interface
- public annots: TableAnnots;
+ protected annots: TableAnnots;
+ protected changedData: string[] = [];
+ protected payloadParams: any;
+ protected sortParams: SortParams;
+ protected selectCallback; // Function
+ protected parentSelCb = null;
+ protected responseCallback; // Function
+ selId: string = undefined;
+ tableData: any[] = [];
+ tableDataFilter: TableFilter;
+ toggleRefresh; // Function
autoRefresh: boolean = true;
autoRefreshTip: string = 'Toggle auto refresh'; // TODO: get LION string
- changedData: string[] = [];
- payloadParams: any;
- selId: string = undefined;
- sortParams: any;
- tableData: any[] = [];
- toggleRefresh; // Function
- selectCallback; // Function
- parentSelCb = null;
- sortCallback; // Function
- responseCallback; // Function
private root: string;
private req: string;
@@ -87,19 +99,24 @@
protected wss: WebSocketService,
protected tag: string,
protected idKey: string = 'id',
- protected query: string = '',
protected selCb = () => ({}) // Function
) {
this.root = tag + 's';
this.req = tag + 'DataRequest';
this.resp = tag + 'DataResponse';
- this.sortCallback = this.requestTableData;
this.selectCallback = this.rowSelectionCb;
this.toggleRefresh = () => {
this.autoRefresh = !this.autoRefresh;
this.autoRefresh ? this.startRefresh() : this.stopRefresh();
};
+
+ // Mapped to the search and searchBy inputs in template
+ // Changes are handled through TableFilterPipe
+ this.tableDataFilter = <TableFilter>{
+ queryStr: '',
+ queryBy: '$',
+ };
}
init() {
@@ -115,8 +132,8 @@
// Now send the WebSocket request and make it repeat every 2 seconds
this.requestTableData();
this.startRefresh();
-
- this.log.debug('TableBase initialized');
+ this.log.debug('TableBase initialized. Calling ', this.req,
+ 'every', REFRESH_INTERVAL, 'ms');
}
destroy() {
@@ -137,7 +154,7 @@
const newTableData: any[] = Array.from(data[this.root]);
this.annots.noRowsMsg = data.annots.no_rows_msg;
- // If the onResp() function is set then call it
+ // If the parents onResp() function is set then call it
if (this.responseCallback) {
this.responseCallback(data);
}
@@ -161,9 +178,12 @@
/**
* Table Data Request
+ * Pass in sort parameters and the set will be returned sorted
+ * In the old GUI there was also a query parameter, but this was not
+ * implemented on the server end
*/
requestTableData() {
- const p = Object.assign({}, this.sortParams, this.payloadParams, this.query);
+ const p = Object.assign({}, this.sortParams, this.payloadParams);
// Allow it to sit in pending events
if (this.wss.isConnected()) {
@@ -178,11 +198,11 @@
/**
* Row Selected
*/
- rowSelectionCb(event: any, selRow: any) {
+ rowSelectionCb(event: any, selRow: any): void {
const selId: string = selRow[this.idKey];
this.selId = (this.selId === selId) ? undefined : selId;
+ this.log.debug('Row', selId, 'selected');
if (this.parentSelCb) {
- this.log.debug('Parent called on Row', selId, 'selected');
this.parentSelCb(event, selRow);
}
}
@@ -212,4 +232,54 @@
isChanged(id: string): boolean {
return (this.fs.inArray(id, this.changedData) === -1) ? false : true;
}
+
+ /**
+ * A dummy implementation of the lionFn until the response is received and the LION
+ * bundle is received from the WebSocket
+ */
+ dummyLion(key: string): string {
+ return '%' + key + '%';
+ }
+
+ /**
+ * Change the sort order of the data returned
+ *
+ * sortParams are passed to the server by WebSocket and the data is
+ * returned sorted
+ *
+ * This is usually assigned to the (click) event on a column, and the column
+ * name passed in e.g. (click)="onSort('origin')
+ * If the column that is passed in is already the firstCol, then reverse its direction
+ * If a new column is passed in, then make the existing col the 2nd sort order
+ */
+ onSort(colName: string) {
+ if (this.sortParams.firstCol === colName) {
+ if (this.sortParams.firstDir === SortDir.desc) {
+ this.sortParams.firstDir = SortDir.asc;
+ return;
+ } else {
+ this.sortParams.firstDir = SortDir.desc;
+ return;
+ }
+ } else {
+ this.sortParams.secondCol = this.sortParams.firstCol;
+ this.sortParams.secondDir = this.sortParams.firstDir;
+ this.sortParams.firstCol = colName;
+ this.sortParams.firstDir = SortDir.desc;
+ }
+ this.log.debug('Sort params', this.sortParams);
+ this.requestTableData();
+ }
+
+ sortIcon(column: string): string {
+ if (this.sortParams.firstCol === column) {
+ if (this.sortParams.firstDir === SortDir.asc) {
+ return 'upArrow';
+ } else {
+ return 'downArrow';
+ }
+ } else {
+ return '';
+ }
+ }
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table-theme.css b/web/gui2/src/main/webapp/app/fw/widget/table.theme.css
similarity index 100%
rename from web/gui2/src/main/webapp/app/fw/widget/table-theme.css
rename to web/gui2/src/main/webapp/app/fw/widget/table.theme.css
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tabledetail.service.ts b/web/gui2/src/main/webapp/app/fw/widget/tabledetail.service.ts
deleted file mode 100644
index 76a5764..0000000
--- a/web/gui2/src/main/webapp/app/fw/widget/tabledetail.service.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.
- */
-import { Injectable } from '@angular/core';
-import { FnService } from '../util/fn.service';
-import { LogService } from '../../log.service';
-
-/**
- * ONOS GUI -- Widget -- Table Detail Panel Service
- */
-@Injectable()
-export class TableDetailService {
-
- constructor(
- private fs: FnService,
- private log: LogService,
- ) {
- this.log.debug('TableDetailService constructed');
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.spec.ts b/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.spec.ts
new file mode 100644
index 0000000..8832feb
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.spec.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+import { TableFilterPipe } from './tablefilter.pipe';
+import { TableFilter } from './table.base';
+
+describe('TableFilterPipe', () => {
+
+ const pipe = new TableFilterPipe();
+ const items: any[] = new Array();
+ // Array item 0
+ items.push({
+ id: 'abc',
+ title: 'def',
+ origin: 'ghi'
+ });
+ // Array item 1
+ items.push({
+ id: 'pqr',
+ title: 'stu',
+ origin: 'vwx'
+ });
+ // Array item 2
+ items.push({
+ id: 'dog',
+ title: 'mouse',
+ origin: 'cat'
+ });
+
+
+ it('create an instance', () => {
+ expect(pipe).toBeTruthy();
+ });
+
+ it('expect it to handle empty search', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: '', queryBy: 'title'});
+ expect(filteredItems).toEqual(items);
+ });
+
+ it('expect it to handle empty items', () => {
+ const filteredItems: any[] =
+ pipe.transform(new Array(), <TableFilter>{queryStr: 'de', queryBy: 'title'});
+ expect(filteredItems).toEqual(new Array());
+ });
+
+
+ it('expect it to match 0 by title', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'de', queryBy: 'title'});
+ expect(filteredItems).toEqual(items.slice(0, 1));
+ });
+
+ it('expect it to match 1 by title', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'st', queryBy: 'title'});
+ expect(filteredItems).toEqual(items.slice(1, 2));
+ });
+
+ it('expect it to match 1 by uppercase title', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'sT', queryBy: 'title'});
+ expect(filteredItems).toEqual(items.slice(1, 2));
+ });
+
+ it('expect it to not match by title', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'pq', queryBy: 'title'});
+ expect(filteredItems.length).toEqual(0);
+ });
+
+ it('expect it to match 1 by all fields', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'pq', queryBy: '$'});
+ expect(filteredItems).toEqual(items.slice(1, 2));
+ });
+
+ it('expect it to not match by all fields', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 'yz', queryBy: '$'});
+ expect(filteredItems.length).toEqual(0);
+ });
+
+ /**
+ * Check that items one and two contain a 't' - title=stu and origin=cat
+ */
+ it('expect it to match 1,2 by all fields', () => {
+ const filteredItems: any[] =
+ pipe.transform(items, <TableFilter>{queryStr: 't', queryBy: '$'});
+ expect(filteredItems).toEqual(items.slice(1));
+ });
+});
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.ts b/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.ts
new file mode 100644
index 0000000..5ef048c
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/tablefilter.pipe.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+import { TableFilter } from './table.base';
+
+/**
+ * Only return the tabledata that matches filtering with some queries
+ *
+ * Note: the pipe is marked pure here as we need to filter on the
+ * content of the filter object (it's not a primitive type)
+ */
+@Pipe({
+ name: 'filter',
+ pure: false
+})
+export class TableFilterPipe implements PipeTransform {
+
+ /**
+ * From an array of table items just return those that match the filter
+ */
+ transform(items: any[], tableDataFilter: TableFilter): any[] {
+ if (!items) {
+ return [];
+ }
+ if (!tableDataFilter.queryStr) {
+ return items;
+ }
+
+ const queryStr = tableDataFilter.queryStr.toLowerCase();
+
+ return items.filter( it => {
+ if (tableDataFilter.queryBy === '$') {
+ const t1 = Object.values(it);
+ const t2 = Object.values(it).filter(value => {
+ return (<string>value).toLowerCase().includes(queryStr);
+ });
+ return Object.values(it).filter(value => {
+ return (<string>value).toLowerCase().includes(queryStr);
+ }).length > 0;
+ } else {
+ return it[tableDataFilter.queryBy].toLowerCase().includes(queryStr);
+ }
+ });
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/toolbar.service.ts b/web/gui2/src/main/webapp/app/fw/widget/toolbar.service.ts
index 3681c80..6c1cb94 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/toolbar.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/toolbar.service.ts
@@ -19,7 +19,6 @@
import { FnService } from '../util/fn.service';
import { IconService } from '../svg/icon.service';
import { LogService } from '../../log.service';
-import { PanelService } from '../layer/panel.service';
/**
* ONOS GUI -- Widget -- Toolbar Service
@@ -34,7 +33,6 @@
private bns: ButtonService,
private is: IconService,
private log: LogService,
- private ps: PanelService,
) {
this.log.debug('ToolbarService constructed');
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
index 50d013c..4a09032 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
@@ -21,11 +21,11 @@
import { ButtonService } from './button.service';
import { ChartBuilderService } from './chartbuilder.service';
import { ListService } from './list.service';
-import { TableDetailService } from './tabledetail.service';
import { ToolbarService } from './toolbar.service';
import { SortableHeaderDirective } from './sortableheader.directive';
import { TableResizeDirective } from './tableresize.directive';
import { FlashChangesDirective } from './flashchanges.directive';
+import { TableFilterPipe } from './tablefilter.pipe';
/**
* ONOS GUI -- Widgets Module
@@ -39,14 +39,13 @@
declarations: [
SortableHeaderDirective,
TableResizeDirective,
- FlashChangesDirective
+ FlashChangesDirective,
+ TableFilterPipe
+ ],
+ exports: [
+ TableFilterPipe
],
providers: [
- ButtonService,
- ChartBuilderService,
- ListService,
- TableDetailService,
- ToolbarService
]
})
export class WidgetModule { }