GUI2 implementation of device/flow/port/group/meter/host/link/tunnel view

Review comments incorporated.

Change-Id: I45dd6570961cc3e0f4ffddb7acbf02cd7d860de5
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps.module.ts b/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
index 738ee18..0e79a2b 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps.module.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.
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
index 3096bae..636fb0c 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
@@ -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.
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
index 943e26b..37826fa 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
@@ -36,24 +36,24 @@
             <div class="separator"></div>
 
             <div class="active" (click)="triggerForm()">
-                <onos-icon classes="{{ 'active upload' }}"
+                <onos-icon classes="{{ 'active-rect upload' }}"
                            iconId="upload" iconSize="42" toolTip="{{ uploadTip }}"></onos-icon>
             </div>
 
-            <div (click)="confirmAction(AppActionEnum.ACTIVATE)">
-                <onos-icon classes="{{ ctrlBtnState.installed?'active play':'play' }}"
+            <div (click)="(!!selId) ? confirmAction(AppActionEnum.ACTIVATE) : ''">
+                <onos-icon classes="{{ ctrlBtnState.installed?'active-rect play':'play' }}"
                            iconId="play" iconSize="42" toolTip="{{ activateTip }}"></onos-icon>
             </div>
-            <div (click)="confirmAction(AppActionEnum.DEACTIVATE)">
-                <onos-icon classes="{{ ctrlBtnState.active?'active stop':'stop' }}"
+            <div (click)="(!!selId) ? confirmAction(AppActionEnum.DEACTIVATE) : ''">
+                <onos-icon classes="{{ ctrlBtnState.active?'active-rect stop':'stop' }}"
                         iconId="stop" iconSize="42" toolTip="{{ deactivateTip }}"></onos-icon>
             </div>
-            <div (click)="confirmAction(AppActionEnum.UNINSTALL)">
-                <onos-icon classes="{{ ctrlBtnState.selection?'active garbage':'garbage' }}"
+            <div (click)="(!!selId) ? confirmAction(AppActionEnum.UNINSTALL) : ''">
+                <onos-icon classes="{{ ctrlBtnState.selection?'active-rect garbage':'garbage' }}"
                         iconId="garbage" iconSize="42" toolTip="{{ uninstallTip }}"></onos-icon>
             </div>
-            <div (click)="downloadApp()">
-                <onos-icon classes="{{ ctrlBtnState.selection?'active download':'download' }}"
+            <div (click)="(!!selId) ? downloadApp() : ''">
+                <onos-icon classes="{{ ctrlBtnState.selection?'active-rect download':'download' }}"
                         iconId="download" iconSize="42" toolTip="{{ downloadTip }}"></onos-icon>
             </div>
         </div>
@@ -74,34 +74,34 @@
 
     </div>
 
-    <div id="summary-list" class="summary-list">
+    <div id="summary-list" class="summary-list" onosTableResize>
         <div class="table-header">
-            <table onosTableResize>
+            <table>
                 <tr>
                     <th colId="state" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('state')">
-                        <onos-icon classes="active" [iconId]="sortIcon('state')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('state')"></onos-icon>
                     </th>
                     <th colId="icon" [ngStyle]="{width: '32px'}" class="table-icon"></th>
                     <th colId="title"  (click)="onSort('title')">{{lionFn('title')}}
-                        <onos-icon classes="active" [iconId]="sortIcon('title')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('title')"></onos-icon>
                     </th>
                     <th colId="id" (click)="onSort('id')">{{lionFn('app_id')}}
-                        <onos-icon classes="active" [iconId]="sortIcon('id')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('id')"></onos-icon>
                     </th>
                     <th colId="version" (click)="onSort('version')"> {{lionFn('version')}}
-                        <onos-icon classes="active" [iconId]="sortIcon('version')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('version')"></onos-icon>
                     </th>
                     <th colId="category" (click)="onSort('category')"> {{lionFn('category')}}
-                        <onos-icon classes="active" [iconId]="sortIcon('category')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('category')"></onos-icon>
                     </th>
                     <th colId="origin" (click)="onSort('origin')"> {{lionFn('origin')}}
-                        <onos-icon classes="active" [iconId]="sortIcon('origin')"></onos-icon>
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('origin')"></onos-icon>
                     </th>
                 </tr>
             </table>
         </div>
         <div class="table-body">
-            <table onosTableResize>
+            <table>
                 <tr *ngIf="tableData.length === 0" class="no-data">
                     <td colspan="5">
                         {{annots.no_rows_msg}}
@@ -137,6 +137,6 @@
      The advantage in 2) is that panel can be animated in and out, as it is not
      killed every time the selection changes.
      -->
-    <onos-appsdetails  class="floatpanels" id="{{ selId }}"></onos-appsdetails>
+    <onos-appsdetails class="floatpanels" id="{{ selId }}" (closeEvent)="deselectRow($event)"></onos-appsdetails>
 
 </div>
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
index cddf9ae..e022ebc 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
@@ -38,6 +38,7 @@
 import { UrlFnService } from '../../../fw/remote/urlfn.service';
 import { WebSocketService } from '../../../fw/remote/websocket.service';
 import { of } from 'rxjs';
+import { } from 'jasmine';
 
 class MockActivatedRoute extends ActivatedRoute {
     constructor(params: Params) {
@@ -46,33 +47,33 @@
     }
 }
 
-class MockDialogService {}
+class MockDialogService { }
 
-class MockFnService {}
+class MockFnService { }
 
 class MockHttpClient {}
 
 class MockIconService {
-    loadIconDef() {}
+    loadIconDef() { }
 }
 
-class MockKeyService {}
+class MockKeyService { }
 
 class MockLoadingService {
-    startAnim() {}
-    stop() {}
-    waiting() {}
+    startAnim() { }
+    stop() { }
+    waiting() { }
 }
 
-class MockThemeService {}
+class MockThemeService { }
 
-class MockUrlFnService {}
+class MockUrlFnService { }
 
 class MockWebSocketService {
-    createWebSocket() {}
+    createWebSocket() { }
     isConnected() { return false; }
-    unbindHandlers() {}
-    bindHandlers() {}
+    unbindHandlers() { }
+    bindHandlers() { }
 }
 
 /**
@@ -90,21 +91,21 @@
             test: 'test1'
         }
     };
-    const mockLion = (key) =>  {
+    const mockLion = (key) => {
         return bundleObj[key] || '%' + key + '%';
     };
 
     beforeEach(async(() => {
         const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
-        ar = new MockActivatedRoute({'debug': 'txrx'});
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
 
         windowMock = <any>{
-            location: <any> {
+            location: <any>{
                 hostname: 'foo',
                 host: 'foo',
                 port: '80',
                 protocol: 'http',
-                search: { debug: 'true'},
+                search: { debug: 'true' },
                 href: 'ws://foo:123/onos/ui/websock/path',
                 absUrl: 'ws://foo:123/onos/ui/websock/path'
             }
@@ -127,7 +128,8 @@
                 { provide: HttpClient, useClass: MockHttpClient },
                 { provide: IconService, useClass: MockIconService },
                 { provide: KeyService, useClass: MockKeyService },
-                { provide: LionService, useFactory: (() => {
+                {
+                    provide: LionService, useFactory: (() => {
                         return {
                             bundle: ((bundleId) => mockLion),
                             ubercache: new Array(),
@@ -143,7 +145,7 @@
                 { provide: 'Window', useValue: windowMock },
             ]
         })
-        .compileComponents();
+            .compileComponents();
         logServiceSpy = TestBed.get(LogService);
     }));
 
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
index ffd7b37..b2f6231 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
@@ -337,4 +337,13 @@
         evt.preventDefault();
         evt.stopPropagation();
     }
+
+    deselectRow(event) {
+        this.log.debug('Details panel close event');
+        this.selId = event;
+        this.ctrlBtnState = <CtrlBtnState>{
+            installed: undefined,
+            active: undefined
+        };
+    }
 }
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css b/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
index 13f1847..a705d45 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-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.
@@ -41,7 +41,6 @@
 
 #ov-app div.summary-list .table-body {
     overflow:scroll;
-    max-height:70vh;
 }
 #ov-app h2 {
     display: inline-block;
diff --git a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
index ce9af3a..90d4b14 100644
--- a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
+++ b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
@@ -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.
diff --git a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.html b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.html
index a78bdd9..d43c264 100644
--- a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.html
+++ b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.html
@@ -1,5 +1,5 @@
 <!--
-~ Copyright 2014-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.
diff --git a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.spec.ts b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.spec.ts
index f890cde..75f5f8e 100644
--- a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.spec.ts
@@ -21,10 +21,10 @@
 
 import { LogService } from '../../../log.service';
 import { AppsDetailsComponent } from './appsdetails.component';
-import { FnService } from '../../../../app/fw/util/fn.service';
-import { IconComponent } from '../../../../app/fw/svg/icon/icon.component';
-import { IconService } from '../../../../app/fw/svg/icon.service';
-import { LionService } from '../../../../app/fw/util/lion.service';
+import { FnService } from '../../../fw/util/fn.service';
+import { IconComponent } from '../../../fw/svg/icon/icon.component';
+import { IconService } from '../../../fw/svg/icon.service';
+import { LionService } from '../../../fw/util/lion.service';
 import { UrlFnService } from '../../../fw/remote/urlfn.service';
 import { WebSocketService } from '../../../fw/remote/websocket.service';
 import { of } from 'rxjs';
diff --git a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.ts b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.ts
index 1e6ed29..c0d2e02 100644
--- a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.ts
@@ -101,8 +101,20 @@
         this.log.debug('App Details Component destroyed');
     }
 
+    /**
+     * Details Panel Data Request on row selection changes
+     * Should be called whenever id changes
+     * If id is empty, no request is made
+     */
     ngOnChanges() {
-        this.requestDetailsPanelData(this.id);
+        if (this.id === '') {
+            return '';
+        } else {
+            const query = {
+                'id': this.id
+            };
+            this.requestDetailsPanelData(query);
+        }
     }
 
     iconUrl(appId: string): string {