mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-12-17 07:11:27 +01:00
Implemented table building functions
Change-Id: Ie4003080b13725561df22de41ec85f8c3f31c794
This commit is contained in:
parent
667538d4ea
commit
2bd11b79bf
@ -88,14 +88,17 @@ There is a fair bit of refactoring has to take place. An important thing to unde
|
|||||||
is that DOM manipulations from inside JavaScript code is not the Angular 6
|
is that DOM manipulations from inside JavaScript code is not the Angular 6
|
||||||
way of doing things - there was a lot of this in the old ONOS GUI, using d3.append(..)
|
way of doing things - there was a lot of this in the old ONOS GUI, using d3.append(..)
|
||||||
and so on.
|
and so on.
|
||||||
The Angular 6 way of doing things is to defined DOM objects (elements) in the
|
The Angular 6 way of doing things is to define DOM objects (elements) in the
|
||||||
html template of a component, and use the Component Java Script code as a base
|
html template of a component, and use the Component Java Script code as a base
|
||||||
for logic that can influence the display of these objects in the template.
|
for logic that can influence the display of these objects in the template.
|
||||||
What this means is that what were previously defined as services (e.g. VeilService or
|
What this means is that what were previously defined as services (e.g. VeilService or
|
||||||
LoadingService) should now become Components in Angular 6 (e.g. VeilComponent or
|
LoadingService) should now become Components in Angular 6 (e.g. VeilComponent or
|
||||||
LoadingComponent).
|
LoadingComponent).
|
||||||
|
|
||||||
###How do I know whether a Service should be made a Component in this new GUI?
|
Similarly a directive might be trying to do DOM manipulation and have a CSS - this
|
||||||
|
should be made in to a component instead (see IconComponent)
|
||||||
|
|
||||||
|
###How do I know whether a Service or Directive should be made a Component in this new GUI?
|
||||||
The general rule to follow is _"if a service in the old GUI has an associated CSS
|
The general rule to follow is _"if a service in the old GUI has an associated CSS
|
||||||
file or two then is should be a component in the new GUI"_.
|
file or two then is should be a component in the new GUI"_.
|
||||||
|
|
||||||
@ -129,6 +132,15 @@ Any component that needs to use WSS for data should inject the WSS service __AND
|
|||||||
needs to include the components in its template by adding <onos-loading> and
|
needs to include the components in its template by adding <onos-loading> and
|
||||||
<onos-veil>.
|
<onos-veil>.
|
||||||
|
|
||||||
|
### Consider if a service is really needs to be a service that runs all the time
|
||||||
|
Or does it just support a few functions. See the TableBase class. This now
|
||||||
|
replaces the old TableBuilderService - that was just on function that
|
||||||
|
manipulated the scope of a view component. Instead view components now
|
||||||
|
extend this class.
|
||||||
|
|
||||||
|
Also sometimes directive are always used together e.g. icon directive and tooltip
|
||||||
|
directive and they can be merged in to one
|
||||||
|
|
||||||
## fw/remote/wsock.service
|
## fw/remote/wsock.service
|
||||||
Taking for a really simple example the fw/remote/WSockService, this was originally defined in
|
Taking for a really simple example the fw/remote/WSockService, this was originally defined in
|
||||||
the __app/fw/remote/wsock.js__ file and is now redefined in
|
the __app/fw/remote/wsock.js__ file and is now redefined in
|
||||||
@ -222,22 +234,49 @@ There are several things worth noting here:
|
|||||||
* (d3 object).append(..).attr values should be listed individually (see icon.service for example)
|
* (d3 object).append(..).attr values should be listed individually (see icon.service for example)
|
||||||
* Please try do avoid d3 DOM manipulations in ONOS GUI 2, as this is not the Angular 6 way of
|
* Please try do avoid d3 DOM manipulations in ONOS GUI 2, as this is not the Angular 6 way of
|
||||||
doing things
|
doing things
|
||||||
|
* $interval should be replaced by
|
||||||
|
task = setInterval(() => functionname_or_body, speed);
|
||||||
|
* To cancel the timer clearInterval(task)
|
||||||
|
|
||||||
|
|
||||||
# Progress so far - 24 May 2018
|
# Progress so far - 18 Jun 2018
|
||||||
The following services are partially migrated:
|
The following services are partially migrated:
|
||||||
* fw/util/FnService - some essential components of this have been migrated - still lots to do
|
* fw/util/FnService - full migrated with Unit tests
|
||||||
* fw/svg/GlyphDataService - mostly migrated. Values are stored in maps as constants
|
* fw/svg/GlyphDataService - mostly migrated. Values are stored in maps as constants
|
||||||
* fw/svg/GlyphService - partly implemented - enough to support the icon service
|
* fw/svg/GlyphService - partly implemented - enough to support the icon service
|
||||||
* fw/svg/IconService - mostly implemented - enough to support the IconDirective
|
* fw/svg/IconService - mostly implemented - enough to support the IconDirective
|
||||||
* fw/svg/IconDirective - mostly implemented - although want to replace this with
|
|
||||||
the IconComponent
|
|
||||||
* fw/svg/icon/IconComponent - replacement for the IconDirective - decided to make
|
* fw/svg/icon/IconComponent - replacement for the IconDirective - decided to make
|
||||||
it a component because it has CSS files which are scoped to just that component
|
it a component because it has CSS files which are scoped to just that component
|
||||||
* fw/layer/LoadingService - mostly implemented - I'm leaving this as a Service,
|
It also incorporates the old fw/widget/tooltip.js which was a directive - combined
|
||||||
although maybe it should become a component - its CSS is has to be loaded
|
tooltip in to icon because the 2 are always used together in tabular views
|
||||||
globally in index.html
|
* fw/layer/LoadingService - mostly implemented - this should become a component
|
||||||
|
- its CSS is has to be loaded globally in index.html
|
||||||
* fw/layer/flash/FlashComponent - implemented as a Component instead of the old Flash Service
|
* fw/layer/flash/FlashComponent - implemented as a Component instead of the old Flash Service
|
||||||
because it has a CSS file. Replaced all of the D3 Dom manipulations with Template code
|
because it has a CSS file. Replaced all of the D3 Dom manipulations with Template code
|
||||||
in the Angular 6 style of doing things
|
in the Angular 6 style of doing things
|
||||||
|
* fw/layer/veil/VeilComponent - changed to a component - fully implemented
|
||||||
|
* fw/remote/urlfn.service - fully implemented with Unit tests
|
||||||
|
* fw/remote/WebSocketService - fully implemented with Unit tests
|
||||||
|
* fw/widget/TableBase - previously the TableBuilderService this has now been changed
|
||||||
|
to a plain interface and class - any table views should extend this
|
||||||
|
|
||||||
|
|
||||||
|
# Devices View
|
||||||
|
This is now a Component, whose class extends the TableBase - this is where it gets
|
||||||
|
most of its functionality. As before all data comes from the WebSocket.
|
||||||
|
There is still a bit of work to go on this - scrolling of long lists, device details
|
||||||
|
panel etc
|
||||||
|
|
||||||
|
The major change in the template (html) is that there used to be 2 tables and these
|
||||||
|
are now brought together in to a header and body. This simplifies trying to keep
|
||||||
|
the widths of both in sync.
|
||||||
|
|
||||||
|
For CSS the old device view CSS is included and a link is made across to the
|
||||||
|
common table CSS
|
||||||
|
|
||||||
|
#Apps View
|
||||||
|
This is a Component too, again extending TableBase. Apps view has much more functionality
|
||||||
|
though because it has controls for upload and download of applications.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ genrule(
|
|||||||
+ 'npm5 install -g @angular/cli@6.0.0 2>&1;'
|
+ 'npm5 install -g @angular/cli@6.0.0 2>&1;'
|
||||||
+ 'npm5 install 2>&1;'
|
+ 'npm5 install 2>&1;'
|
||||||
+ 'ng -v;'
|
+ 'ng -v;'
|
||||||
+ 'ng build --preserve-symlinks --base-href /onos/ui2/dist/ --deploy-url /onos/ui2/dist/ --output-path="$OUT" 2>&1',
|
+ 'ng build --extract-css --preserve-symlinks --base-href /onos/ui2/dist/ --deploy-url /onos/ui2/dist/ --output-path="$OUT" 2>&1',
|
||||||
out = 'dist',
|
out = 'dist',
|
||||||
visibility = [ 'PUBLIC' ],
|
visibility = [ 'PUBLIC' ],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,14 +9,23 @@ To use this new GUI you simply have to start the GUI in a running ONOS at the __
|
|||||||
```
|
```
|
||||||
feature:install onos-gui2
|
feature:install onos-gui2
|
||||||
```
|
```
|
||||||
and the gui will be accessible at [http://localhost:8181/onos/ui2/dist/](http://localhost:8181/onos/ui2/dist/)
|
and the gui will be accessible at [http://localhost:8181/onos/ui2](http://localhost:8181/onos/ui2)
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
The project relies on [Angular CLI](https://github.com/angular/angular-cli) v6 to simplify development of the browser side code.
|
There are 2 ways to go about development -
|
||||||
|
1. rebuild the code and rerun through BUCK OR (much like can be done with any ordinary ONOS app)
|
||||||
|
2. use Angular 6 CLI (ng command) to rebuild on the fly (must faster for development)
|
||||||
|
|
||||||
This allows you to develop the Angular 6 Type Script code independent of ONOS in a separate container. At the current moment (May '18) the
|
For 1) if you change the code you can redeploy the application with (requies you to be in ~/onos directory):
|
||||||
implementation of WebSockets and REST calls is not done, so there is __no__ requirement to run ONOS in the background.
|
```
|
||||||
This will change in the coming weeks.
|
onos-buck build //web/gui2:onos-web-gui2-oar --show-output|grep /app.oar | cut -d\ -f2 | xargs onos-app localhost reinstall!
|
||||||
|
```
|
||||||
|
|
||||||
|
For 2) it's well worth becoming familiar with Angular CLI.
|
||||||
|
The project is created with [Angular CLI](https://github.com/angular/angular-cli) v6 to simplify development of the browser side code.
|
||||||
|
|
||||||
|
This allows you to develop the Angular 6 Type Script code independent of ONOS in a separate container.
|
||||||
|
Since WebSockets have been implemented (Jun 18) there is a requirement to run ONOS in the background.
|
||||||
|
|
||||||
There is no need to install node, npm or ng again on your system, and indeed if they are already installed, it's best
|
There is no need to install node, npm or ng again on your system, and indeed if they are already installed, it's best
|
||||||
to use the versions of these that's used by BUCK. To do this add to the __start__ of your PATH environment variable.
|
to use the versions of these that's used by BUCK. To do this add to the __start__ of your PATH environment variable.
|
||||||
@ -64,7 +73,8 @@ To run it manually in Angular CLI run `ng build`
|
|||||||
## Running unit tests
|
## Running unit tests
|
||||||
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
||||||
|
|
||||||
To run it manually in Angular CLI run `ng test --watch=true` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
To run it manually in Angular CLI run `ng test --watch` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
Running it directly like this will test with both Firefox and Chrome. To use only one use the __--browsers__ argument
|
||||||
|
|
||||||
## Running checkstyle
|
## Running checkstyle
|
||||||
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
||||||
|
|||||||
@ -8,6 +8,7 @@ module.exports = function (config) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
require('karma-jasmine'),
|
require('karma-jasmine'),
|
||||||
require('karma-chrome-launcher'),
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-firefox-launcher'),
|
||||||
require('karma-jasmine-html-reporter'),
|
require('karma-jasmine-html-reporter'),
|
||||||
require('karma-coverage-istanbul-reporter'),
|
require('karma-coverage-istanbul-reporter'),
|
||||||
require('@angular-devkit/build-angular/plugins/karma')
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
@ -27,7 +28,7 @@ module.exports = function (config) {
|
|||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO,
|
||||||
autoWatch: true,
|
autoWatch: true,
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome', 'Firefox'],
|
||||||
singleRun: false
|
singleRun: false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
6
web/gui2/package-lock.json
generated
6
web/gui2/package-lock.json
generated
@ -8446,6 +8446,12 @@
|
|||||||
"minimatch": "3.0.4"
|
"minimatch": "3.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"karma-firefox-launcher": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"karma-jasmine": {
|
"karma-jasmine": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz",
|
||||||
|
|||||||
@ -28,10 +28,9 @@
|
|||||||
"zone.js": "^0.8.26"
|
"zone.js": "^0.8.26"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/compiler-cli": "^6.0.0",
|
|
||||||
"@angular-devkit/build-angular": "~0.6.0",
|
"@angular-devkit/build-angular": "~0.6.0",
|
||||||
"typescript": "~2.7.2",
|
|
||||||
"@angular/cli": "~6.0.0",
|
"@angular/cli": "~6.0.0",
|
||||||
|
"@angular/compiler-cli": "^6.0.0",
|
||||||
"@angular/language-service": "^6.0.0",
|
"@angular/language-service": "^6.0.0",
|
||||||
"@types/jasmine": "~2.8.6",
|
"@types/jasmine": "~2.8.6",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
@ -42,10 +41,12 @@
|
|||||||
"karma": "~1.7.1",
|
"karma": "~1.7.1",
|
||||||
"karma-chrome-launcher": "~2.2.0",
|
"karma-chrome-launcher": "~2.2.0",
|
||||||
"karma-coverage-istanbul-reporter": "~1.4.2",
|
"karma-coverage-istanbul-reporter": "~1.4.2",
|
||||||
|
"karma-firefox-launcher": "^1.1.0",
|
||||||
"karma-jasmine": "~1.1.1",
|
"karma-jasmine": "~1.1.1",
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
"karma-jasmine-html-reporter": "^0.2.2",
|
||||||
"protractor": "~5.3.0",
|
"protractor": "~5.3.0",
|
||||||
"ts-node": "~5.0.1",
|
"ts-node": "~5.0.1",
|
||||||
"tslint": "~5.9.1"
|
"tslint": "~5.9.1",
|
||||||
|
"typescript": "~2.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { Logger } from './log.service';
|
import { Logger } from './log.service';
|
||||||
|
|
||||||
export let isDebugMode = environment.isDebugMode;
|
export let isDebugMode: boolean = !environment.production;
|
||||||
|
|
||||||
const noop = (): any => undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- LogService
|
* ONOS GUI -- LogService
|
||||||
@ -27,12 +25,13 @@ const noop = (): any => undefined;
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConsoleLoggerService implements Logger {
|
export class ConsoleLoggerService implements Logger {
|
||||||
|
private noop: () => void;
|
||||||
|
|
||||||
get debug() {
|
get debug() {
|
||||||
if (isDebugMode) {
|
if (isDebugMode) {
|
||||||
return console.debug.bind(console);
|
return console.debug.bind(console);
|
||||||
} else {
|
} else {
|
||||||
return noop;
|
return this.noop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ export class ConsoleLoggerService implements Logger {
|
|||||||
if (isDebugMode) {
|
if (isDebugMode) {
|
||||||
return console.info.bind(console);
|
return console.info.bind(console);
|
||||||
} else {
|
} else {
|
||||||
return noop;
|
return this.noop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ export class ConsoleLoggerService implements Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
invokeConsoleMethod(type: string, args?: any): void {
|
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]);
|
logFn.apply(console, [args]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,4 +38,7 @@ export class DialogService {
|
|||||||
this.log.debug('DialogService constructed');
|
this.log.debug('DialogService constructed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createDiv() {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,9 @@ const waitDelay = 500;
|
|||||||
*
|
*
|
||||||
* Provides a mechanism to start/stop the loading animation, center screen.
|
* Provides a mechanism to start/stop the loading animation, center screen.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
export class LoadingService {
|
export class LoadingService {
|
||||||
images: any[] = [];
|
images: any[] = [];
|
||||||
idx = 0;
|
idx = 0;
|
||||||
@ -127,7 +129,7 @@ export class LoadingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return true if start() has been called but not stop()
|
// return true if start() has been called but not stop()
|
||||||
waiting() {
|
waiting(): boolean {
|
||||||
return !!this.wait;
|
return !!this.wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,7 +90,6 @@ export class WebSocketService {
|
|||||||
* built-in handler for the 'boostrap' event
|
* built-in handler for the 'boostrap' event
|
||||||
*/
|
*/
|
||||||
private bootstrap(data: Bootstrap) {
|
private bootstrap(data: Bootstrap) {
|
||||||
this.log.debug('bootstrap data', data);
|
|
||||||
this.loggedInUser = data.user;
|
this.loggedInUser = data.user;
|
||||||
|
|
||||||
this.clusterNodes = data.clusterNodes;
|
this.clusterNodes = data.clusterNodes;
|
||||||
@ -332,6 +331,13 @@ export class WebSocketService {
|
|||||||
return this.url;
|
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
|
* Binds the message handlers to their message type (event type) as
|
||||||
@ -369,12 +375,12 @@ export class WebSocketService {
|
|||||||
* Unbinds the specified message handlers.
|
* Unbinds the specified message handlers.
|
||||||
* Expected that the same map will be used, but we only care about keys
|
* Expected that the same map will be used, but we only care about keys
|
||||||
*/
|
*/
|
||||||
unbindHandlers(handlerMap: Map<string, (data) => void>): void {
|
unbindHandlers(handlerIds: string[]): void {
|
||||||
if (this.noHandlersWarn(handlerMap, 'unbindHandlers')) {
|
if ( handlerIds.length === 0 ) {
|
||||||
|
this.log.warn('WSS.unbindHandlers(): no event handlers');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
for (const eventId of handlerIds) {
|
||||||
for (const [eventId, api] of handlerMap) {
|
|
||||||
this.handlers.delete(eventId);
|
this.handlers.delete(eventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,4 +454,7 @@ export class WebSocketService {
|
|||||||
this.lcd = ld;
|
this.lcd = ld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isConnected(): boolean {
|
||||||
|
return this.wsUp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -13,9 +13,11 @@
|
|||||||
~ See the License for the specific language governing permissions and
|
~ See the License for the specific language governing permissions and
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50">
|
<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50" (mouseover)="toolTipDisp = toolTip" (mouseout)="toolTipDisp = undefined">
|
||||||
<g class="icon" [ngClass]="iconId">
|
<g class="icon" [ngClass]="classes">
|
||||||
<rect width="50" height="50" rx="5"></rect>
|
<rect width="50" height="50" rx="5"></rect>
|
||||||
<use width="50" height="50" class="glyph" [attr.href]="iconTag()"></use>
|
<use width="50" height="50" class="glyph" [attr.href]="iconTag()"></use>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</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>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 1.2 KiB |
@ -16,7 +16,6 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
import { IconService, glyphMapping } from '../icon.service';
|
import { IconService, glyphMapping } from '../icon.service';
|
||||||
import { LogService } from '../../../log.service';
|
import { LogService } from '../../../log.service';
|
||||||
import * as d3 from 'd3';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon Component
|
* Icon Component
|
||||||
@ -31,11 +30,20 @@ import * as d3 from 'd3';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'onos-icon',
|
selector: 'onos-icon',
|
||||||
templateUrl: './icon.component.html',
|
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 {
|
export class IconComponent implements OnInit {
|
||||||
@Input() iconId: string;
|
@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(
|
constructor(
|
||||||
private is: IconService,
|
private is: IconService,
|
||||||
@ -47,7 +55,6 @@ export class IconComponent implements OnInit {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.is.loadIconDef(this.iconId);
|
this.is.loadIconDef(this.iconId);
|
||||||
this.log.debug('IconComponent initialized for ', this.iconId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -40,6 +40,46 @@
|
|||||||
fill: #3c3a3a;
|
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 Theme ========== */
|
||||||
|
|
||||||
.dark div.close-btn svg.embeddedIcon g.icon .glyph {
|
.dark div.close-btn svg.embeddedIcon g.icon .glyph {
|
||||||
@ -61,7 +101,7 @@
|
|||||||
.dark table svg.embeddedIcon .icon .glyph {
|
.dark table svg.embeddedIcon .icon .glyph {
|
||||||
fill: #9999aa;
|
fill: #9999aa;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
svg.embeddedIcon g.icon .glyph {
|
svg.embeddedIcon g.icon .glyph {
|
||||||
fill: #007dc4;
|
fill: #007dc4;
|
||||||
}
|
}
|
||||||
@ -69,3 +109,4 @@ svg.embeddedIcon g.icon .glyph {
|
|||||||
svg.embeddedIcon:hover g.icon .glyph {
|
svg.embeddedIcon:hover g.icon .glyph {
|
||||||
fill: #20b2ff;
|
fill: #20b2ff;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015-present Open Networking Foundation
|
* Copyright 2016-present Open Networking Foundation
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,23 +13,18 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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
|
ONOS GUI -- Tooltip Service (theme) -- CSS file
|
||||||
*/
|
*/
|
||||||
@Directive({
|
.light#tooltip {
|
||||||
selector: '[onosTooltip]'
|
background-color: #dbeffc;
|
||||||
})
|
color: #3c3a3a;
|
||||||
export class TooltipDirective {
|
border-color: #c7c7c0;
|
||||||
|
}
|
||||||
constructor(
|
|
||||||
private fs: FnService,
|
.dark#tooltip {
|
||||||
private log: LogService
|
background-color: #3a3a60;
|
||||||
) {
|
border-color: #6c6a6a;
|
||||||
this.log.debug('TooltipDirective constructed');
|
color: #c7c7c0;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -13,21 +13,18 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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;
|
||||||
}
|
}
|
||||||
@ -25,7 +25,6 @@ import { MapService } from './map.service';
|
|||||||
import { SpriteService } from './sprite.service';
|
import { SpriteService } from './sprite.service';
|
||||||
import { SpriteDataService } from './spritedata.service';
|
import { SpriteDataService } from './spritedata.service';
|
||||||
import { SvgUtilService } from './svgutil.service';
|
import { SvgUtilService } from './svgutil.service';
|
||||||
import { IconDirective } from './icon.directive';
|
|
||||||
import { IconComponent } from './icon/icon.component';
|
import { IconComponent } from './icon/icon.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,7 +32,6 @@ import { IconComponent } from './icon/icon.component';
|
|||||||
*/
|
*/
|
||||||
@NgModule({
|
@NgModule({
|
||||||
exports: [
|
exports: [
|
||||||
IconDirective,
|
|
||||||
IconComponent
|
IconComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
@ -41,7 +39,6 @@ import { IconComponent } from './icon/icon.component';
|
|||||||
UtilModule
|
UtilModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
IconDirective,
|
|
||||||
IconComponent
|
IconComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@ -414,6 +414,35 @@ export class FnService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Return the given string with the first character capitalized.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -34,7 +34,8 @@ interface Lion {
|
|||||||
})
|
})
|
||||||
export class LionService {
|
export class LionService {
|
||||||
|
|
||||||
ubercache: any[];
|
ubercache: any[] = [];
|
||||||
|
loadCb; // Function
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for uberlion event from WSS
|
* Handler for uberlion event from WSS
|
||||||
@ -50,6 +51,10 @@ export class LionService {
|
|||||||
this.log.info(' :=> ', p);
|
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);
|
this.log.debug('LION service: uber-lion bundle received:', data);
|
||||||
}
|
}
|
||||||
@ -69,17 +74,15 @@ export class LionService {
|
|||||||
* returns a function that takes a string and returns a string
|
* returns a function that takes a string and returns a string
|
||||||
*/
|
*/
|
||||||
bundle(bundleId: string): (string) => 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);
|
this.log.warn('No lion bundle registered:', bundleId);
|
||||||
bundle = {};
|
bundleObj = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.getKey;
|
return (key) => {
|
||||||
}
|
return bundleObj[key] || '%' + key + '%';
|
||||||
|
};
|
||||||
getKey(key: string): string {
|
|
||||||
return this.bundle[key] || '%' + key + '%';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import { Injectable } from '@angular/core';
|
|||||||
import { FnService } from '../util/fn.service';
|
import { FnService } from '../util/fn.service';
|
||||||
import { IconService } from '../svg/icon.service';
|
import { IconService } from '../svg/icon.service';
|
||||||
import { LogService } from '../../log.service';
|
import { LogService } from '../../log.service';
|
||||||
import { TooltipService } from './tooltip.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Widget -- Button Service
|
* ONOS GUI -- Widget -- Button Service
|
||||||
@ -29,7 +28,6 @@ export class ButtonService {
|
|||||||
private is: IconService,
|
private is: IconService,
|
||||||
private fs: FnService,
|
private fs: FnService,
|
||||||
private log: LogService,
|
private log: LogService,
|
||||||
private tts: TooltipService
|
|
||||||
) {
|
) {
|
||||||
this.log.debug('ButtonService constructed');
|
this.log.debug('ButtonService constructed');
|
||||||
}
|
}
|
||||||
|
|||||||
152
web/gui2/src/main/webapp/app/fw/widget/table-theme.css
Normal file
152
web/gui2/src/main/webapp/app/fw/widget/table-theme.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
109
web/gui2/src/main/webapp/app/fw/widget/table.css
Normal file
109
web/gui2/src/main/webapp/app/fw/widget/table.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
215
web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
Normal file
215
web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -23,11 +23,8 @@ import { UtilModule } from '../util/util.module';
|
|||||||
import { ButtonService } from './button.service';
|
import { ButtonService } from './button.service';
|
||||||
import { ChartBuilderService } from './chartbuilder.service';
|
import { ChartBuilderService } from './chartbuilder.service';
|
||||||
import { ListService } from './list.service';
|
import { ListService } from './list.service';
|
||||||
import { TableBuilderService } from './tablebuilder.service';
|
|
||||||
import { TableDetailService } from './tabledetail.service';
|
import { TableDetailService } from './tabledetail.service';
|
||||||
import { ToolbarService } from './toolbar.service';
|
import { ToolbarService } from './toolbar.service';
|
||||||
import { TooltipService } from './tooltip.service';
|
|
||||||
import { TooltipDirective } from './tooltip.directive';
|
|
||||||
import { SortableHeaderDirective } from './sortableheader.directive';
|
import { SortableHeaderDirective } from './sortableheader.directive';
|
||||||
import { TableResizeDirective } from './tableresize.directive';
|
import { TableResizeDirective } from './tableresize.directive';
|
||||||
import { FlashChangesDirective } from './flashchanges.directive';
|
import { FlashChangesDirective } from './flashchanges.directive';
|
||||||
@ -42,7 +39,6 @@ import { FlashChangesDirective } from './flashchanges.directive';
|
|||||||
// It's enough to import them in the OnosModule
|
// It's enough to import them in the OnosModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
TooltipDirective,
|
|
||||||
SortableHeaderDirective,
|
SortableHeaderDirective,
|
||||||
TableResizeDirective,
|
TableResizeDirective,
|
||||||
FlashChangesDirective
|
FlashChangesDirective
|
||||||
@ -51,9 +47,7 @@ import { FlashChangesDirective } from './flashchanges.directive';
|
|||||||
ButtonService,
|
ButtonService,
|
||||||
ChartBuilderService,
|
ChartBuilderService,
|
||||||
ListService,
|
ListService,
|
||||||
TableBuilderService,
|
|
||||||
TableDetailService,
|
TableDetailService,
|
||||||
TooltipService,
|
|
||||||
ToolbarService
|
ToolbarService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -27,8 +27,4 @@
|
|||||||
#view h2 {
|
#view h2 {
|
||||||
-webkit-margin-before: 0;
|
-webkit-margin-before: 0;
|
||||||
-webkit-margin-after: 0;
|
-webkit-margin-after: 0;
|
||||||
margin: 32px 0 4px 16px;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 18pt;
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<div style="text-align:center" onosDetectBrowser>
|
<div id="view" onosDetectBrowser>
|
||||||
<onos-mast></onos-mast>
|
<onos-mast></onos-mast>
|
||||||
<onos-nav></onos-nav>
|
<onos-nav></onos-nav>
|
||||||
<onos-veil #veil></onos-veil>
|
<onos-veil #veil></onos-veil>
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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 { LionService } from './fw/util/lion.service';
|
||||||
import { LogService } from './log.service';
|
import { LogService } from './log.service';
|
||||||
import { KeyService } from './fw/util/key.service';
|
import { KeyService } from './fw/util/key.service';
|
||||||
@ -62,7 +62,7 @@ function cap(s: string): string {
|
|||||||
templateUrl: './onos.component.html',
|
templateUrl: './onos.component.html',
|
||||||
styleUrls: ['./onos.component.css', './onos.common.css']
|
styleUrls: ['./onos.component.css', './onos.common.css']
|
||||||
})
|
})
|
||||||
export class OnosComponent implements OnInit {
|
export class OnosComponent implements OnInit, OnDestroy {
|
||||||
public title = 'onos';
|
public title = 'onos';
|
||||||
|
|
||||||
// view ID to help page url map.. injected via the servlet
|
// view ID to help page url map.. injected via the servlet
|
||||||
@ -110,14 +110,19 @@ export class OnosComponent implements OnInit {
|
|||||||
|
|
||||||
this.onos.viewMap = this.viewMap;
|
this.onos.viewMap = this.viewMap;
|
||||||
|
|
||||||
// this.wss.createWebSocket({
|
|
||||||
// wsport: Window.location.search().wsport
|
|
||||||
// });
|
|
||||||
|
|
||||||
// TODO: Enable this this.saucy(this.ee, this.ks);
|
// TODO: Enable this this.saucy(this.ee, this.ks);
|
||||||
this.log.debug('OnosComponent initialized');
|
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) {
|
saucy(ee, ks) {
|
||||||
const map = ee.genMap(sauce);
|
const map = ee.genMap(sauce);
|
||||||
Object.keys(map).forEach(function (k) {
|
Object.keys(map).forEach(function (k) {
|
||||||
|
|||||||
@ -1,3 +1,100 @@
|
|||||||
<div id="ov-app">
|
<div id="ov-app" filedrop on-file-drop="appDropped()">
|
||||||
<p>apps works!</p>
|
<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>
|
</div>
|
||||||
@ -13,46 +13,235 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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 { DialogService } from '../../fw/layer/dialog.service';
|
||||||
import { FnService } from '../../fw/util/fn.service';
|
import { FnService } from '../../fw/util/fn.service';
|
||||||
import { IconService } from '../../fw/svg/icon.service';
|
import { IconService } from '../../fw/svg/icon.service';
|
||||||
import { KeyService } from '../../fw/util/key.service';
|
import { KeyService } from '../../fw/util/key.service';
|
||||||
import { LionService } from '../../fw/util/lion.service';
|
import { LionService } from '../../fw/util/lion.service';
|
||||||
|
import { LoadingService } from '../../fw/layer/loading.service';
|
||||||
import { LogService } from '../../log.service';
|
import { LogService } from '../../log.service';
|
||||||
import { PanelService } from '../../fw/layer/panel.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 { UrlFnService } from '../../fw/remote/urlfn.service';
|
||||||
import { WebSocketService } from '../../fw/remote/websocket.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
|
* ONOS GUI -- Apps View Component
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'onos-apps',
|
selector: 'onos-apps',
|
||||||
templateUrl: './apps.component.html',
|
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(
|
constructor(
|
||||||
private fs: FnService,
|
protected fs: FnService,
|
||||||
private ds: DialogService,
|
private ds: DialogService,
|
||||||
private is: IconService,
|
private is: IconService,
|
||||||
private ks: KeyService,
|
private ks: KeyService,
|
||||||
private ls: LionService,
|
private lion: LionService,
|
||||||
private log: LogService,
|
protected ls: LoadingService,
|
||||||
|
protected log: LogService,
|
||||||
private ps: PanelService,
|
private ps: PanelService,
|
||||||
private tbs: TableBuilderService,
|
|
||||||
private ufs: UrlFnService,
|
private ufs: UrlFnService,
|
||||||
private wss: WebSocketService,
|
protected wss: WebSocketService,
|
||||||
private window: Window
|
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() {
|
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 + '%';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { AppsComponent } from './apps.component';
|
import { AppsComponent } from './apps.component';
|
||||||
import { TriggerFormDirective } from './triggerform.directive';
|
import { TriggerFormDirective } from './triggerform.directive';
|
||||||
|
import { SvgModule } from '../../fw/svg/svg.module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Apps View Module
|
* ONOS GUI -- Apps View Module
|
||||||
@ -30,7 +31,8 @@ import { TriggerFormDirective } from './triggerform.directive';
|
|||||||
AppsComponent
|
AppsComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
SvgModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppsComponent,
|
AppsComponent,
|
||||||
|
|||||||
@ -17,12 +17,15 @@
|
|||||||
/*
|
/*
|
||||||
ONOS GUI -- Device View (layout) -- CSS file
|
ONOS GUI -- Device View (layout) -- CSS file
|
||||||
*/
|
*/
|
||||||
|
#ov-device .tabular-header {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
#ov-device h2 {
|
#ov-device h2 {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ov-device div.ctrl-btns {
|
#ov-device, div.ctrl-btns {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,99 @@
|
|||||||
<div id="ov-device">
|
<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>
|
</div>
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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 { DetailsPanelService } from '../../fw/layer/detailspanel.service';
|
||||||
import { FnService } from '../../fw/util/fn.service';
|
import { FnService } from '../../fw/util/fn.service';
|
||||||
import { IconService } from '../../fw/svg/icon.service';
|
import { IconService } from '../../fw/svg/icon.service';
|
||||||
@ -23,42 +23,78 @@ import { LogService } from '../../log.service';
|
|||||||
import { MastService } from '../../fw/mast/mast.service';
|
import { MastService } from '../../fw/mast/mast.service';
|
||||||
import { NavService } from '../../fw/nav/nav.service';
|
import { NavService } from '../../fw/nav/nav.service';
|
||||||
import { PanelService } from '../../fw/layer/panel.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 { TableDetailService } from '../../fw/widget/tabledetail.service';
|
||||||
import { WebSocketService } from '../../fw/remote/websocket.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
|
* ONOS GUI -- Device View Component
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'onos-device',
|
selector: 'onos-device',
|
||||||
templateUrl: './device.component.html',
|
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(
|
constructor(
|
||||||
private dps: DetailsPanelService,
|
private dps: DetailsPanelService,
|
||||||
private fs: FnService,
|
protected fs: FnService,
|
||||||
|
protected ls: LoadingService,
|
||||||
private is: IconService,
|
private is: IconService,
|
||||||
private ks: KeyService,
|
private ks: KeyService,
|
||||||
private log: LogService,
|
protected log: LogService,
|
||||||
private mast: MastService,
|
private mast: MastService,
|
||||||
private nav: NavService,
|
private nav: NavService,
|
||||||
private ps: PanelService,
|
private ps: PanelService,
|
||||||
private tbs: TableBuilderService,
|
|
||||||
private tds: TableDetailService,
|
private tds: TableDetailService,
|
||||||
private wss: WebSocketService,
|
protected wss: WebSocketService,
|
||||||
private ls: LoadingService, // TODO: Remove - already added through tbs
|
private window: Window,
|
||||||
private window: Window
|
|
||||||
) {
|
) {
|
||||||
this.log.debug('DeviceComponent constructed');
|
super(fs, ls, log, wss, 'device');
|
||||||
|
this.responseCallback = this.deviceResponseCb;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.init();
|
||||||
this.log.debug('DeviceComponent initialized');
|
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { DeviceComponent } from './device.component';
|
import { DeviceComponent } from './device.component';
|
||||||
import { DeviceDetailsPanelDirective } from './devicedetailspanel.directive';
|
import { DeviceDetailsPanelDirective } from './devicedetailspanel.directive';
|
||||||
import { RemoteModule } from '../../fw/remote/remote.module';
|
import { SvgModule } from '../../fw/svg/svg.module';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Device View Module
|
* ONOS GUI -- Device View Module
|
||||||
*/
|
*/
|
||||||
@ -27,7 +29,8 @@ import { RemoteModule } from '../../fw/remote/remote.module';
|
|||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
RemoteModule
|
RouterModule,
|
||||||
|
SvgModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DeviceComponent,
|
DeviceComponent,
|
||||||
|
|||||||
@ -41,6 +41,10 @@ body {
|
|||||||
|
|
||||||
#view h2 {
|
#view h2 {
|
||||||
color: #3c3a3a;
|
color: #3c3a3a;
|
||||||
|
margin: 32px 0 4px 16px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 18pt;
|
||||||
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
|||||||
@ -33,8 +33,6 @@ class MockActivatedRoute extends ActivatedRoute {
|
|||||||
|
|
||||||
class MockGlyphService {}
|
class MockGlyphService {}
|
||||||
|
|
||||||
class MockWSock {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Remote -- Web Socket Service - Unit Tests
|
* ONOS GUI -- Remote -- Web Socket Service - Unit Tests
|
||||||
*/
|
*/
|
||||||
@ -103,7 +101,7 @@ describe('WebSocketService', () => {
|
|||||||
'noHandlersWarn', 'resetState',
|
'noHandlersWarn', 'resetState',
|
||||||
'createWebSocket', 'bindHandlers', 'unbindHandlers',
|
'createWebSocket', 'bindHandlers', 'unbindHandlers',
|
||||||
'addOpenListener', 'removeOpenListener', 'sendEvent',
|
'addOpenListener', 'removeOpenListener', 'sendEvent',
|
||||||
'setVeilDelegate', 'setLoadingDelegate'
|
'setVeilDelegate', 'setLoadingDelegate', 'isConnected', 'closeWebSocket'
|
||||||
])).toBeTruthy();
|
])).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -228,9 +226,7 @@ describe('WebSocketService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should warn if no arguments, unbindHandlers', () => {
|
it('should warn if no arguments, unbindHandlers', () => {
|
||||||
expect(wss.unbindHandlers(
|
expect(wss.unbindHandlers([])).toBeNull();
|
||||||
new Map<string, (data) => void>([])
|
|
||||||
)).toBeNull();
|
|
||||||
expect(logServiceSpy.warn).toHaveBeenCalledWith(
|
expect(logServiceSpy.warn).toHaveBeenCalledWith(
|
||||||
'WSS.unbindHandlers(): no event handlers'
|
'WSS.unbindHandlers(): no event handlers'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@ -235,7 +235,7 @@ describe('FnService', () => {
|
|||||||
'isFirefox', 'parseDebugFlags',
|
'isFirefox', 'parseDebugFlags',
|
||||||
'debugOn', 'debug', 'find', 'inArray', 'removeFromArray',
|
'debugOn', 'debug', 'find', 'inArray', 'removeFromArray',
|
||||||
'isEmptyObject', 'cap', 'noPx', 'noPxStyle', 'endsWith',
|
'isEmptyObject', 'cap', 'noPx', 'noPxStyle', 'endsWith',
|
||||||
'inEvilList', 'analyze', 'sanitize'
|
'inEvilList', 'analyze', 'sanitize', 'sameObjProps', 'containsObj'
|
||||||
// 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
|
// 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
|
||||||
// 'eecode', 'noPx', 'noPxStyle', 'endsWith', 'addToTrie', 'removeFromTrie', 'trieLookup',
|
// 'eecode', 'noPx', 'noPxStyle', 'endsWith', 'addToTrie', 'removeFromTrie', 'trieLookup',
|
||||||
// 'classNames', 'extend', 'sanitize'
|
// 'classNames', 'extend', 'sanitize'
|
||||||
|
|||||||
@ -20,14 +20,11 @@ import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
|||||||
import { ButtonService } from '../../../../app/fw/widget/button.service';
|
import { ButtonService } from '../../../../app/fw/widget/button.service';
|
||||||
import { FnService } from '../../../../app/fw/util/fn.service';
|
import { FnService } from '../../../../app/fw/util/fn.service';
|
||||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||||
import { TooltipService } from '../../../../app/fw/widget/tooltip.service';
|
|
||||||
|
|
||||||
class MockIconService {}
|
class MockIconService {}
|
||||||
|
|
||||||
class MockFnService {}
|
class MockFnService {}
|
||||||
|
|
||||||
class MockTooltipService {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Widget -- Button Service - Unit Tests
|
* ONOS GUI -- Widget -- Button Service - Unit Tests
|
||||||
*/
|
*/
|
||||||
@ -42,7 +39,6 @@ describe('ButtonService', () => {
|
|||||||
{ provide: LogService, useValue: log },
|
{ provide: LogService, useValue: log },
|
||||||
{ provide: IconService, useClass: MockIconService },
|
{ provide: IconService, useClass: MockIconService },
|
||||||
{ provide: FnService, useClass: MockFnService },
|
{ provide: FnService, useClass: MockFnService },
|
||||||
{ provide: TooltipService, useClass: MockTooltipService },
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@ -59,6 +59,8 @@ class MockIconService {}
|
|||||||
|
|
||||||
class MockKeyService {}
|
class MockKeyService {}
|
||||||
|
|
||||||
|
class MockLionService {}
|
||||||
|
|
||||||
class MockNavService {}
|
class MockNavService {}
|
||||||
|
|
||||||
class MockOnosService {}
|
class MockOnosService {}
|
||||||
@ -73,6 +75,11 @@ class MockThemeService {}
|
|||||||
|
|
||||||
class MockVeilComponent {}
|
class MockVeilComponent {}
|
||||||
|
|
||||||
|
class MockWebSocketService {
|
||||||
|
createWebSocket() {}
|
||||||
|
isConnected() { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Onos Component - Unit Tests
|
* ONOS GUI -- Onos Component - Unit Tests
|
||||||
*/
|
*/
|
||||||
@ -81,6 +88,8 @@ describe('OnosComponent', () => {
|
|||||||
let fs: FnService;
|
let fs: FnService;
|
||||||
let ar: MockActivatedRoute;
|
let ar: MockActivatedRoute;
|
||||||
let windowMock: Window;
|
let windowMock: Window;
|
||||||
|
let fixture;
|
||||||
|
let app;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
log = new ConsoleLoggerService();
|
log = new ConsoleLoggerService();
|
||||||
@ -117,6 +126,7 @@ describe('OnosComponent', () => {
|
|||||||
{ provide: GlyphService, useClass: MockGlyphService },
|
{ provide: GlyphService, useClass: MockGlyphService },
|
||||||
{ provide: IconService, useClass: MockIconService },
|
{ provide: IconService, useClass: MockIconService },
|
||||||
{ provide: KeyService, useClass: MockKeyService },
|
{ provide: KeyService, useClass: MockKeyService },
|
||||||
|
{ provide: LionService, useClass: MockLionService },
|
||||||
{ provide: LogService, useValue: log },
|
{ provide: LogService, useValue: log },
|
||||||
{ provide: NavService, useClass: MockNavService },
|
{ provide: NavService, useClass: MockNavService },
|
||||||
{ provide: OnosService, useClass: MockOnosService },
|
{ provide: OnosService, useClass: MockOnosService },
|
||||||
@ -124,20 +134,22 @@ describe('OnosComponent', () => {
|
|||||||
{ provide: PanelService, useClass: MockPanelService },
|
{ provide: PanelService, useClass: MockPanelService },
|
||||||
{ provide: SpriteService, useClass: MockSpriteService },
|
{ provide: SpriteService, useClass: MockSpriteService },
|
||||||
{ provide: ThemeService, useClass: MockThemeService },
|
{ provide: ThemeService, useClass: MockThemeService },
|
||||||
|
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||||
{ provide: Window, useFactory: (() => windowMock ) },
|
{ provide: Window, useFactory: (() => windowMock ) },
|
||||||
]
|
]
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(OnosComponent);
|
||||||
|
app = fixture.componentInstance;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create the app', async(() => {
|
it('should create the app', async(() => {
|
||||||
const fixture = TestBed.createComponent(OnosComponent);
|
|
||||||
const app = fixture.debugElement.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
expect(app).toBeTruthy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it(`should have as title 'onos'`, async(() => {
|
// it(`should have as title 'onos'`, async(() => {
|
||||||
const fixture = TestBed.createComponent(OnosComponent);
|
// const fixture = TestBed.createComponent(OnosComponent);
|
||||||
const app = fixture.debugElement.componentInstance;
|
// const app = fixture.componentInstance;
|
||||||
expect(app.title).toEqual('onos');
|
// expect(app.title).toEqual('onos');
|
||||||
}));
|
// }));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,72 +14,126 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
import { LogService } from '../../../../app/log.service';
|
import { LogService } from '../../../../app/log.service';
|
||||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
|
||||||
import { AppsComponent } from '../../../../app/view/apps/apps.component';
|
import { AppsComponent } from '../../../../app/view/apps/apps.component';
|
||||||
import { DialogService } from '../../../../app/fw/layer/dialog.service';
|
import { DialogService } from '../../../../app/fw/layer/dialog.service';
|
||||||
import { FnService } from '../../../../app/fw/util/fn.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 { IconService } from '../../../../app/fw/svg/icon.service';
|
||||||
import { KeyService } from '../../../../app/fw/util/key.service';
|
import { KeyService } from '../../../../app/fw/util/key.service';
|
||||||
import { LionService } from '../../../../app/fw/util/lion.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 { 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 { UrlFnService } from '../../../../app/fw/remote/urlfn.service';
|
||||||
import { WebSocketService } from '../../../../app/fw/remote/websocket.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 MockDialogService {}
|
||||||
|
|
||||||
class MockFnService {}
|
class MockFnService {}
|
||||||
|
|
||||||
class MockIconService {}
|
class MockIconService {
|
||||||
|
loadIconDef() {}
|
||||||
|
}
|
||||||
|
|
||||||
class MockKeyService {}
|
class MockKeyService {}
|
||||||
|
|
||||||
class MockLionService {}
|
class MockLoadingService {
|
||||||
|
startAnim() {}
|
||||||
|
stop() {}
|
||||||
|
waiting() {}
|
||||||
|
}
|
||||||
|
|
||||||
class MockPanelService {}
|
class MockPanelService {}
|
||||||
|
|
||||||
class MockTableBuilderService {}
|
class MockTableBuilderService {}
|
||||||
|
|
||||||
|
class MockThemeService {}
|
||||||
|
|
||||||
class MockUrlFnService {}
|
class MockUrlFnService {}
|
||||||
|
|
||||||
class MockWebSocketService {}
|
class MockWebSocketService {
|
||||||
|
createWebSocket() {}
|
||||||
|
isConnected() { return false; }
|
||||||
|
unbindHandlers() {}
|
||||||
|
bindHandlers() {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Apps View -- Unit Tests
|
* ONOS GUI -- Apps View -- Unit Tests
|
||||||
*/
|
*/
|
||||||
describe('AppsComponent', () => {
|
describe('AppsComponent', () => {
|
||||||
let log: LogService;
|
let fs: FnService;
|
||||||
|
let ar: MockActivatedRoute;
|
||||||
|
let windowMock: Window;
|
||||||
|
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||||
let component: AppsComponent;
|
let component: AppsComponent;
|
||||||
let fixture: ComponentFixture<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(() => {
|
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({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ AppsComponent ],
|
declarations: [ AppsComponent, IconComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: DialogService, useClass: MockDialogService },
|
{ provide: DialogService, useClass: MockDialogService },
|
||||||
{ provide: FnService, useClass: MockFnService },
|
{ provide: FnService, useValue: fs },
|
||||||
{ provide: IconService, useClass: MockIconService },
|
{ provide: IconService, useClass: MockIconService },
|
||||||
{ provide: KeyService, useClass: MockKeyService },
|
{ provide: KeyService, useClass: MockKeyService },
|
||||||
{ provide: LionService, useClass: MockLionService },
|
{ provide: LionService, useFactory: (() => {
|
||||||
{ provide: LogService, useValue: log },
|
return {
|
||||||
|
bundle: ((bundleId) => mockLion),
|
||||||
|
ubercache: new Array()
|
||||||
|
};
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ provide: LoadingService, useClass: MockLoadingService },
|
||||||
|
{ provide: LogService, useValue: logSpy },
|
||||||
{ provide: PanelService, useClass: MockPanelService },
|
{ provide: PanelService, useClass: MockPanelService },
|
||||||
{ provide: TableBuilderService, useClass: MockTableBuilderService },
|
{ provide: ThemeService, useClass: MockThemeService },
|
||||||
{ provide: UrlFnService, useClass: MockUrlFnService },
|
{ provide: UrlFnService, useClass: MockUrlFnService },
|
||||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||||
{ provide: Window, useValue: windowMock },
|
{ provide: Window, useValue: windowMock },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
logServiceSpy = TestBed.get(LogService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AppsComponent);
|
fixture = TestBed.createComponent(AppsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.debugElement.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -14,46 +14,50 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
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 { LogService } from '../../../../app/log.service';
|
||||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
|
||||||
import { DeviceComponent } from '../../../../app/view/device/device.component';
|
import { DeviceComponent } from '../../../../app/view/device/device.component';
|
||||||
|
|
||||||
import { DetailsPanelService } from '../../../../app/fw/layer/detailspanel.service';
|
import { DetailsPanelService } from '../../../../app/fw/layer/detailspanel.service';
|
||||||
import { FnService, WindowSize } from '../../../../app/fw/util/fn.service';
|
import { FnService, WindowSize } from '../../../../app/fw/util/fn.service';
|
||||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||||
import { GlyphService } from '../../../../app/fw/svg/glyph.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 { KeyService } from '../../../../app/fw/util/key.service';
|
||||||
import { LoadingService } from '../../../../app/fw/layer/loading.service';
|
import { LoadingService } from '../../../../app/fw/layer/loading.service';
|
||||||
import { NavService } from '../../../../app/fw/nav/nav.service';
|
import { NavService } from '../../../../app/fw/nav/nav.service';
|
||||||
import { MastService } from '../../../../app/fw/mast/mast.service';
|
import { MastService } from '../../../../app/fw/mast/mast.service';
|
||||||
import { PanelService } from '../../../../app/fw/layer/panel.service';
|
import { PanelService } from '../../../../app/fw/layer/panel.service';
|
||||||
import { SvgUtilService } from '../../../../app/fw/svg/svgutil.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 { TableDetailService } from '../../../../app/fw/widget/tabledetail.service';
|
||||||
|
import { ThemeService } from '../../../../app/fw/util/theme.service';
|
||||||
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
|
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
class MockDetailsPanelService {}
|
class MockActivatedRoute extends ActivatedRoute {
|
||||||
|
constructor(params: Params) {
|
||||||
class MockFnService {
|
super();
|
||||||
windowSize(offH: number = 0, offW: number = 0): WindowSize {
|
this.queryParams = of(params);
|
||||||
return {
|
|
||||||
height: 123,
|
|
||||||
width: 456
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockIconService {}
|
class MockDetailsPanelService {}
|
||||||
|
|
||||||
|
class MockFnService {}
|
||||||
|
|
||||||
|
class MockIconService {
|
||||||
|
loadIconDef() {}
|
||||||
|
}
|
||||||
|
|
||||||
class MockGlyphService {}
|
class MockGlyphService {}
|
||||||
|
|
||||||
class MockKeyService {}
|
class MockKeyService {}
|
||||||
|
|
||||||
class MockLoadingService {
|
class MockLoadingService {
|
||||||
startAnim() {
|
startAnim() {}
|
||||||
// Do nothing
|
stop() {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockNavService {}
|
class MockNavService {}
|
||||||
@ -66,49 +70,81 @@ class MockTableBuilderService {}
|
|||||||
|
|
||||||
class MockTableDetailService {}
|
class MockTableDetailService {}
|
||||||
|
|
||||||
class MockWebSocketService {}
|
class MockThemeService {}
|
||||||
|
|
||||||
|
class MockWebSocketService {
|
||||||
|
createWebSocket() {}
|
||||||
|
isConnected() { return false; }
|
||||||
|
unbindHandlers() {}
|
||||||
|
bindHandlers() {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ONOS GUI -- Device View Module - Unit Tests
|
* ONOS GUI -- Device View Module - Unit Tests
|
||||||
*/
|
*/
|
||||||
describe('DeviceComponent', () => {
|
describe('DeviceComponent', () => {
|
||||||
let log: LogService;
|
let fs: FnService;
|
||||||
|
let ar: MockActivatedRoute;
|
||||||
|
let windowMock: Window;
|
||||||
|
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||||
let component: DeviceComponent;
|
let component: DeviceComponent;
|
||||||
let fixture: ComponentFixture<DeviceComponent>;
|
let fixture: ComponentFixture<DeviceComponent>;
|
||||||
const windowMock = <any>{ location: <any> { hostname: 'localhost' } };
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
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({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ DeviceComponent ],
|
declarations: [ DeviceComponent, IconComponent ],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: DetailsPanelService, useClass: MockDetailsPanelService },
|
{ provide: DetailsPanelService, useClass: MockDetailsPanelService },
|
||||||
{ provide: FnService, useClass: MockFnService },
|
{ provide: FnService, useValue: fs },
|
||||||
{ provide: IconService, useClass: MockIconService },
|
{ provide: IconService, useClass: MockIconService },
|
||||||
{ provide: GlyphService, useClass: MockGlyphService },
|
{ provide: GlyphService, useClass: MockGlyphService },
|
||||||
{ provide: KeyService, useClass: MockKeyService },
|
{ provide: KeyService, useClass: MockKeyService },
|
||||||
{ provide: LoadingService, useClass: MockLoadingService },
|
{ provide: LoadingService, useClass: MockLoadingService },
|
||||||
{ provide: MastService, useClass: MockMastService },
|
{ provide: MastService, useClass: MockMastService },
|
||||||
{ provide: NavService, useClass: MockNavService },
|
{ provide: NavService, useClass: MockNavService },
|
||||||
{ provide: LogService, useValue: log },
|
{ provide: LogService, useValue: logSpy },
|
||||||
{ provide: PanelService, useClass: MockPanelService },
|
{ provide: PanelService, useClass: MockPanelService },
|
||||||
{ provide: TableBuilderService, useClass: MockTableBuilderService },
|
|
||||||
{ provide: TableDetailService, useClass: MockTableDetailService },
|
{ provide: TableDetailService, useClass: MockTableDetailService },
|
||||||
|
{ provide: ThemeService, useClass: MockThemeService },
|
||||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||||
{ provide: Window, useValue: windowMock },
|
{ provide: Window, useValue: windowMock },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
logServiceSpy = TestBed.get(LogService);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(DeviceComponent);
|
fixture = TestBed.createComponent(DeviceComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.debugElement.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
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 ');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user