Implemented table building functions
Change-Id: Ie4003080b13725561df22de41ec85f8c3f31c794
diff --git a/web/gui2/src/main/webapp/app/consolelogger.service.ts b/web/gui2/src/main/webapp/app/consolelogger.service.ts
index 1ba88f9..84c9d03 100644
--- a/web/gui2/src/main/webapp/app/consolelogger.service.ts
+++ b/web/gui2/src/main/webapp/app/consolelogger.service.ts
@@ -17,9 +17,7 @@
import { environment } from '../environments/environment';
import { Logger } from './log.service';
-export let isDebugMode = environment.isDebugMode;
-
-const noop = (): any => undefined;
+export let isDebugMode: boolean = !environment.production;
/**
* ONOS GUI -- LogService
@@ -27,12 +25,13 @@
*/
@Injectable()
export class ConsoleLoggerService implements Logger {
+ private noop: () => void;
get debug() {
if (isDebugMode) {
return console.debug.bind(console);
} else {
- return noop;
+ return this.noop;
}
}
@@ -40,7 +39,7 @@
if (isDebugMode) {
return console.info.bind(console);
} else {
- return noop;
+ return this.noop;
}
}
@@ -53,7 +52,7 @@
}
invokeConsoleMethod(type: string, args?: any): void {
- const logFn: Function = (console)[type] || console.log || noop;
+ const logFn: Function = (console)[type] || console.log || this.noop;
logFn.apply(console, [args]);
}
}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts b/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
index 880e2bd..d6c37bb 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
@@ -38,4 +38,7 @@
this.log.debug('DialogService constructed');
}
+ createDiv() {
+ }
+
}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts b/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
index 0d03ad6..3e6d48b 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
@@ -33,7 +33,9 @@
*
* Provides a mechanism to start/stop the loading animation, center screen.
*/
-@Injectable()
+@Injectable({
+ providedIn: 'root',
+})
export class LoadingService {
images: any[] = [];
idx = 0;
@@ -127,7 +129,7 @@
}
// return true if start() has been called but not stop()
- waiting() {
+ waiting(): boolean {
return !!this.wait;
}
diff --git a/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts b/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
index e470ec1..34a8ea9 100644
--- a/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
@@ -90,7 +90,6 @@
* built-in handler for the 'boostrap' event
*/
private bootstrap(data: Bootstrap) {
- this.log.debug('bootstrap data', data);
this.loggedInUser = data.user;
this.clusterNodes = data.clusterNodes;
@@ -332,6 +331,13 @@
return this.url;
}
+ /**
+ * Tell the WebSocket to close - this should call the handleClose() method
+ */
+ closeWebSocket() {
+ this.ws.close();
+ }
+
/**
* Binds the message handlers to their message type (event type) as
@@ -369,12 +375,12 @@
* Unbinds the specified message handlers.
* Expected that the same map will be used, but we only care about keys
*/
- unbindHandlers(handlerMap: Map<string, (data) => void>): void {
- if (this.noHandlersWarn(handlerMap, 'unbindHandlers')) {
+ unbindHandlers(handlerIds: string[]): void {
+ if ( handlerIds.length === 0 ) {
+ this.log.warn('WSS.unbindHandlers(): no event handlers');
return null;
}
-
- for (const [eventId, api] of handlerMap) {
+ for (const eventId of handlerIds) {
this.handlers.delete(eventId);
}
}
@@ -448,4 +454,7 @@
this.lcd = ld;
}
+ isConnected(): boolean {
+ return this.wsUp;
+ }
}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts b/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts
deleted file mode 100644
index a8de561..0000000
--- a/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-import { Directive, ElementRef, Input, OnInit } from '@angular/core';
-import { IconService } from './icon.service';
-import { LogService } from '../../log.service';
-import * as d3 from 'd3';
-
-/**
- * ONOS GUI -- SVG -- Icon Directive
- *
- * TODO: Deprecated - this directive may be removed altogether as it has been
- * rebuilt as IconComponent instead
- */
-@Directive({
- selector: '[onosIcon]'
-})
-export class IconDirective implements OnInit {
- @Input() iconId: string;
- @Input() iconSize = 20;
-
- constructor(
- private el: ElementRef,
- private is: IconService,
- private log: LogService
- ) {
- // Note: iconId is not available until initialization
- this.log.debug('IconDirective constructed');
- }
-
- ngOnInit() {
- const div = d3.select(this.el.nativeElement);
- div.selectAll('*').remove();
- this.is.loadEmbeddedIcon(div, this.iconId, this.iconSize);
- this.log.debug('IconDirective initialized:', this.iconId);
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
index 6a32d87..5be4c19 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
@@ -13,9 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50">
- <g class="icon" [ngClass]="iconId">
+<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50" (mouseover)="toolTipDisp = toolTip" (mouseout)="toolTipDisp = undefined">
+ <g class="icon" [ngClass]="classes">
<rect width="50" height="50" rx="5"></rect>
<use width="50" height="50" class="glyph" [attr.href]="iconTag()"></use>
</g>
</svg>
+<!-- I'm fixing class as light as view encapsulation changes how the hirerarchy of css is handled -->
+<p id="tooltip" class="light" *ngIf="toolTip" [ngStyle]="{ 'display': toolTipDisp ? 'inline-block':'none' }">{{ toolTipDisp }}</p>
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
index 45be81f..c851f47 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
@@ -16,7 +16,6 @@
import { Component, OnInit, Input } from '@angular/core';
import { IconService, glyphMapping } from '../icon.service';
import { LogService } from '../../../log.service';
-import * as d3 from 'd3';
/**
* Icon Component
@@ -31,11 +30,20 @@
@Component({
selector: 'onos-icon',
templateUrl: './icon.component.html',
- styleUrls: ['./icon.component.css', './icon.theme.css', './glyph.css', './glyph-theme.css']
+ styleUrls: [
+ './icon.component.css', './icon.theme.css',
+ './glyph.css', './glyph-theme.css',
+ './tooltip.css', './tooltip-theme.css'
+ ]
})
export class IconComponent implements OnInit {
@Input() iconId: string;
- @Input() iconSize = 20;
+ @Input() iconSize: number = 20;
+ @Input() toolTip: string = undefined;
+ @Input() classes: string = undefined;
+
+ // The displayed tooltip - undefined until mouse hovers over, then equals toolTip
+ toolTipDisp: string = undefined;
constructor(
private is: IconService,
@@ -47,7 +55,6 @@
ngOnInit() {
this.is.loadIconDef(this.iconId);
- this.log.debug('IconComponent initialized for ', this.iconId);
}
/**
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
index 59cf10f..3e6f601 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
@@ -40,6 +40,46 @@
fill: #3c3a3a;
}
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+svg.embeddedIcon g.icon use {
+ fill: #e0dfd6;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+svg.embeddedIcon g.icon.active use {
+ fill: #939598;
+}
+svg.embeddedIcon g.icon.active:hover use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+svg.embeddedIcon g.icon.current-view rect {
+ fill: #518ecc;
+}
+svg.embeddedIcon g.icon.current-view use {
+ fill: white;
+}
+
+/* REFRESH */
+svg.embeddedIcon g.icon.refresh use {
+ fill: #cdeff2;
+}
+svg.embeddedIcon g.icon.refresh:hover use {
+ fill: #ce5b58;
+}
+svg.embeddedIcon g.icon.refresh.active use {
+ fill: #009fdb;
+}
+svg.embeddedIcon g.icon.refresh.active:hover use {
+ fill: #ce5b58;
+}
+
+
/* ========== DARK Theme ========== */
.dark div.close-btn svg.embeddedIcon g.icon .glyph {
@@ -61,11 +101,12 @@
.dark table svg.embeddedIcon .icon .glyph {
fill: #9999aa;
}
-
+/*
svg.embeddedIcon g.icon .glyph {
fill: #007dc4;
}
svg.embeddedIcon:hover g.icon .glyph {
fill: #20b2ff;
-}
\ No newline at end of file
+}
+*/
\ No newline at end of file
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css
new file mode 100644
index 0000000..a703d1b
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css
@@ -0,0 +1,30 @@
+/*
+ * 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 -- Tooltip Service (theme) -- CSS file
+ */
+.light#tooltip {
+ background-color: #dbeffc;
+ color: #3c3a3a;
+ border-color: #c7c7c0;
+}
+
+.dark#tooltip {
+ background-color: #3a3a60;
+ border-color: #6c6a6a;
+ color: #c7c7c0;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
similarity index 62%
rename from web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts
rename to web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
index 6b08779..74a5443 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
@@ -13,21 +13,18 @@
* 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 -- Tooltip Service
+/*
+ ONOS GUI -- Tooltip Service (layout) -- CSS file
*/
-@Injectable()
-export class TooltipService {
- constructor(
- private fs: FnService,
- private log: LogService,
- ) {
- this.log.debug('TooltipService constructed');
- }
-
+#tooltip {
+ text-align: center;
+ font-size: 80%;
+ border: 1px solid;
+ padding: 5px;
+ position: absolute;
+ z-index: 5000;
+ display: none;
+ pointer-events: none;
}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts b/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
index 9688c30..3174263 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
@@ -25,7 +25,6 @@
import { SpriteService } from './sprite.service';
import { SpriteDataService } from './spritedata.service';
import { SvgUtilService } from './svgutil.service';
-import { IconDirective } from './icon.directive';
import { IconComponent } from './icon/icon.component';
/**
@@ -33,7 +32,6 @@
*/
@NgModule({
exports: [
- IconDirective,
IconComponent
],
imports: [
@@ -41,7 +39,6 @@
UtilModule
],
declarations: [
- IconDirective,
IconComponent
],
providers: [
diff --git a/web/gui2/src/main/webapp/app/fw/util/fn.service.ts b/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
index d355ce9..7b9ea24 100644
--- a/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
@@ -415,6 +415,35 @@
}
/**
+ * returns true if the two objects have all the same properties
+ */
+ sameObjProps(obj1: Object, obj2: Object): boolean {
+ for (const key in obj1) {
+ if (obj1.hasOwnProperty(key)) {
+ if (!(obj1[key] === obj2[key])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * returns true if the array contains the object
+ * does NOT use strict object reference equality,
+ * instead checks each property individually for equality
+ */
+ containsObj(arr: any[], obj: Object): boolean {
+ const len = arr.length;
+ for (let i = 0; i < len; i++) {
+ if (this.sameObjProps(arr[i], obj)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Return the given string with the first character capitalized.
*/
cap(s: string): string {
diff --git a/web/gui2/src/main/webapp/app/fw/util/lion.service.ts b/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
index 5ab9f65..248fe51 100644
--- a/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
@@ -34,7 +34,8 @@
})
export class LionService {
- ubercache: any[];
+ ubercache: any[] = [];
+ loadCb; // Function
/**
* Handler for uberlion event from WSS
@@ -50,6 +51,10 @@
this.log.info(' :=> ', p);
}
}
+ if (this.loadCb) {
+ this.log.debug('Calling the load callback');
+ this.loadCb();
+ }
this.log.debug('LION service: uber-lion bundle received:', data);
}
@@ -69,17 +74,15 @@
* returns a function that takes a string and returns a string
*/
bundle(bundleId: string): (string) => string {
- let bundle = this.ubercache[bundleId];
+ let bundleObj = this.ubercache[bundleId];
- if (!bundle) {
+ if (!bundleObj) {
this.log.warn('No lion bundle registered:', bundleId);
- bundle = {};
+ bundleObj = {};
}
- return this.getKey;
- }
-
- getKey(key: string): string {
- return this.bundle[key] || '%' + key + '%';
+ return (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
}
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/button.service.ts b/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
index da6cbab..4e22763 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
@@ -17,7 +17,6 @@
import { FnService } from '../util/fn.service';
import { IconService } from '../svg/icon.service';
import { LogService } from '../../log.service';
-import { TooltipService } from './tooltip.service';
/**
* ONOS GUI -- Widget -- Button Service
@@ -29,7 +28,6 @@
private is: IconService,
private fs: FnService,
private log: LogService,
- private tts: TooltipService
) {
this.log.debug('ButtonService constructed');
}
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
new file mode 100644
index 0000000..1edfab0
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/table-theme.css
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/* ------ for summary-list tables (theme) ------ */
+
+.light div.summary-list, .table-header th {
+ background-color: #e5e5e6;
+ color: #3c3a3a;
+}
+
+.light div.summary-list, td {
+ color: #3c3a3a;
+}
+
+.light div.summary-list, tr:nth-child(even) {
+ background-color: #f4f4f4;
+}
+.light div.summary-list, tr:nth-child(odd) {
+ background-color: #fbfbfb;
+}
+
+.light div.summary-list, tr.selected {
+ background-color: #dbeffc !important;
+}
+
+
+.light div.summary-list, tr.data-change {
+ background-color: #FDFFDC;
+}
+
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+.light .ctrl-btns div svg.embeddedIcon g.icon use {
+ fill: #e0dfd6;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+.light .ctrl-btns div.active svg.embeddedIcon g.icon use {
+ fill: #939598;
+}
+.light .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+.light .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
+ fill: #518ecc;
+}
+.light .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
+ fill: white;
+}
+
+/* REFRESH */
+.light .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
+ fill: #cdeff2;
+}
+.light .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+.light .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
+ fill: #009fdb;
+}
+.light .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark div.summary-list .table-header td {
+ background-color: #222222;
+ color: #cccccc;
+}
+
+.dark div.summary-list td {
+ /* note: don't put background-color here */
+ color: #cccccc;
+}
+.dark div.summary-list tr.no-data td {
+ background-color: #333333;
+}
+
+.dark div.summary-list tr:nth-child(even) {
+ background-color: #333333;
+}
+.dark div.summary-list tr:nth-child(odd) {
+ background-color: #3a3a3a;
+}
+
+.dark div.summary-list tr.selected {
+ background-color: #304860 !important;
+}
+
+
+.dark div.summary-list tr.data-change {
+ background-color: #423708;
+}
+
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+.dark .ctrl-btns div svg.embeddedIcon g.icon use {
+ fill: #444444;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+.dark .ctrl-btns div.active svg.embeddedIcon g.icon use {
+ fill: #939598;
+}
+.dark .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
+ fill: #518ecc;
+}
+.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
+ fill: #dddddd;
+}
+
+/* REFRESH */
+.dark .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
+ fill: #364144;
+}
+.dark .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+.dark .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
+ fill: #0074a6;
+}
+.dark .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table.css b/web/gui2/src/main/webapp/app/fw/widget/table.css
new file mode 100644
index 0000000..3b761f6
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.css
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+/* ------ for summary-list tables (layout) ------ */
+
+div.summary-list {
+ margin: 0 20px 16px 10px;
+ font-size: 10pt;
+ border-spacing: 0;
+}
+
+div.summary-list table {
+ border-collapse: collapse;
+ table-layout: fixed;
+ empty-cells: show;
+ margin: 0;
+}
+
+div.summary-list div.table-body {
+ overflow-y: scroll;
+}
+
+div.summary-list div.table-body::-webkit-scrollbar {
+ display: none;
+}
+
+div.summary-list tr.no-data td {
+ text-align: center;
+ font-style: italic;
+}
+
+
+/* highlighting */
+div.summary-list tr {
+ transition: background-color 500ms;
+}
+
+div.summary-list td {
+ padding: 4px;
+ text-align: left;
+ word-wrap: break-word;
+ font-size: 10pt;
+}
+
+div.summary-list td.table-icon {
+ width: 42px;
+ padding-top: 4px;
+ padding-bottom: 0px;
+ padding-left: 4px;
+ text-align: center;
+}
+
+div.summary-list .table-header th {
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+ font-size: 10pt;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ letter-spacing: 0.02em;
+ cursor: pointer;
+}
+
+/* rows are selectable */
+div.summary-list .table-body td {
+ cursor: pointer;
+}
+
+/* Tabular view controls */
+
+div.tabular-header .search {
+ margin: 0 0 10px 10px;
+}
+
+
+div.tabular-header div.ctrl-btns {
+ display: inline-block;
+ float: right;
+ height: 44px;
+ margin-top: 24px;
+ margin-right: 20px;
+ position: absolute;
+ right: 0px;
+}
+
+div.tabular-header div.ctrl-btns div {
+ display: inline-block;
+ cursor: pointer;
+}
+
+div.tabular-header div.ctrl-btns div.separator {
+ width: 0;
+ height: 40px;
+ padding: 0;
+ border-right: 1px solid #c7c7c0;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts b/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
new file mode 100644
index 0000000..807a014
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+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';
+
+const REFRESH_INTERVAL = 2000;
+
+/**
+ * Base model of table view - implemented by Table components
+ */
+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
+ *
+ * There is an interface extending from this one in the parent component
+ */
+export interface TableResponse {
+ annots: any;
+ // There will be other parts to the response depending on table type
+ // Expect one called tag+'s' e.g. devices or apps
+}
+
+/**
+ * ONOS GUI -- Widget -- Table Base class
+ */
+export class TableBaseImpl implements TableBase {
+ // attributes from the interface
+ public annots: TableAnnots;
+ 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;
+ private resp: string;
+ private refreshPromise: any = null;
+ private handlers: string[] = [];
+
+ constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ 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();
+ };
+ }
+
+ init() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ [this.resp, (data) => this.tableDataResponseCb(data)]
+ ]));
+ this.handlers.push(this.resp);
+
+ this.annots = <TableAnnots>{
+ noRowsMsg: ''
+ };
+
+ // Now send the WebSocket request and make it repeat every 2 seconds
+ this.requestTableData();
+ this.startRefresh();
+
+ this.log.debug('TableBase initialized');
+ }
+
+ destroy() {
+ this.wss.unbindHandlers(this.handlers);
+ this.stopRefresh();
+ this.ls.stop();
+ }
+
+ /**
+ * A callback that executes when the table data that was requested
+ * on WebSocketService arrives.
+ *
+ * Happens every 2 seconds
+ */
+ tableDataResponseCb(data: TableResponse) {
+ this.ls.stop();
+
+ 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 (this.responseCallback) {
+ this.responseCallback(data);
+ }
+ this.changedData = [];
+
+ // checks if data changed for row flashing
+ if (JSON.stringify(newTableData) !== JSON.stringify(this.tableData)) {
+ this.log.debug('table data has changed');
+ const oldTableData: any[] = this.tableData;
+ this.tableData = [ ...newTableData ]; // ES6 spread syntax
+ // only flash the row if the data already exists
+ if (oldTableData.length > 0) {
+ for (const idx in newTableData) {
+ if (!this.fs.containsObj(oldTableData, newTableData[idx])) {
+ this.changedData.push(newTableData[idx][this.idKey]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Table Data Request
+ */
+ requestTableData() {
+ const p = Object.assign({}, this.sortParams, this.payloadParams, this.query);
+
+ // Allow it to sit in pending events
+ if (this.wss.isConnected()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Table data REQUEST:', this.req, p);
+ }
+ this.wss.sendEvent(this.req, p);
+ this.ls.start();
+ }
+ }
+
+ /**
+ * Row Selected
+ */
+ rowSelectionCb(event: any, selRow: any) {
+ const selId: string = selRow[this.idKey];
+ this.selId = (this.selId === selId) ? undefined : selId;
+ if (this.parentSelCb) {
+ this.log.debug('Parent called on Row', selId, 'selected');
+ this.parentSelCb(event, selRow);
+ }
+ }
+
+ /**
+ * autoRefresh functions
+ */
+ startRefresh() {
+ this.refreshPromise =
+ setInterval(() => {
+ if (!this.ls.waiting()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Refreshing ' + this.root + ' page');
+ }
+ this.requestTableData();
+ }
+ }, REFRESH_INTERVAL);
+ }
+
+ stopRefresh() {
+ if (this.refreshPromise) {
+ clearInterval(this.refreshPromise);
+ this.refreshPromise = null;
+ }
+ }
+
+ isChanged(id: string): boolean {
+ return (this.fs.inArray(id, this.changedData) === -1) ? false : true;
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts b/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts
deleted file mode 100644
index 6c804d1..0000000
--- a/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-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';
-
-/**
- * ONOS GUI -- Widget -- Table Builder Service
- */
-@Injectable()
-export class TableBuilderService {
-
- constructor(
- private fs: FnService,
- private ls: LoadingService,
- private log: LogService,
- private wss: WebSocketService
- ) {
- this.log.debug('TableBuilderService constructed');
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts b/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts
deleted file mode 100644
index 92f8ae3..0000000
--- a/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-import { Directive } from '@angular/core';
-import { FnService } from '../util/fn.service';
-import { LogService } from '../../log.service';
-
-/**
- * ONOS GUI -- Widget -- Tooltip Directive
- */
-@Directive({
- selector: '[onosTooltip]'
-})
-export class TooltipDirective {
-
- constructor(
- private fs: FnService,
- private log: LogService
- ) {
- this.log.debug('TooltipDirective 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 2e7a0ea..02393a9 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
@@ -23,11 +23,8 @@
import { ButtonService } from './button.service';
import { ChartBuilderService } from './chartbuilder.service';
import { ListService } from './list.service';
-import { TableBuilderService } from './tablebuilder.service';
import { TableDetailService } from './tabledetail.service';
import { ToolbarService } from './toolbar.service';
-import { TooltipService } from './tooltip.service';
-import { TooltipDirective } from './tooltip.directive';
import { SortableHeaderDirective } from './sortableheader.directive';
import { TableResizeDirective } from './tableresize.directive';
import { FlashChangesDirective } from './flashchanges.directive';
@@ -42,7 +39,6 @@
// It's enough to import them in the OnosModule
],
declarations: [
- TooltipDirective,
SortableHeaderDirective,
TableResizeDirective,
FlashChangesDirective
@@ -51,9 +47,7 @@
ButtonService,
ChartBuilderService,
ListService,
- TableBuilderService,
TableDetailService,
- TooltipService,
ToolbarService
]
})
diff --git a/web/gui2/src/main/webapp/app/onos.component.css b/web/gui2/src/main/webapp/app/onos.component.css
index 60933d8..e57f958 100644
--- a/web/gui2/src/main/webapp/app/onos.component.css
+++ b/web/gui2/src/main/webapp/app/onos.component.css
@@ -27,8 +27,4 @@
#view h2 {
-webkit-margin-before: 0;
-webkit-margin-after: 0;
- margin: 32px 0 4px 16px;
- padding: 0;
- font-size: 18pt;
- font-weight: lighter;
}
diff --git a/web/gui2/src/main/webapp/app/onos.component.html b/web/gui2/src/main/webapp/app/onos.component.html
index 42d2bdd..18297ed 100644
--- a/web/gui2/src/main/webapp/app/onos.component.html
+++ b/web/gui2/src/main/webapp/app/onos.component.html
@@ -1,4 +1,4 @@
-<div style="text-align:center" onosDetectBrowser>
+<div id="view" onosDetectBrowser>
<onos-mast></onos-mast>
<onos-nav></onos-nav>
<onos-veil #veil></onos-veil>
diff --git a/web/gui2/src/main/webapp/app/onos.component.ts b/web/gui2/src/main/webapp/app/onos.component.ts
index bbec955..1246314 100644
--- a/web/gui2/src/main/webapp/app/onos.component.ts
+++ b/web/gui2/src/main/webapp/app/onos.component.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
import { LionService } from './fw/util/lion.service';
import { LogService } from './log.service';
import { KeyService } from './fw/util/key.service';
@@ -62,7 +62,7 @@
templateUrl: './onos.component.html',
styleUrls: ['./onos.component.css', './onos.common.css']
})
-export class OnosComponent implements OnInit {
+export class OnosComponent implements OnInit, OnDestroy {
public title = 'onos';
// view ID to help page url map.. injected via the servlet
@@ -110,14 +110,19 @@
this.onos.viewMap = this.viewMap;
-// this.wss.createWebSocket({
-// wsport: Window.location.search().wsport
-// });
-
// TODO: Enable this this.saucy(this.ee, this.ks);
this.log.debug('OnosComponent initialized');
}
+ ngOnDestroy() {
+ if (this.wss.isConnected()) {
+ this.log.debug('Stopping Web Socket connection');
+ this.wss.closeWebSocket();
+ }
+
+ this.log.debug('OnosComponent destroyed');
+ }
+
saucy(ee, ks) {
const map = ee.genMap(sauce);
Object.keys(map).forEach(function (k) {
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps.component.html b/web/gui2/src/main/webapp/app/view/apps/apps.component.html
index 90f9a16..6397f63 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps.component.html
+++ b/web/gui2/src/main/webapp/app/view/apps/apps.component.html
@@ -1,3 +1,100 @@
-<div id="ov-app">
- <p>apps works!</p>
-</div>
\ No newline at end of file
+<div id="ov-app" filedrop on-file-drop="appDropped()">
+ <div class="tabular-header">
+ <h2>
+ {{lionFn('title_apps')}}
+ ({{ tableData.length }}
+ {{ lionFn('total') }})
+ </h2>
+ <div class="ctrl-btns">
+ <div class="refresh" (click)="toggleRefresh()">
+ <onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}"
+ iconId="refresh" iconSize="42" toolTip="{{ autoRefreshTip }}"></onos-icon>
+ </div>
+ <div class="separator"></div>
+
+ <!--<form id="inputFileForm">-->
+ <!--<input id="uploadFile"-->
+ <!--type="file" size="50" accept=".oar,.jar"-->
+ <!--file-model="appFile">-->
+ <!--</form>-->
+
+ <div class="active" trigger-form>
+ <onos-icon classes="{{ 'active upload' }}"
+ iconId="upload" iconSize="42" toolTip="{{ uploadTip }}"></onos-icon>
+ </div>
+ <div (click)="appAction('activate')">
+ <onos-icon classes="{{ ctrlBtnState.installed?'active play':'play' }}"
+ iconId="play" iconSize="42" toolTip="{{ activateTip }}"></onos-icon>
+ </div>
+ <div (click)="appAction('deactivate')">
+ <onos-icon classes="{{ ctrlBtnState.active?'active stop':'stop' }}"
+ iconId="stop" iconSize="42" toolTip="{{ deactivateTip }}"></onos-icon>
+ </div>
+ <div (click)="appAction('uninstall')">
+ <!--[ngClass]="{active: ctrlBtnState.selection}">-->
+ <!--tooltip tt-msg="uninstallTip"-->
+ <onos-icon classes="{{ ctrlBtnState.selection?'active garbage':'garbage' }}"
+ iconId="garbage" iconSize="42" toolTip="{{ uninstallTip }}"></onos-icon>
+ </div>
+ <div (click)="downloadApp()">
+ <onos-icon classes="{{ ctrlBtnState.selection?'active download':'download' }}"
+ iconId="download" iconSize="42" toolTip="{{ downloadTip }}"></onos-icon>
+ </div>
+ </div>
+
+ <!--<div class="search">-->
+ <!--<input type="text" ng-model="queryTxt" placeholder="Search"/>-->
+ <!--<select ng-model="queryBy">-->
+ <!--<option value="" disabled>Search By</option>-->
+ <!--<option value="$">All Fields</option>-->
+ <!--<option value="title">{{lionFn('title')}}</option>-->
+ <!--<option value="id">{{lionFn('app_id')}}</option>-->
+ <!--<option value="version">{{lionFn('version')}}</option>-->
+ <!--<option value="category">{{lionFn('category')}}</option>-->
+ <!--<option value="apporiginName">{{lionFn('origin')}}</option>-->
+
+ <!--</select>-->
+ <!--</div>-->
+
+
+ </div>
+
+ <div class="summary-list" onos-table-resize>
+ <table onos-flash-changes id-prop="id" width="100%">
+ <tr class="table-header">
+ <th colId="state" class="table-icon" sortable></th>
+ <th colId="icon" class="table-icon"></th>
+ <th colId="title" [ngClass]="{width: '340'}" sortable> {{lionFn('title')}} </th>
+ <th colId="id" [ngClass]="{width: '320px'}"sortable> {{lionFn('app_id')}} </th>
+ <th colId="version" [ngClass]="{width: '140px'}"sortable> {{lionFn('version')}} </th>
+ <th colId="category" [ngClass]="{width: '136px'}"sortable> {{lionFn('category')}} </th>
+ <th colId="origin" sortable> {{lionFn('origin')}} </th>
+ </tr>
+
+ <tr *ngIf="tableData.length === 0" class="no-data">
+ <td colspan="5">
+ {{annots.no_rows_msg}}
+ </td>
+ </tr>
+ <!--<!–TODO: Add back in | filter:queryFilter–>-->
+ <tr class="table-body" *ngFor="let app of tableData; trackBy $index"
+ (click)="selectCallback($event, app)"
+ [ngClass]="{selected: app.id === selId, 'data-change': isChanged(app.id)}">
+ <td class="table-icon">
+ <onos-icon iconId="{{app._iconid_state}}"></onos-icon>
+ </td>
+ <td class="table-icon">
+ <!--<img data-ng-src="./rs/applications/{{app.icon}}/icon"-->
+ <!--height="24px" width="24px" />-->
+ </td>
+ <td>{{ app.title }}</td>
+ <td>{{ app.id }}</td>
+ <td>{{ app.version }}</td>
+ <td>{{ app.category }}</td>
+ <td>{{ app.origin }}</td>
+ </tr>
+ </table>
+
+ </div>
+
+</div>
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps.component.ts b/web/gui2/src/main/webapp/app/view/apps/apps.component.ts
index 5eaa38f..b2bb38b 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps.component.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps.component.ts
@@ -13,46 +13,235 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
import { DialogService } from '../../fw/layer/dialog.service';
import { FnService } from '../../fw/util/fn.service';
import { IconService } from '../../fw/svg/icon.service';
import { KeyService } from '../../fw/util/key.service';
import { LionService } from '../../fw/util/lion.service';
+import { LoadingService } from '../../fw/layer/loading.service';
import { LogService } from '../../log.service';
import { PanelService } from '../../fw/layer/panel.service';
-import { TableBuilderService } from '../../fw/widget/tablebuilder.service';
+import { TableBaseImpl, TableResponse } from '../../fw/widget/tablebase';
import { UrlFnService } from '../../fw/remote/urlfn.service';
import { WebSocketService } from '../../fw/remote/websocket.service';
+const INSTALLED = 'INSTALLED';
+const ACTIVE = 'ACTIVE';
+const appMgmtReq = 'appManagementRequest';
+const topPdg = 60;
+const panelWidth = 540;
+const pName = 'application-details-panel';
+const detailsReq = 'appDetailsRequest';
+const detailsResp = 'appDetailsResponse';
+const fileUploadUrl = 'applications/upload';
+const activateOption = '?activate=true';
+const appUrlPrefix = 'rs/applications/';
+const iconUrlSuffix = '/icon';
+const downloadSuffix = '/download';
+const dialogId = 'app-dialog';
+const dialogOpts = {
+ edge: 'right',
+ width: 400,
+};
+const strongWarning = {
+ 'org.onosproject.drivers': true,
+};
+const propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'];
+
+interface AppTableResponse extends TableResponse {
+ apps: Apps[];
+}
+
+interface Apps {
+ category: string;
+ desc: string;
+ features: string;
+ icon: string;
+ id: string;
+ origin: string;
+ permissions: string;
+ readme: string;
+ required_apps: string;
+ role: string;
+ state: string;
+ title: string;
+ url: string;
+ version: string;
+ _iconid_state: string;
+}
+
+interface CtrlBtnState {
+ installed: boolean;
+ selection: string;
+ active: boolean;
+}
+
/**
* ONOS GUI -- Apps View Component
*/
@Component({
selector: 'onos-apps',
templateUrl: './apps.component.html',
- styleUrls: ['./apps.component.css']
+ styleUrls: [
+ './apps.component.css', './apps.theme.css',
+ '../../fw/widget/table.css', '../../fw/widget/table-theme.css'
+ ]
})
-export class AppsComponent implements OnInit {
+export class AppsComponent extends TableBaseImpl implements OnInit, OnDestroy {
+
+ // deferred localization strings
+ lionFn; // Function
+ warnDeactivate: string;
+ warnOwnRisk: string;
+ friendlyProps: string[];
+ ctrlBtnState: CtrlBtnState;
+ detailsPanel: any;
constructor(
- private fs: FnService,
+ protected fs: FnService,
private ds: DialogService,
private is: IconService,
private ks: KeyService,
- private ls: LionService,
- private log: LogService,
+ private lion: LionService,
+ protected ls: LoadingService,
+ protected log: LogService,
private ps: PanelService,
- private tbs: TableBuilderService,
private ufs: UrlFnService,
- private wss: WebSocketService,
- private window: Window
+ protected wss: WebSocketService,
+ private window: Window,
) {
- this.log.debug('AppsComponent constructed');
+ super(fs, null, log, wss, 'app');
+ this.responseCallback = this.appResponseCb;
+ this.sortParams = {
+ firstCol: 'state',
+ firstDir: 'desc',
+ secondCol: 'title',
+ secondDir: 'asc',
+ };
+ // We want doLion() to be called only after the Lion service is populated (from the WebSocket)
+ this.lion.loadCb = (() => this.doLion());
+ this.ctrlBtnState = <CtrlBtnState>{
+ installed: false,
+ active: false
+ };
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ } else {
+ this.doLion();
+ }
}
ngOnInit() {
- this.log.debug('AppsComponent initialized');
+ this.init();
+ this.log.debug('AppComponent initialized');
}
+ ngOnDestroy() {
+ this.destroy();
+ this.log.debug('AppComponent destroyed');
+ }
+
+ /**
+ * The callback called when App data returns from WSS
+ */
+ appResponseCb(data: AppTableResponse) {
+ this.log.debug('App response received for ', data.apps.length, 'apps');
+ }
+
+ refreshCtrls() {
+ let row;
+ let rowIdx;
+ if (this.ctrlBtnState.selection) {
+ rowIdx = this.fs.find(this.selId, this.tableData);
+ row = rowIdx >= 0 ? this.tableData[rowIdx] : null;
+
+ this.ctrlBtnState.installed = row && row.state === INSTALLED;
+ this.ctrlBtnState.active = row && row.state === ACTIVE;
+ } else {
+ this.ctrlBtnState.installed = false;
+ this.ctrlBtnState.active = false;
+ }
+ }
+
+ createConfirmationText(action, itemId) {
+// let content = this.ds.createDiv();
+// content.append('p').text(this.lionFn(action) + ' ' + itemId);
+// if (strongWarning[itemId]) {
+// content.append('p').html(
+// this.fs.sanitize(this.warnDeactivate) +
+// '<br>' +
+// this.fs.sanitize(this.warnOwnRisk)
+// ).classed('strong', true);
+// }
+// return content;
+ }
+
+ confirmAction(action): void {
+ const itemId = this.selId;
+ const spar = this.sortParams;
+
+ function dOk() {
+ this.log.debug('Initiating', action, 'of', itemId);
+ this.wss.sendEvent(appMgmtReq, {
+ action: action,
+ name: itemId,
+ sortCol: spar.sortCol,
+ sortDir: spar.sortDir,
+ });
+ if (action === 'uninstall') {
+ this.detailsPanel.hide();
+ } else {
+ this.wss.sendEvent(detailsReq, { id: itemId });
+ }
+ }
+
+ function dCancel() {
+ this.log.debug('Canceling', action, 'of', itemId);
+ }
+
+// this.ds.openDialog(dialogId, dialogOpts)
+// .setTitle(this.lionFn('dlg_confirm_action'))
+// .addContent(this.createConfirmationText(action, itemId))
+// .addOk(dOk)
+// .addCancel(dCancel)
+// .bindKeys();
+ }
+
+ appAction(action) {
+ if (this.ctrlBtnState.selection) {
+ this.confirmAction(action);
+ }
+ }
+
+ downloadApp() {
+ if (this.ctrlBtnState.selection) {
+ (<any>this.window).location = appUrlPrefix + this.selId + downloadSuffix;
+ }
+ }
+
+ /**
+ * Read the LION bundle for App - this should replace the dummyLion implementation
+ * of lionFn with a function from the LION Service
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.App');
+
+ this.warnDeactivate = this.lionFn('dlg_warn_deactivate');
+ this.warnOwnRisk = this.lionFn('dlg_warn_own_risk');
+
+ this.friendlyProps = [
+ this.lionFn('app_id'), this.lionFn('state'),
+ this.lionFn('category'), this.lionFn('version'),
+ this.lionFn('origin'), this.lionFn('role'),
+ ];
+ }
+
+ /**
+ * 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/view/apps/apps.module.ts b/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
index 3083b55..3e3d5c4 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
@@ -17,6 +17,7 @@
import { CommonModule } from '@angular/common';
import { AppsComponent } from './apps.component';
import { TriggerFormDirective } from './triggerform.directive';
+import { SvgModule } from '../../fw/svg/svg.module';
/**
* ONOS GUI -- Apps View Module
@@ -30,7 +31,8 @@
AppsComponent
],
imports: [
- CommonModule
+ CommonModule,
+ SvgModule
],
declarations: [
AppsComponent,
diff --git a/web/gui2/src/main/webapp/app/view/device/device.component.css b/web/gui2/src/main/webapp/app/view/device/device.component.css
index c578112..4d8454d 100644
--- a/web/gui2/src/main/webapp/app/view/device/device.component.css
+++ b/web/gui2/src/main/webapp/app/view/device/device.component.css
@@ -17,12 +17,15 @@
/*
ONOS GUI -- Device View (layout) -- CSS file
*/
+#ov-device .tabular-header {
+ text-align: left;
+}
#ov-device h2 {
display: inline-block;
}
-#ov-device div.ctrl-btns {
+#ov-device, div.ctrl-btns {
}
diff --git a/web/gui2/src/main/webapp/app/view/device/device.component.html b/web/gui2/src/main/webapp/app/view/device/device.component.html
index febc99a..e2d3fda 100644
--- a/web/gui2/src/main/webapp/app/view/device/device.component.html
+++ b/web/gui2/src/main/webapp/app/view/device/device.component.html
@@ -1,3 +1,99 @@
<div id="ov-device">
- <p>device works!</p>
+ <div class="tabular-header">
+ <h2>Devices ({{ tableData.length }} total)</h2>
+ <div class="ctrl-btns">
+ <div class="refresh" (click)="toggleRefresh()">
+ <!-- See icon.theme.css for the defintions of the classes active and refresh-->
+ <onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}"
+ iconId="refresh" iconSize="42" toolTip="{{ autoRefreshTip }}"></onos-icon>
+ </div>
+ <div class="separator"></div>
+
+ <div>
+ <onos-icon classes="{{ selId ? 'current-view':undefined }}"
+ iconId="deviceTable" iconSize="42"></onos-icon>
+ </div>
+
+ <div routerLink="/flow" routerLinkActive="active">
+ <onos-icon classes="{{ selId ? 'active':undefined }}"
+ iconId="flowTable" iconSize="42" toolTip="{{ flowTip }}"></onos-icon>
+ </div>
+
+ <div routerLink="/port" routerLinkActive="active">
+ <onos-icon classes="{{ selId ? 'active':undefined }}"
+ iconId="portTable" iconSize="42" toolTip="{{ portTip }}"></onos-icon>
+ </div>
+
+ <div routerLink="/group" routerLinkActive="active">
+ <onos-icon classes="{{ selId ? 'active':undefined }}"
+ iconId="groupTable" iconSize="42" toolTip="{{ groupTip }}"></onos-icon>
+ </div>
+
+ <div routerLink="/meter" routerLinkActive="active">
+ <onos-icon classes="{{ selId ? 'active':undefined }}"
+ iconId="meterTable" iconSize="42" toolTip="{{ meterTip }}"></onos-icon>
+ </div>
+
+ <div routerLink="/pipeconf" routerLinkActive="active">
+ <onos-icon classes="{{ selId ? 'active':undefined }}"
+ iconId="pipeconfTable" iconSize="42" toolTip="{{ pipeconfTip }}"></onos-icon>
+ </div>
+ </div>
+ </div>
+
+ <div class="summary-list" onos-table-resize>
+ <table onos-flash-changes id-prop="id" width="100%">
+ <tr class="table-header">
+ <th colId="available" class="table-icon" sortable></th>
+ <th colId="type" class="table-icon"></th>
+ <th colId="name" sortable>Friendly Name </th>
+ <th colId="id" sortable>Device ID </th>
+ <th colId="masterid" [ngClass]="{width: '130px'}" sortable>Master </th>
+ <th colId="num_ports" [ngClass]="{width: '70px'}" sortable>Ports </th>
+ <th colId="mfr" sortable>Vendor </th>
+ <th colId="hw" sortable>H/W Version </th>
+ <th colId="sw" sortable>S/W Version </th>
+ <th colId="protocol" [ngClass]="{width: '100px'}" sortable>Protocol </th>
+ </tr>
+
+ <tr class="table-body" *ngIf="tableData.length === 0" class="no-data">
+ <td colspan="9">{{ annots.noRowsMsg }}</td>
+ </tr>
+
+
+ <tr class="table-body" *ngFor="let dev of tableData; trackBy $index"
+ (click)="selectCallback($event, dev)"
+ [ngClass]="{selected: dev.id === selId, 'data-change': isChanged(dev.id)}">
+ <td class="table-icon">
+ <!--[ngClass]="{width: devAvail.getBBox().width}"-->
+ <onos-icon iconId="{{dev._iconid_available}}"></onos-icon>
+ </td>
+ <td class="table-icon">
+ <onos-icon iconId="{{dev._iconid_type}}"></onos-icon>
+ </td>
+ <td>{{ dev.name }}</td>
+ <td>{{ dev.id }}</td>
+ <td>{{ dev.masterid }}</td>
+ <td>{{ dev.num_ports }}</td>
+ <td>{{ dev.mfr }}</td>
+ <td>{{ dev.hw }}</td>
+ <td>{{ dev.sw }}</td>
+ <td>{{ dev.protocol }}</td>
+ </tr>
+ </table>
+ </div>
+ <small>
+ <p>TODO (21 Jun 18): Add in:</p>
+ <ul>
+ <li>Scrolling for long lists of devices</li>
+ <li>Sorting by column</li>
+ <li>Left align header columns</li>
+ <li>Move tooltip to underneath icon</li>
+ <li>Correct width and icon colour of active and device icon columns</li>
+ <li>Add device details panel</li>
+ <li>Add more unit tests</li>
+ <li>Make icon for #undefined work (e.g. for device type olt or unknown)</li>
+ <li>Change loading service to fade in and out and have a threshold of </li>
+ </ul>
+ </small>
</div>
diff --git a/web/gui2/src/main/webapp/app/view/device/device.component.ts b/web/gui2/src/main/webapp/app/view/device/device.component.ts
index 6929f9b..f4a6fbe 100644
--- a/web/gui2/src/main/webapp/app/view/device/device.component.ts
+++ b/web/gui2/src/main/webapp/app/view/device/device.component.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
import { DetailsPanelService } from '../../fw/layer/detailspanel.service';
import { FnService } from '../../fw/util/fn.service';
import { IconService } from '../../fw/svg/icon.service';
@@ -23,42 +23,78 @@
import { MastService } from '../../fw/mast/mast.service';
import { NavService } from '../../fw/nav/nav.service';
import { PanelService } from '../../fw/layer/panel.service';
-import { TableBuilderService } from '../../fw/widget/tablebuilder.service';
+import { TableBaseImpl, TableResponse } from '../../fw/widget/tablebase';
import { TableDetailService } from '../../fw/widget/tabledetail.service';
import { WebSocketService } from '../../fw/remote/websocket.service';
+interface DeviceTableResponse extends TableResponse {
+ devices: Device[];
+}
+
+interface Device {
+ available: boolean;
+ chassisid: string;
+ hw: string;
+ id: string;
+ masterid: string;
+ mfr: string;
+ name: string;
+ num_ports: number;
+ protocol: string;
+ serial: string;
+ sw: string;
+ _iconid_available: string;
+ _iconid_type: string;
+}
+
+
/**
* ONOS GUI -- Device View Component
*/
@Component({
selector: 'onos-device',
templateUrl: './device.component.html',
- styleUrls: ['./device.component.css']
+ styleUrls: ['./device.component.css', './device.theme.css', '../../fw/widget/table.css', '../../fw/widget/table-theme.css']
})
-export class DeviceComponent implements OnInit {
+export class DeviceComponent extends TableBaseImpl implements OnInit, OnDestroy {
+
+ // TODO: Update for LION
+ flowTip = 'Show flow view for selected device';
+ portTip = 'Show port view for selected device';
+ groupTip = 'Show group view for selected device';
+ meterTip = 'Show meter view for selected device';
+ pipeconfTip = 'Show pipeconf view for selected device';
constructor(
private dps: DetailsPanelService,
- private fs: FnService,
+ protected fs: FnService,
+ protected ls: LoadingService,
private is: IconService,
private ks: KeyService,
- private log: LogService,
+ protected log: LogService,
private mast: MastService,
private nav: NavService,
private ps: PanelService,
- private tbs: TableBuilderService,
private tds: TableDetailService,
- private wss: WebSocketService,
- private ls: LoadingService, // TODO: Remove - already added through tbs
- private window: Window
+ protected wss: WebSocketService,
+ private window: Window,
) {
- this.log.debug('DeviceComponent constructed');
+ super(fs, ls, log, wss, 'device');
+ this.responseCallback = this.deviceResponseCb;
}
ngOnInit() {
+ this.init();
this.log.debug('DeviceComponent initialized');
- // TODO: Remove this - it's only for demo purposes
-// this.ls.startAnim();
+ }
+
+ ngOnDestroy() {
+ this.destroy();
+ this.log.debug('DeviceComponent destroyed');
+ }
+
+ deviceResponseCb(data: DeviceTableResponse) {
+ this.log.debug('Device response received for ', data.devices.length, 'devices');
}
}
diff --git a/web/gui2/src/main/webapp/app/view/device/device.module.ts b/web/gui2/src/main/webapp/app/view/device/device.module.ts
index 8f0f351..99b15bd 100644
--- a/web/gui2/src/main/webapp/app/view/device/device.module.ts
+++ b/web/gui2/src/main/webapp/app/view/device/device.module.ts
@@ -15,9 +15,11 @@
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
+import { RouterModule, Routes } from '@angular/router';
import { DeviceComponent } from './device.component';
import { DeviceDetailsPanelDirective } from './devicedetailspanel.directive';
-import { RemoteModule } from '../../fw/remote/remote.module';
+import { SvgModule } from '../../fw/svg/svg.module';
+
/**
* ONOS GUI -- Device View Module
*/
@@ -27,7 +29,8 @@
],
imports: [
CommonModule,
- RemoteModule
+ RouterModule,
+ SvgModule
],
declarations: [
DeviceComponent,
diff --git a/web/gui2/src/main/webapp/onos.theme.css b/web/gui2/src/main/webapp/onos.theme.css
index dc14c80..a2ac0aa 100644
--- a/web/gui2/src/main/webapp/onos.theme.css
+++ b/web/gui2/src/main/webapp/onos.theme.css
@@ -41,6 +41,10 @@
#view h2 {
color: #3c3a3a;
+ margin: 32px 0 4px 16px;
+ padding: 0;
+ font-size: 18pt;
+ font-weight: lighter;
}
a {
diff --git a/web/gui2/src/main/webapp/tests/app/fw/remote/websocket.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/remote/websocket.service.spec.ts
index 5c8d6b7..391b62c 100644
--- a/web/gui2/src/main/webapp/tests/app/fw/remote/websocket.service.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/fw/remote/websocket.service.spec.ts
@@ -33,8 +33,6 @@
class MockGlyphService {}
-class MockWSock {}
-
/**
* ONOS GUI -- Remote -- Web Socket Service - Unit Tests
*/
@@ -103,7 +101,7 @@
'noHandlersWarn', 'resetState',
'createWebSocket', 'bindHandlers', 'unbindHandlers',
'addOpenListener', 'removeOpenListener', 'sendEvent',
- 'setVeilDelegate', 'setLoadingDelegate'
+ 'setVeilDelegate', 'setLoadingDelegate', 'isConnected', 'closeWebSocket'
])).toBeTruthy();
});
@@ -228,9 +226,7 @@
});
it('should warn if no arguments, unbindHandlers', () => {
- expect(wss.unbindHandlers(
- new Map<string, (data) => void>([])
- )).toBeNull();
+ expect(wss.unbindHandlers([])).toBeNull();
expect(logServiceSpy.warn).toHaveBeenCalledWith(
'WSS.unbindHandlers(): no event handlers'
);
diff --git a/web/gui2/src/main/webapp/tests/app/fw/remote/wsock.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/remote/wsock.service.spec.ts
deleted file mode 100644
index 61d5ab4..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/remote/wsock.service.spec.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { WSock } from '../../../../app/fw/remote/wsock.service';
-
-/**
- * ONOS GUI -- Remote -- WSock Service - Unit Tests
- */
-describe('WSock', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [WSock,
- { provide: LogService, useValue: log },
- ]
- });
- });
-
- it('should be created', inject([WSock], (service: WSock) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/svg/icon.directive.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/svg/icon.directive.spec.ts
deleted file mode 100644
index 4c5c252..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/svg/icon.directive.spec.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { ElementRef } from '@angular/core';
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { IconDirective } from '../../../../app/fw/svg/icon.directive';
-import { IconService } from '../../../../app/fw/svg/icon.service';
-import { GlyphService } from '../../../../app/fw/svg/glyph.service';
-import { SvgUtilService } from '../../../../app/fw/svg/svgutil.service';
-import { FnService } from '../../../../app/fw//util/fn.service';
-import { ActivatedRoute, Router} from '@angular/router';
-
-class MockFnService {}
-
-class MockGlyphService {}
-
-class MockIconService {}
-
-/**
- * ONOS GUI -- SVG -- Icon Directive - Unit Tests
- */
-describe('IconDirective', () => {
- let log: LogService;
- const elementMock = <any>{ };
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [ IconDirective,
- { provide: FnService, useClass: MockFnService },
- { provide: LogService, useValue: log },
- { provide: ElementRef, useValue: elementMock },
- { provide: GlyphService, useClass: MockGlyphService },
- { provide: IconService, useClass: MockIconService },
- ]
- });
- });
-
- afterEach(() => {
- log = null;
- });
-
- it('should create an instance', inject([IconDirective], (directive: IconDirective) => {
- expect(directive).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts
index 84b5f094..bf75091 100644
--- a/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts
@@ -235,7 +235,7 @@
'isFirefox', 'parseDebugFlags',
'debugOn', 'debug', 'find', 'inArray', 'removeFromArray',
'isEmptyObject', 'cap', 'noPx', 'noPxStyle', 'endsWith',
- 'inEvilList', 'analyze', 'sanitize'
+ 'inEvilList', 'analyze', 'sanitize', 'sameObjProps', 'containsObj'
// 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
// 'eecode', 'noPx', 'noPxStyle', 'endsWith', 'addToTrie', 'removeFromTrie', 'trieLookup',
// 'classNames', 'extend', 'sanitize'
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/button.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/button.service.spec.ts
index cc8113b..c3e501d 100644
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/button.service.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/fw/widget/button.service.spec.ts
@@ -20,14 +20,11 @@
import { ButtonService } from '../../../../app/fw/widget/button.service';
import { FnService } from '../../../../app/fw/util/fn.service';
import { IconService } from '../../../../app/fw/svg/icon.service';
-import { TooltipService } from '../../../../app/fw/widget/tooltip.service';
class MockIconService {}
class MockFnService {}
-class MockTooltipService {}
-
/**
* ONOS GUI -- Widget -- Button Service - Unit Tests
*/
@@ -42,7 +39,6 @@
{ provide: LogService, useValue: log },
{ provide: IconService, useClass: MockIconService },
{ provide: FnService, useClass: MockFnService },
- { provide: TooltipService, useClass: MockTooltipService },
]
});
});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/tablebuilder.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/tablebuilder.service.spec.ts
deleted file mode 100644
index 1126d28..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/tablebuilder.service.spec.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
-import { FnService } from '../../../../app/fw//util/fn.service';
-import { LoadingService } from '../../../../app/fw/layer/loading.service';
-import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
-
-class MockFnService {}
-
-class MockLoadingService {}
-
-class MockWebSocketService {}
-
-/*
- ONOS GUI -- Widget -- Table Builder Service - Unit Tests
- */
-describe('TableBuilderService', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [TableBuilderService,
- { provide: FnService, useClass: MockFnService },
- { provide: LoadingService, useClass: MockLoadingService },
- { provide: LogService, useValue: log },
- { provide: WebSocketService, useClass: MockWebSocketService },
-
- ]
- });
- });
-
- it('should be created', inject([TableBuilderService], (service: TableBuilderService) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.directive.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.directive.spec.ts
deleted file mode 100644
index 7445a05..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.directive.spec.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { TooltipDirective } from '../../../../app/fw/widget/tooltip.directive';
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { FnService } from '../../../../app/fw/util/fn.service';
-
-class MockFnService {}
-
-/**
- * ONOS GUI -- Widget -- Tooltip Directive - Unit Tests
- */
-describe('TooltipDirective', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [ TooltipDirective,
- { provide: FnService, useClass: MockFnService },
- { provide: LogService, useValue: log },
- ]
- });
- });
-
- afterEach(() => {
- log = null;
- });
-
- it('should create an instance', inject([TooltipDirective], (directive: TooltipDirective) => {
- expect(directive).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.service.spec.ts
deleted file mode 100644
index 98a5a18..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/tooltip.service.spec.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { TooltipService } from '../../../../app/fw/widget/tooltip.service';
-import { FnService } from '../../../../app/fw/util/fn.service';
-
-class MockFnService {}
-
-/**
- * ONOS GUI -- Widget -- Tooltip Service - Unit Tests
- */
-describe('TooltipService', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [TooltipService,
- { provide: LogService, useValue: log },
- { provide: FnService, useClass: MockFnService },
- ]
- });
- });
-
- it('should be created', inject([TooltipService], (service: TooltipService) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/onos.component.spec.ts b/web/gui2/src/main/webapp/tests/app/onos.component.spec.ts
index 7a15504..8dcb9e0 100644
--- a/web/gui2/src/main/webapp/tests/app/onos.component.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/onos.component.spec.ts
@@ -59,6 +59,8 @@
class MockKeyService {}
+class MockLionService {}
+
class MockNavService {}
class MockOnosService {}
@@ -73,6 +75,11 @@
class MockVeilComponent {}
+class MockWebSocketService {
+ createWebSocket() {}
+ isConnected() { return false; }
+}
+
/**
* ONOS GUI -- Onos Component - Unit Tests
*/
@@ -81,6 +88,8 @@
let fs: FnService;
let ar: MockActivatedRoute;
let windowMock: Window;
+ let fixture;
+ let app;
beforeEach(async(() => {
log = new ConsoleLoggerService();
@@ -117,6 +126,7 @@
{ provide: GlyphService, useClass: MockGlyphService },
{ provide: IconService, useClass: MockIconService },
{ provide: KeyService, useClass: MockKeyService },
+ { provide: LionService, useClass: MockLionService },
{ provide: LogService, useValue: log },
{ provide: NavService, useClass: MockNavService },
{ provide: OnosService, useClass: MockOnosService },
@@ -124,20 +134,22 @@
{ provide: PanelService, useClass: MockPanelService },
{ provide: SpriteService, useClass: MockSpriteService },
{ provide: ThemeService, useClass: MockThemeService },
+ { provide: WebSocketService, useClass: MockWebSocketService },
{ provide: Window, useFactory: (() => windowMock ) },
]
}).compileComponents();
+
+ fixture = TestBed.createComponent(OnosComponent);
+ app = fixture.componentInstance;
}));
it('should create the app', async(() => {
- const fixture = TestBed.createComponent(OnosComponent);
- const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
- it(`should have as title 'onos'`, async(() => {
- const fixture = TestBed.createComponent(OnosComponent);
- const app = fixture.debugElement.componentInstance;
- expect(app.title).toEqual('onos');
- }));
+// it(`should have as title 'onos'`, async(() => {
+// const fixture = TestBed.createComponent(OnosComponent);
+// const app = fixture.componentInstance;
+// expect(app.title).toEqual('onos');
+// }));
});
diff --git a/web/gui2/src/main/webapp/tests/app/view/apps/apps.component.spec.ts b/web/gui2/src/main/webapp/tests/app/view/apps/apps.component.spec.ts
index a998cb0..6682e41 100644
--- a/web/gui2/src/main/webapp/tests/app/view/apps/apps.component.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/view/apps/apps.component.spec.ts
@@ -14,72 +14,126 @@
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
+import { ActivatedRoute, Params } from '@angular/router';
import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
import { AppsComponent } from '../../../../app/view/apps/apps.component';
import { DialogService } from '../../../../app/fw/layer/dialog.service';
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 { KeyService } from '../../../../app/fw/util/key.service';
import { LionService } from '../../../../app/fw/util/lion.service';
+import { LoadingService } from '../../../../app/fw/layer/loading.service';
import { PanelService } from '../../../../app/fw/layer/panel.service';
-import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
+import { ThemeService } from '../../../../app/fw/util/theme.service';
import { UrlFnService } from '../../../../app/fw/remote/urlfn.service';
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
+import { of } from 'rxjs';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
class MockDialogService {}
class MockFnService {}
-class MockIconService {}
+class MockIconService {
+ loadIconDef() {}
+}
class MockKeyService {}
-class MockLionService {}
+class MockLoadingService {
+ startAnim() {}
+ stop() {}
+ waiting() {}
+}
class MockPanelService {}
class MockTableBuilderService {}
+class MockThemeService {}
+
class MockUrlFnService {}
-class MockWebSocketService {}
+class MockWebSocketService {
+ createWebSocket() {}
+ isConnected() { return false; }
+ unbindHandlers() {}
+ bindHandlers() {}
+}
/**
* ONOS GUI -- Apps View -- Unit Tests
*/
describe('AppsComponent', () => {
- let log: LogService;
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
let component: AppsComponent;
let fixture: ComponentFixture<AppsComponent>;
- const windowMock = <any>{ location: <any> { hostname: 'localhost' } };
+ const bundleObj = {
+ 'core.view.App': {
+ test: 'test1'
+ }
+ };
+ const mockLion = (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
beforeEach(async(() => {
- log = new ConsoleLoggerService();
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({'debug': 'txrx'});
+
+ windowMock = <any>{
+ location: <any> {
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true'},
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
TestBed.configureTestingModule({
- declarations: [ AppsComponent ],
+ declarations: [ AppsComponent, IconComponent ],
providers: [
{ provide: DialogService, useClass: MockDialogService },
- { provide: FnService, useClass: MockFnService },
+ { provide: FnService, useValue: fs },
{ provide: IconService, useClass: MockIconService },
{ provide: KeyService, useClass: MockKeyService },
- { provide: LionService, useClass: MockLionService },
- { provide: LogService, useValue: log },
+ { provide: LionService, useFactory: (() => {
+ return {
+ bundle: ((bundleId) => mockLion),
+ ubercache: new Array()
+ };
+ })
+ },
+ { provide: LoadingService, useClass: MockLoadingService },
+ { provide: LogService, useValue: logSpy },
{ provide: PanelService, useClass: MockPanelService },
- { provide: TableBuilderService, useClass: MockTableBuilderService },
+ { provide: ThemeService, useClass: MockThemeService },
{ provide: UrlFnService, useClass: MockUrlFnService },
{ provide: WebSocketService, useClass: MockWebSocketService },
{ provide: Window, useValue: windowMock },
]
})
.compileComponents();
+ logServiceSpy = TestBed.get(LogService);
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppsComponent);
- component = fixture.componentInstance;
+ component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});
diff --git a/web/gui2/src/main/webapp/tests/app/view/device/device.component.spec.ts b/web/gui2/src/main/webapp/tests/app/view/device/device.component.spec.ts
index 960d241..6d54ac4 100644
--- a/web/gui2/src/main/webapp/tests/app/view/device/device.component.spec.ts
+++ b/web/gui2/src/main/webapp/tests/app/view/device/device.component.spec.ts
@@ -14,46 +14,50 @@
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
+import { ActivatedRoute, Params } from '@angular/router';
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
import { DeviceComponent } from '../../../../app/view/device/device.component';
import { DetailsPanelService } from '../../../../app/fw/layer/detailspanel.service';
import { FnService, WindowSize } from '../../../../app/fw/util/fn.service';
import { IconService } from '../../../../app/fw/svg/icon.service';
import { GlyphService } from '../../../../app/fw/svg/glyph.service';
+import { IconComponent } from '../../../../app/fw/svg/icon/icon.component';
import { KeyService } from '../../../../app/fw/util/key.service';
import { LoadingService } from '../../../../app/fw/layer/loading.service';
import { NavService } from '../../../../app/fw/nav/nav.service';
import { MastService } from '../../../../app/fw/mast/mast.service';
import { PanelService } from '../../../../app/fw/layer/panel.service';
import { SvgUtilService } from '../../../../app/fw/svg/svgutil.service';
-import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
import { TableDetailService } from '../../../../app/fw/widget/tabledetail.service';
+import { ThemeService } from '../../../../app/fw/util/theme.service';
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
+import { of } from 'rxjs';
-class MockDetailsPanelService {}
-
-class MockFnService {
- windowSize(offH: number = 0, offW: number = 0): WindowSize {
- return {
- height: 123,
- width: 456
- };
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
}
}
-class MockIconService {}
+class MockDetailsPanelService {}
+
+class MockFnService {}
+
+class MockIconService {
+ loadIconDef() {}
+}
class MockGlyphService {}
class MockKeyService {}
class MockLoadingService {
- startAnim() {
- // Do nothing
- }
+ startAnim() {}
+ stop() {}
}
class MockNavService {}
@@ -66,49 +70,81 @@
class MockTableDetailService {}
-class MockWebSocketService {}
+class MockThemeService {}
+
+class MockWebSocketService {
+ createWebSocket() {}
+ isConnected() { return false; }
+ unbindHandlers() {}
+ bindHandlers() {}
+}
/**
* ONOS GUI -- Device View Module - Unit Tests
*/
describe('DeviceComponent', () => {
- let log: LogService;
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
let component: DeviceComponent;
let fixture: ComponentFixture<DeviceComponent>;
- const windowMock = <any>{ location: <any> { hostname: 'localhost' } };
beforeEach(async(() => {
- log = new ConsoleLoggerService();
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({'debug': 'txrx'});
+
+ windowMock = <any>{
+ location: <any> {
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true'},
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+
TestBed.configureTestingModule({
- declarations: [ DeviceComponent ],
+ declarations: [ DeviceComponent, IconComponent ],
providers: [
{ provide: DetailsPanelService, useClass: MockDetailsPanelService },
- { provide: FnService, useClass: MockFnService },
+ { provide: FnService, useValue: fs },
{ provide: IconService, useClass: MockIconService },
{ provide: GlyphService, useClass: MockGlyphService },
{ provide: KeyService, useClass: MockKeyService },
{ provide: LoadingService, useClass: MockLoadingService },
{ provide: MastService, useClass: MockMastService },
{ provide: NavService, useClass: MockNavService },
- { provide: LogService, useValue: log },
+ { provide: LogService, useValue: logSpy },
{ provide: PanelService, useClass: MockPanelService },
- { provide: TableBuilderService, useClass: MockTableBuilderService },
{ provide: TableDetailService, useClass: MockTableDetailService },
+ { provide: ThemeService, useClass: MockThemeService },
{ provide: WebSocketService, useClass: MockWebSocketService },
{ provide: Window, useValue: windowMock },
]
})
.compileComponents();
+ logServiceSpy = TestBed.get(LogService);
}));
beforeEach(() => {
fixture = TestBed.createComponent(DeviceComponent);
- component = fixture.componentInstance;
+ component = fixture.debugElement.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should have .table-header with "Friendly Name..."', () => {
+ const appDe: DebugElement = fixture.debugElement;
+ const divDe = appDe.query(By.css('.table-header'));
+ const div: HTMLElement = divDe.nativeElement;
+ expect(div.textContent).toEqual('Friendly Name Device ID Master Ports Vendor H/W Version S/W Version Protocol ');
+ });
});